8185829: Update Graal
authordlong
Fri, 04 Aug 2017 19:59:33 -0700
changeset 46762 f7defa99f173
parent 46761 ef9fb3cc71f0
child 46763 1b0ff6953b5a
8185829: Update Graal Reviewed-by: iveresov
hotspot/src/jdk.internal.vm.compiler/.mx.graal/suite.py
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64ArithmeticLIRGenerator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.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.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/ZeroSignExtendTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/CountUppercaseParallelTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisTreesTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.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/src/org/graalvm/compiler/debug/Assertions.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/DebugContext.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/DiagnosticsOutputDirectory.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.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSuitesProvider.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSuitesCreator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.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/CompilerConfigurationFactory.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ExceptionHandlerStub.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/UnwindExceptionToCallerStub.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.lir/src/org/graalvm/compiler/lir/alloc/lsra/Interval.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/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/trace/bu/BottomUpAllocator.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceInterval.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/TraceLinearScanPhase.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/asm/CompilationResultBuilder.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/AllocationStage.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/LoopTransformations.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/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.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/extended/FixedValueAnchorNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawStoreNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/StateSplitProxyNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.test/src/org/graalvm/compiler/options/test/TestOptionKey.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/ModifiableOptionValues.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionValues.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.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/src/org/graalvm/compiler/phases/BasePhase.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.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/GraalDebugHandlersFactory.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadReplacementPhase.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnVolatileReadTest.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsUtil.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.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.graphio/src/org/graalvm/graphio/DefaultGraphBlocks.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/DefaultGraphTypes.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphBlocks.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphElements.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphOutput.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphStructure.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphTypes.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/ProtocolImpl.java
hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java
--- a/hotspot/src/jdk.internal.vm.compiler/.mx.graal/suite.py	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/.mx.graal/suite.py	Fri Aug 04 19:59:33 2017 -0700
@@ -179,6 +179,14 @@
       "workingSets" : "Graal",
     },
 
+    "org.graalvm.graphio" : {
+      "subDir" : "share/classes",
+      "sourceDirs" : ["src"],
+      "checkstyle" : "org.graalvm.compiler.graph",
+      "javaCompliance" : "1.8",
+      "workingSets" : "API,Graal",
+    },
+
     "org.graalvm.util" : {
       "subDir" : "share/classes",
       "sourceDirs" : ["src"],
@@ -1011,6 +1019,7 @@
       "subDir" : "share/classes",
       "sourceDirs" : ["src"],
       "dependencies" : [
+        "org.graalvm.graphio",
         "org.graalvm.compiler.core",
         "org.graalvm.compiler.java",
       ],
@@ -1138,6 +1147,13 @@
       ],
     },
 
+    "GRAAL_GRAPHIO" : {
+      "subDir" : "share/classes",
+      "dependencies" : ["org.graalvm.graphio"],
+      "distDependencies" : [
+      ],
+    },
+
     "GRAAL_OPTIONS_PROCESSOR" : {
       "subDir" : "share/classes",
       "dependencies" : ["org.graalvm.compiler.options.processor"],
@@ -1203,6 +1219,7 @@
       "distDependencies" : [
         "GRAAL_API",
         "GRAAL_COMPILER",
+        "GRAAL_GRAPHIO",
       ],
     },
 
@@ -1297,6 +1314,7 @@
     "GRAAL" : {
       "subDir" : "share/classes",
       "overlaps" : [
+        "GRAAL_GRAPHIO",
         "GRAAL_OPTIONS",
         "GRAAL_NODEINFO",
         "GRAAL_API",
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64ArithmeticLIRGenerator.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64ArithmeticLIRGenerator.java	Fri Aug 04 19:59:33 2017 -0700
@@ -94,7 +94,7 @@
         }
     }
 
-    protected Value emitExtendMemory(boolean isSigned, AArch64Kind memoryKind, int resultBits, AArch64AddressValue address, LIRFrameState state) {
+    public Value emitExtendMemory(boolean isSigned, AArch64Kind memoryKind, int resultBits, AArch64AddressValue address, LIRFrameState state) {
         // Issue a zero extending load of the proper bit size and set the result to
         // the proper kind.
         Variable result = getLIRGen().newVariable(LIRKind.value(resultBits == 32 ? AArch64Kind.DWORD : AArch64Kind.QWORD));
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java	Fri Aug 04 19:59:33 2017 -0700
@@ -24,14 +24,9 @@
 package org.graalvm.compiler.core.aarch64;
 
 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.lir.LIRFrameState;
-import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodes.DeoptimizingNode;
-import org.graalvm.compiler.nodes.calc.SignExtendNode;
-import org.graalvm.compiler.nodes.calc.ZeroExtendNode;
 import org.graalvm.compiler.nodes.memory.Access;
 
 import jdk.vm.ci.aarch64.AArch64Kind;
@@ -61,18 +56,4 @@
     protected AArch64ArithmeticLIRGenerator getArithmeticLIRGenerator() {
         return (AArch64ArithmeticLIRGenerator) getLIRGeneratorTool().getArithmetic();
     }
-
-    @MatchRule("(ZeroExtend Read=access)")
-    @MatchRule("(ZeroExtend FloatingRead=access)")
-    public ComplexMatchResult zeroExtend(ZeroExtendNode root, Access access) {
-        AArch64Kind memoryKind = getMemoryKind(access);
-        return builder -> getArithmeticLIRGenerator().emitExtendMemory(false, memoryKind, root.getResultBits(), (AArch64AddressValue) operand(access.getAddress()), getState(access));
-    }
-
-    @MatchRule("(SignExtend Read=access)")
-    @MatchRule("(SignExtend FloatingRead=access)")
-    public ComplexMatchResult signExtend(SignExtendNode root, Access access) {
-        AArch64Kind memoryKind = getMemoryKind(access);
-        return builder -> getArithmeticLIRGenerator().emitExtendMemory(true, memoryKind, root.getResultBits(), (AArch64AddressValue) operand(access.getAddress()), getState(access));
-    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/GraalOptions.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -22,7 +22,6 @@
  */
 package org.graalvm.compiler.core.common;
 
-import org.graalvm.compiler.debug.Assertions;
 import org.graalvm.compiler.options.Option;
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
@@ -269,9 +268,6 @@
     @Option(help = "Use a cache for snippet graphs.", type = OptionType.Debug)
     public static final OptionKey<Boolean> UseSnippetGraphCache = new OptionKey<>(true);
 
-    @Option(help = "Enable expensive assertions.", type = OptionType.Debug)
-    public static final OptionKey<Boolean> DetailedAsserts = new OptionKey<>(Assertions.ENABLED);
-
     @Option(help = "Enable experimental Trace Register Allocation.", type = OptionType.Debug)
     public static final OptionKey<Boolean> TraceRA = new OptionKey<>(false);
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -111,6 +111,7 @@
 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.MidTierContext;
 import org.graalvm.compiler.phases.tiers.Suites;
 import org.graalvm.compiler.phases.tiers.TargetProvider;
 import org.graalvm.compiler.phases.util.Providers;
@@ -595,6 +596,10 @@
         return new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
     }
 
+    protected MidTierContext getDefaultMidTierContext() {
+        return new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, null);
+    }
+
     protected SnippetReflectionProvider getSnippetReflection() {
         return Graal.getRequiredCapability(SnippetReflectionProvider.class);
     }
@@ -926,7 +931,7 @@
      */
     @SuppressWarnings("try")
     protected InstalledCode getCode(final ResolvedJavaMethod installedCodeOwner, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) {
-        if (!forceCompile) {
+        if (!forceCompile && graph == null) {
             InstalledCode cached = cache.get(installedCodeOwner);
             if (cached != null) {
                 if (cached.isValid()) {
--- /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/ZeroSignExtendTest.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, Red Hat Inc. 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.junit.Test;
+
+/*
+ * Test compilation of ZeroExtend and SignExtend nodes
+ */
+
+public class ZeroSignExtendTest extends GraalCompilerTest {
+
+    int testSnippet1(char[] chars) {
+        int x = 1;
+        x += chars[0];
+        x -= chars[1];
+        x *= chars[2];
+        x /= chars[3];
+        x &= chars[4];
+        x |= chars[5];
+        x ^= chars[6];
+        x <<= chars[7];
+        x >>= (chars[8] - chars[0]);
+        x >>>= (chars[9] - chars[0]);
+        x += chars[1];
+        return x;
+    }
+
+    long testSnippet2(char[] chars) {
+        long y = 2;
+        y += chars[0];
+        y -= chars[1];
+        y *= chars[2];
+        y /= chars[3];
+        y &= chars[4];
+        y |= chars[5];
+        y ^= chars[6];
+        y <<= chars[7];
+        y >>= (chars[8] - chars[0]);
+        y >>>= (chars[9] - chars[0]);
+        y += chars[1];
+        return y;
+    }
+
+    int testSnippet3(short[] shorts) {
+        int x = 1;
+        x += shorts[0];
+        x -= shorts[1];
+        x *= shorts[2];
+        x /= shorts[3];
+        x &= shorts[4];
+        x |= shorts[5];
+        x ^= shorts[6];
+        x <<= shorts[7];
+        x >>= (shorts[8] - shorts[0]);
+        x >>>= (shorts[9] - shorts[0]);
+        x += shorts[1];
+        return x;
+    }
+
+    long testSnippet4(short[] shorts) {
+        long y = 2;
+        y += shorts[0];
+        y -= shorts[1];
+        y *= shorts[2];
+        y /= shorts[3];
+        y &= shorts[4];
+        y |= shorts[5];
+        y ^= shorts[6];
+        y <<= shorts[7];
+        y >>= (shorts[8] - shorts[0]);
+        y >>>= (shorts[9] - shorts[0]);
+        y += shorts[1];
+        return y;
+    }
+
+    int testSnippet5(byte[] bytes) {
+        int x = 1;
+        x += bytes[0];
+        x -= bytes[1];
+        x *= bytes[2];
+        x /= bytes[3];
+        x &= bytes[4];
+        x |= bytes[5];
+        x ^= bytes[6];
+        x <<= bytes[7];
+        x >>= (bytes[8] - bytes[0]);
+        x >>>= (bytes[9] - bytes[0]);
+        x += bytes[1];
+        return x;
+    }
+
+    long testSnippet6(byte[] bytes) {
+        long y = 2;
+        y += bytes[0];
+        y -= bytes[1];
+        y *= bytes[2];
+        y /= bytes[3];
+        y &= bytes[4];
+        y |= bytes[5];
+        y ^= bytes[6];
+        y <<= bytes[7];
+        y >>= (bytes[8] - bytes[0]);
+        y >>>= (bytes[9] - bytes[0]);
+        y += bytes[1];
+        return y;
+    }
+
+    @Test
+
+    public void test() {
+        char[] input1 = new char[]{'0', '1', '2', '3', '4', '5', '7', '8', '9', 'A'};
+        char[] input2 = new char[]{'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'};
+
+        short[] input3 = new short[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        short[] input4 = new short[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
+
+        byte[] input5 = new byte[]{21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
+        byte[] input6 = new byte[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 40};
+
+        test("testSnippet1", input1);
+        test("testSnippet2", input2);
+        test("testSnippet3", input3);
+        test("testSnippet4", input4);
+        test("testSnippet5", input5);
+        test("testSnippet6", input6);
+    }
+}
--- /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/ea/CountUppercaseParallelTest.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,42 @@
+/*
+ * 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.ea;
+
+import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.junit.Test;
+
+public class CountUppercaseParallelTest extends GraalCompilerTest {
+    public static long count(CharSequence sentence) {
+        return sentence.chars().parallel().filter(c -> Character.isUpperCase(c)).count();
+    }
+
+    @Test
+    public void testCount() {
+        String sequence = "In 2017 I would like to run ALL languages in one VM.";
+        for (int i = 0; i < 5000; i++) {
+            count(sequence);
+        }
+        test("count", sequence);
+    }
+
+}
--- /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/ea/PartialEscapeAnalysisTreesTest.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,136 @@
+/*
+ * 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.ea;
+
+import java.util.HashSet;
+
+import org.graalvm.compiler.api.directives.GraalDirectives;
+import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.nodes.debug.BlackholeNode;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
+import org.junit.Assert;
+import org.junit.Test;
+
+import jdk.vm.ci.code.InstalledCode;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+/*
+ * Test whether complex tree structures properly maintain identity.
+ */
+public class PartialEscapeAnalysisTreesTest extends EATestBase {
+
+    static class TreeNode {
+        TreeNode left;
+        TreeNode right;
+
+        TreeNode() {
+
+        }
+
+        TreeNode(TreeNode left, TreeNode right) {
+            this.left = left;
+            this.right = right;
+        }
+
+        public void visit(HashSet<TreeNode> instances) {
+            instances.add(this);
+            if (left != null) {
+                left.visit(instances);
+            }
+            if (right != null) {
+                right.visit(instances);
+            }
+        }
+
+        int countInstances() {
+            HashSet<TreeNode> instances = new HashSet<>();
+            visit(instances);
+            return instances.size();
+        }
+    }
+
+    public static TreeNode buildTree(boolean a) {
+        TreeNode leftChild;
+        TreeNode rightChild;
+        TreeNode taskToFork;
+        TreeNode task;
+        if (a) {
+            GraalDirectives.blackhole(new TreeNode());
+            leftChild = new TreeNode();
+            rightChild = new TreeNode();
+            task = new TreeNode(leftChild, rightChild);
+            taskToFork = rightChild;
+            GraalDirectives.blackhole(task);
+        } else {
+            leftChild = new TreeNode();
+            rightChild = new TreeNode();
+            task = new TreeNode(leftChild, rightChild);
+            taskToFork = leftChild;
+            GraalDirectives.blackhole(task);
+        }
+        if (taskToFork.left == null) {
+            taskToFork.left = new TreeNode();
+        }
+
+        return new TreeNode(task, null);
+    }
+
+    @Test
+    public void testBuildTree() {
+        testGraph("buildTree");
+    }
+
+    /**
+     * Prepare a graph that includes some blackholes and then remove the blackholes and compile
+     * normally to create an unusual situation for PEA.
+     */
+    @SuppressWarnings("try")
+    public void testGraph(String name) {
+        ResolvedJavaMethod method = getResolvedJavaMethod(name);
+
+        prepareGraph(name, true);
+        try (DebugContext.Scope s = graph.getDebug().scope(getClass(), method, getCodeCache(), graph)) {
+            for (BlackholeNode node : graph.getNodes().filter(BlackholeNode.class)) {
+                graph.removeFixed(node);
+            }
+            new DeadCodeEliminationPhase().apply(graph);
+            new CanonicalizerPhase().apply(graph, context);
+
+            InstalledCode code = getCode(method, graph, true);
+
+            GraalCompilerTest.Result r = executeExpected(method, null, true);
+            int expectedInstances = ((TreeNode) r.returnValue).countInstances();
+            TreeNode r2 = (TreeNode) code.executeVarargs(true);
+            Assert.assertEquals("Wrong number of nodes in tree", expectedInstances, r2.countInstances());
+
+            r = executeExpected(method, null, false);
+            expectedInstances = ((TreeNode) r.returnValue).countInstances();
+            r2 = (TreeNode) code.executeVarargs(false);
+            Assert.assertEquals("Wrong number of nodes in tree", expectedInstances, r2.countInstances());
+        } catch (Throwable e) {
+            throw graph.getDebug().handle(e);
+        }
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java	Fri Aug 04 19:59:33 2017 -0700
@@ -38,6 +38,10 @@
     private static final long fieldOffset1;
     private static final long fieldOffset2;
 
+    private static final long byteArrayBaseOffset;
+    private static final long intArrayBaseOffset;
+    private static final long longArrayBaseOffset;
+
     static {
         try {
             long localFieldOffset1 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("x"));
@@ -51,6 +55,9 @@
                 fieldOffset2 = UNSAFE.objectFieldOffset(TestClassInt.class.getField("z"));
             }
             assert fieldOffset2 == fieldOffset1 + 4;
+            byteArrayBaseOffset = UNSAFE.arrayBaseOffset(byte[].class);
+            intArrayBaseOffset = UNSAFE.arrayBaseOffset(int[].class);
+            longArrayBaseOffset = UNSAFE.arrayBaseOffset(long[].class);
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -195,4 +202,77 @@
         }
         return x;
     }
+
+    public static int testWriteIntToByteArraySnippet() {
+        byte[] array = new byte[4];
+        UNSAFE.putInt(array, byteArrayBaseOffset, 0x01020304);
+        return array[0];
+    }
+
+    @Test
+    public void testWriteIntToByteArray() {
+        test("testWriteIntToByteArraySnippet");
+    }
+
+    public static byte testWriteSignedExtendedByteToByteArraySnippet(byte b) {
+        byte[] array = new byte[4];
+        array[0] = 0x01;
+        array[1] = 0x02;
+        array[2] = 0x03;
+        array[3] = 0x04;
+        UNSAFE.putInt(array, byteArrayBaseOffset, b);
+        return array[3];
+    }
+
+    @Test
+    public void testWriteSignedExtendedByteToByteArray() {
+        test("testWriteSignedExtendedByteToByteArraySnippet", (byte) 0);
+    }
+
+    public static int testWriteLongToIntArraySnippet() {
+        int[] array = new int[2];
+        UNSAFE.putLong(array, intArrayBaseOffset, 0x0102030405060708L);
+        return array[0];
+    }
+
+    @Test
+    public void testWriteLongToIntArray() {
+        test("testWriteLongToIntArraySnippet");
+    }
+
+    public static int testWriteByteToIntArraySnippet() {
+        int[] array = new int[1];
+        array[0] = 0x01020304;
+        UNSAFE.putByte(array, intArrayBaseOffset, (byte) 0x05);
+        return array[0];
+    }
+
+    @Test
+    public void testWriteByteToIntArray() {
+        test("testWriteByteToIntArraySnippet");
+    }
+
+    public static long testWriteIntToLongArraySnippet() {
+        long[] array = new long[1];
+        array[0] = 0x0102030405060708L;
+        UNSAFE.putInt(array, longArrayBaseOffset, 0x04030201);
+        return array[0];
+    }
+
+    @Test
+    public void testWriteIntToLongArray() {
+        test("testWriteIntToLongArraySnippet");
+    }
+
+    public static float testWriteFloatToIntArraySnippet() {
+        float[] array = new float[1];
+        UNSAFE.putInt(array, intArrayBaseOffset, Float.floatToRawIntBits(0.5f));
+        return array[0];
+    }
+
+    @Test
+    public void testWriteFloatToIntArray() {
+        test("testWriteFloatToIntArraySnippet");
+    }
+
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugContextTest.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/DebugContextTest.java	Fri Aug 04 19:59:33 2017 -0700
@@ -227,7 +227,7 @@
      */
     @Test
     public void testInvariantChecking() throws InterruptedException {
-        Assume.assumeTrue(Assertions.ENABLED);
+        Assume.assumeTrue(Assertions.assertionsEnabled());
         EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
         // Configure with an option that enables counters
         map.put(DebugOptions.Counters, "");
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Assertions.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Assertions.java	Fri Aug 04 19:59:33 2017 -0700
@@ -22,6 +22,11 @@
  */
 package org.graalvm.compiler.debug;
 
+import org.graalvm.compiler.options.Option;
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionType;
+import org.graalvm.compiler.options.OptionValues;
+
 /**
  * Utility for query whether assertions are enabled.
  */
@@ -30,11 +35,28 @@
      * Determines if assertions are enabled. Strictly speaking, this may only be true for the
      * {@link Assertions} class but we assume assertions are enabled/disabled for Graal as a whole.
      */
-    public static final boolean ENABLED = assertionsEnabled();
-
-    private static boolean assertionsEnabled() {
+    public static boolean assertionsEnabled() {
         boolean enabled = false;
         assert (enabled = true) == true;
         return enabled;
     }
+
+    /**
+     * Determines if detailed assertions are enabled. This requires that the normal assertions are
+     * also enabled.
+     *
+     * @param values the current OptionValues that might define a value for DetailAsserts.
+     */
+    public static boolean detailedAssertionsEnabled(OptionValues values) {
+        return assertionsEnabled() && Options.DetailedAsserts.getValue(values);
+    }
+
+    // @formatter:off
+    public static class Options {
+
+        @Option(help = "Enable expensive assertions. (Require normal assertions enabled)", type = OptionType.Debug)
+        public static final OptionKey<Boolean> DetailedAsserts = new OptionKey<>(true);
+
+    }
+    // @formatter:on
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfigImpl.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugConfigImpl.java	Fri Aug 04 19:59:33 2017 -0700
@@ -65,7 +65,7 @@
                         DebugOptions.TrackMemUse.getValue(options),
                         DebugOptions.Time.getValue(options),
                         DebugOptions.Dump.getValue(options),
-                        DebugOptions.Verify.getValue(options),
+                        getVerifyOptionValue(options),
                         DebugOptions.MethodFilter.getValue(options),
                         output, dumpHandlers, verifyHandlers);
     }
@@ -99,6 +99,10 @@
         this.output = output;
     }
 
+    private static String getVerifyOptionValue(OptionValues values) {
+        return !DebugOptions.Verify.hasBeenSet(values) && Assertions.assertionsEnabled() ? "" : DebugOptions.Verify.getValue(values);
+    }
+
     @Override
     public OptionValues getOptions() {
         return options;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java	Fri Aug 04 19:59:33 2017 -0700
@@ -32,11 +32,9 @@
 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;
@@ -374,16 +372,7 @@
                     }
                 }
             }
-            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);
+            currentConfig = new DebugConfigImpl(options, logStream, dumpHandlers, verifyHandlers);
             currentScope = new ScopeImpl(this, Thread.currentThread());
             currentScope.updateFlags(currentConfig);
             metricsEnabled = true;
@@ -575,7 +564,7 @@
         }
     }
 
-    private final Invariants invariants = Assertions.ENABLED ? new Invariants() : null;
+    private final Invariants invariants = Assertions.assertionsEnabled() ? new Invariants() : null;
 
     static StackTraceElement[] getStackTrace(Thread thread) {
         return thread.getStackTrace();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugOptions.java	Fri Aug 04 19:59:33 2017 -0700
@@ -75,7 +75,7 @@
     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);
+    public static final OptionKey<String> Verify = new OptionKey<>(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)
@@ -115,7 +115,7 @@
     @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);
+    public static final OptionKey<Boolean> ShowDumpFiles = new OptionKey<>(false);
 
     @Option(help = "Enable dumping to the C1Visualizer. Enabling this option implies PrintBackendCFG.", type = OptionType.Debug)
     public static final OptionKey<Boolean> PrintCFG = new OptionKey<>(false);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DiagnosticsOutputDirectory.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DiagnosticsOutputDirectory.java	Fri Aug 04 19:59:33 2017 -0700
@@ -148,7 +148,8 @@
                         @Override
                         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                             if (attrs.isRegularFile()) {
-                                ZipEntry ze = new ZipEntry(file.toString());
+                                String name = dir.relativize(file).toString();
+                                ZipEntry ze = new ZipEntry(name);
                                 zos.putNextEntry(ze);
                                 zos.write(Files.readAllBytes(file));
                                 zos.closeEntry();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph.test/src/org/graalvm/compiler/graph/test/NodeMapTest.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -119,7 +119,7 @@
         TestNode newNode = graph.add(new TestNode());
         try {
             map.get(newNode);
-            fail("expected " + (Assertions.ENABLED ? AssertionError.class.getSimpleName() : ArrayIndexOutOfBoundsException.class.getSimpleName()));
+            fail("expected " + (Assertions.assertionsEnabled() ? AssertionError.class.getSimpleName() : ArrayIndexOutOfBoundsException.class.getSimpleName()));
         } catch (AssertionError ae) {
             // thrown when assertions are enabled
         } catch (ArrayIndexOutOfBoundsException e) {
@@ -136,7 +136,7 @@
         TestNode newNode = graph.add(new TestNode());
         try {
             map.set(newNode, 1);
-            fail("expected " + (Assertions.ENABLED ? AssertionError.class.getSimpleName() : ArrayIndexOutOfBoundsException.class.getSimpleName()));
+            fail("expected " + (Assertions.assertionsEnabled() ? AssertionError.class.getSimpleName() : ArrayIndexOutOfBoundsException.class.getSimpleName()));
         } catch (AssertionError ae) {
             // thrown when assertions are enabled
         } catch (ArrayIndexOutOfBoundsException e) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSuitesProvider.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSuitesProvider.java	Fri Aug 04 19:59:33 2017 -0700
@@ -31,9 +31,11 @@
 import org.graalvm.compiler.phases.common.AddressLoweringByUsePhase;
 import org.graalvm.compiler.phases.common.ExpandLogicPhase;
 import org.graalvm.compiler.phases.common.FixReadsPhase;
+import org.graalvm.compiler.phases.common.PropagateDeoptimizeProbabilityPhase;
 import org.graalvm.compiler.phases.tiers.LowTierContext;
 import org.graalvm.compiler.phases.tiers.Suites;
 import org.graalvm.compiler.phases.tiers.SuitesCreator;
+import org.graalvm.compiler.replacements.aarch64.AArch64ReadReplacementPhase;
 
 import java.util.ListIterator;
 
@@ -60,6 +62,9 @@
         }
         findPhase.add(new AddressLoweringByUsePhase(addressLoweringByUse));
 
+        findPhase = suites.getLowTier().findPhase(PropagateDeoptimizeProbabilityPhase.class);
+        findPhase.add(new AArch64ReadReplacementPhase());
+
         return suites;
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSuitesCreator.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSuitesCreator.java	Fri Aug 04 19:59:33 2017 -0700
@@ -23,7 +23,7 @@
 package org.graalvm.compiler.hotspot.amd64;
 
 import org.graalvm.compiler.core.amd64.AMD64SuitesCreator;
-import org.graalvm.compiler.core.common.GraalOptions;
+import org.graalvm.compiler.debug.Assertions;
 import org.graalvm.compiler.hotspot.lir.HotSpotZapRegistersPhase;
 import org.graalvm.compiler.lir.phases.LIRSuites;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
@@ -39,7 +39,7 @@
     @Override
     public LIRSuites createLIRSuites(OptionValues options) {
         LIRSuites lirSuites = super.createLIRSuites(options);
-        if (GraalOptions.DetailedAsserts.getValue(options)) {
+        if (Assertions.detailedAssertionsEnabled(options)) {
             lirSuites.getPostAllocationOptimizationStage().appendPhase(new HotSpotZapRegistersPhase());
         }
         return lirSuites;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java	Fri Aug 04 19:59:33 2017 -0700
@@ -82,6 +82,8 @@
                         "test");
     }
 
+    private static final boolean VERBOSE = Boolean.getBoolean(CompilationWrapperTest.class.getSimpleName() + ".verbose");
+
     private static void testHelper(List<String> extraVmArgs, String... mainClassAndArgs) throws IOException, InterruptedException {
         final File dumpPath = new File(CompilationWrapperTest.class.getSimpleName() + "_" + System.currentTimeMillis()).getAbsoluteFile();
         List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine());
@@ -92,7 +94,9 @@
         vmArgs.addAll(extraVmArgs);
 
         Subprocess proc = SubprocessUtil.java(vmArgs, mainClassAndArgs);
-        System.out.println(proc);
+        if (VERBOSE) {
+            System.out.println(proc);
+        }
 
         String forcedCrashString = "Forced crash after compiling";
         String diagnosticOutputFilePrefix = "Graal diagnostic output saved in ";
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java	Fri Aug 04 19:59:33 2017 -0700
@@ -53,6 +53,7 @@
 import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
 import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult;
 import jdk.vm.ci.hotspot.HotSpotInstalledCode;
+import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory;
 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
 import jdk.vm.ci.hotspot.HotSpotNmethod;
 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
@@ -145,7 +146,7 @@
              * respect CompilationFailureAction if it has been explicitly set.
              */
             if (actionKey == CompilationFailureAction && !actionKey.hasBeenSet(values)) {
-                if (Assertions.ENABLED || compiler.getGraalRuntime().isBootstrapping()) {
+                if (Assertions.assertionsEnabled() || compiler.getGraalRuntime().isBootstrapping()) {
                     return ExitVM;
                 }
             }
@@ -305,6 +306,10 @@
             if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) {
                 return HotSpotCompilationRequestResult.failure("Already compiled", false);
             }
+            if (HotSpotGraalCompilerFactory.checkGraalCompileOnlyFilter(method.getDeclaringClass().toJavaName(), method.getName(), method.getSignature().toString(),
+                            HotSpotJVMCICompilerFactory.CompilationLevel.FullOptimization) != HotSpotJVMCICompilerFactory.CompilationLevel.FullOptimization) {
+                return HotSpotCompilationRequestResult.failure("GraalCompileOnly excluded", false);
+            }
         }
 
         HotSpotCompilationWrapper compilation = new HotSpotCompilationWrapper(compilationEvent);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilerConfigurationFactory.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilerConfigurationFactory.java	Fri Aug 04 19:59:33 2017 -0700
@@ -130,7 +130,7 @@
      * List used to assert uniqueness of {@link #name} and {@link #autoSelectionPriority} across all
      * {@link CompilerConfigurationFactory} instances.
      */
-    private static final List<CompilerConfigurationFactory> factories = Assertions.ENABLED ? new ArrayList<>() : null;
+    private static final List<CompilerConfigurationFactory> factories = Assertions.assertionsEnabled() ? new ArrayList<>() : null;
 
     private static boolean checkAndAddNewFactory(CompilerConfigurationFactory factory) {
         for (CompilerConfigurationFactory other : factories) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalCompilerFactory.java	Fri Aug 04 19:59:33 2017 -0700
@@ -162,9 +162,20 @@
      * This method is static so it can be exercised during initialization.
      */
     private static CompilationLevel adjustCompilationLevelInternal(Class<?> declaringClass, String name, String signature, CompilationLevel level) {
+        if (compileGraalWithC1Only) {
+            if (level.ordinal() > CompilationLevel.Simple.ordinal()) {
+                String declaringClassName = declaringClass.getName();
+                if (declaringClassName.startsWith("jdk.vm.ci") || declaringClassName.startsWith("org.graalvm") || declaringClassName.startsWith("com.oracle.graal")) {
+                    return CompilationLevel.Simple;
+                }
+            }
+        }
+        return checkGraalCompileOnlyFilter(declaringClass.getName(), name, signature, level);
+    }
+
+    public static CompilationLevel checkGraalCompileOnlyFilter(String declaringClassName, String name, String signature, CompilationLevel level) {
         if (graalCompileOnlyFilter != null) {
             if (level == CompilationLevel.FullOptimization) {
-                String declaringClassName = declaringClass.getName();
                 HotSpotSignature sig = null;
                 for (MethodFilter filter : graalCompileOnlyFilter) {
                     if (filter.hasSignature() && sig == null) {
@@ -177,14 +188,6 @@
                 return CompilationLevel.Simple;
             }
         }
-        if (compileGraalWithC1Only) {
-            if (level.ordinal() > CompilationLevel.Simple.ordinal()) {
-                String declaringClassName = declaringClass.getName();
-                if (declaringClassName.startsWith("jdk.vm.ci") || declaringClassName.startsWith("org.graalvm") || declaringClassName.startsWith("com.oracle.graal")) {
-                    return CompilationLevel.Simple;
-                }
-            }
-        }
         return level;
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java	Fri Aug 04 19:59:33 2017 -0700
@@ -138,8 +138,6 @@
                 if (build >= JVMCI9_MIN_EA_BUILD) {
                     return;
                 }
-                // Using Object.equals suppresses Eclipse's "Dead code" warning.
-                // Unfortunately @SuppressWarnings("unused") can only be applied at method level.
                 if (Objects.equals(JVMCI9_MIN_EA_BUILD, Integer.MAX_VALUE)) {
                     failVersionCheck(exitOnFailure, "This version of Graal is not compatible with any JDK 9 Early Access build.%n");
                 } else {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ExceptionHandlerStub.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ExceptionHandlerStub.java	Fri Aug 04 19:59:33 2017 -0700
@@ -153,7 +153,7 @@
     @Fold
     @SuppressWarnings("all")
     static boolean assertionsEnabled(@InjectedParameter GraalHotSpotVMConfig config) {
-        return Assertions.ENABLED || cAssertionsEnabled(config);
+        return Assertions.assertionsEnabled() || cAssertionsEnabled(config);
     }
 
     public static final ForeignCallDescriptor EXCEPTION_HANDLER_FOR_PC = newDescriptor(ExceptionHandlerStub.class, "exceptionHandlerForPc", Word.class, Word.class);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/UnwindExceptionToCallerStub.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/UnwindExceptionToCallerStub.java	Fri Aug 04 19:59:33 2017 -0700
@@ -116,7 +116,7 @@
     @Fold
     @SuppressWarnings("all")
     static boolean assertionsEnabled(@InjectedParameter GraalHotSpotVMConfig config) {
-        return Assertions.ENABLED || cAssertionsEnabled(config);
+        return Assertions.assertionsEnabled() || cAssertionsEnabled(config);
     }
 
     public static final ForeignCallDescriptor EXCEPTION_HANDLER_FOR_RETURN_ADDRESS = newDescriptor(UnwindExceptionToCallerStub.class, "exceptionHandlerForReturnAddress", Word.class, Word.class,
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java	Fri Aug 04 19:59:33 2017 -0700
@@ -371,6 +371,7 @@
 import org.graalvm.compiler.nodes.extended.LoadHubNode;
 import org.graalvm.compiler.nodes.extended.LoadMethodNode;
 import org.graalvm.compiler.nodes.extended.MembarNode;
+import org.graalvm.compiler.nodes.extended.StateSplitProxyNode;
 import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.ClassInitializationPlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
@@ -1236,6 +1237,10 @@
         }
     }
 
+    protected StateSplitProxyNode genVolatileFieldReadProxy(ValueNode fieldRead) {
+        return new StateSplitProxyNode(fieldRead);
+    }
+
     protected ValueNode emitExplicitNullCheck(ValueNode receiver) {
         if (StampTool.isPointerNonNull(receiver.stamp())) {
             return receiver;
@@ -1429,7 +1434,7 @@
         createNonInlinedInvoke(withExceptionEdge, bci(), callTarget, resultType);
     }
 
-    private Invoke appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) {
+    protected Invoke appendInvoke(InvokeKind initialInvokeKind, ResolvedJavaMethod initialTargetMethod, ValueNode[] args) {
         ResolvedJavaMethod targetMethod = initialTargetMethod;
         InvokeKind invokeKind = initialInvokeKind;
         if (initialInvokeKind.isIndirect()) {
@@ -1676,7 +1681,7 @@
         final Mark mark;
 
         InvocationPluginAssertions(InvocationPlugin plugin, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType) {
-            guarantee(Assertions.ENABLED, "%s should only be loaded and instantiated if assertions are enabled", getClass().getSimpleName());
+            guarantee(Assertions.assertionsEnabled(), "%s should only be loaded and instantiated if assertions are enabled", getClass().getSimpleName());
             this.plugin = plugin;
             this.targetMethod = targetMethod;
             this.args = args;
@@ -1908,7 +1913,7 @@
                 }
             }
 
-            InvocationPluginAssertions assertions = Assertions.ENABLED ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null;
+            InvocationPluginAssertions assertions = Assertions.assertionsEnabled() ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null;
             if (plugin.execute(this, targetMethod, pluginReceiver, args)) {
                 afterInvocationPluginExecution(true, assertions, intrinsicGuard, invokeKind, args, targetMethod, resultType, returnType);
                 return true;
@@ -3788,11 +3793,22 @@
             }
         }
 
-        frameState.push(resolvedField.getJavaKind(), append(genLoadField(receiver, resolvedField)));
+        ValueNode fieldRead = append(genLoadField(receiver, resolvedField));
+
         if (resolvedField.getDeclaringClass().getName().equals("Ljava/lang/ref/Reference;") && resolvedField.getName().equals("referent")) {
             LocationIdentity referentIdentity = new FieldLocationIdentity(resolvedField);
             append(new MembarNode(0, referentIdentity));
         }
+
+        JavaKind fieldKind = resolvedField.getJavaKind();
+
+        if (resolvedField.isVolatile() && fieldRead instanceof LoadFieldNode) {
+            StateSplitProxyNode readProxy = append(genVolatileFieldReadProxy(fieldRead));
+            frameState.push(fieldKind, readProxy);
+            readProxy.setStateAfter(frameState.create(stream.nextBCI(), readProxy));
+        } else {
+            frameState.push(fieldKind, fieldRead);
+        }
     }
 
     /**
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/Interval.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/Interval.java	Fri Aug 04 19:59:33 2017 -0700
@@ -22,7 +22,6 @@
  */
 package org.graalvm.compiler.lir.alloc.lsra;
 
-import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
@@ -39,6 +38,7 @@
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.util.IntList;
 import org.graalvm.compiler.core.common.util.Util;
+import org.graalvm.compiler.debug.Assertions;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.Variable;
@@ -1140,7 +1140,7 @@
         // split list of use positions
         result.usePosList = usePosList.splitAt(splitPos);
 
-        if (DetailedAsserts.getValue(allocator.getOptions())) {
+        if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
             for (int i = 0; i < usePosList.size(); i++) {
                 assert usePosList.usePos(i) < splitPos;
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScan.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -27,7 +27,6 @@
 import static jdk.vm.ci.code.ValueUtil.isIllegal;
 import static jdk.vm.ci.code.ValueUtil.isLegal;
 import static jdk.vm.ci.code.ValueUtil.isRegister;
-import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
 import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
 
@@ -40,6 +39,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.Assertions;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
@@ -206,7 +206,7 @@
         this.rangeEndMarker = new Range(Integer.MAX_VALUE, Integer.MAX_VALUE, null);
         this.intervalEndMarker = new Interval(Value.ILLEGAL, Interval.END_MARKER_OPERAND_NUMBER, null, rangeEndMarker);
         this.intervalEndMarker.next = intervalEndMarker;
-        this.detailedAsserts = DetailedAsserts.getValue(ir.getOptions());
+        this.detailedAsserts = Assertions.detailedAssertionsEnabled(ir.getOptions());
     }
 
     public LIRGenerationResult getLIRGenerationResult() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanEliminateSpillMovePhase.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -23,7 +23,6 @@
 package org.graalvm.compiler.lir.alloc.lsra;
 
 import static jdk.vm.ci.code.ValueUtil.isRegister;
-import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
 import static org.graalvm.compiler.lir.phases.LIRPhase.Options.LIROptimization;
@@ -31,6 +30,7 @@
 import java.util.ArrayList;
 
 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
+import org.graalvm.compiler.debug.Assertions;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInsertionBuffer;
@@ -99,7 +99,7 @@
              */
             Interval interval;
             interval = allocator.createUnhandledLists(mustStoreAtDefinition, null).getLeft();
-            if (DetailedAsserts.getValue(allocator.getOptions())) {
+            if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
                 checkIntervals(debug, interval);
             }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -26,7 +26,6 @@
 import static jdk.vm.ci.code.ValueUtil.asStackSlot;
 import static jdk.vm.ci.code.ValueUtil.isRegister;
 import static jdk.vm.ci.code.ValueUtil.isStackSlot;
-import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
 import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
 import static org.graalvm.compiler.lir.debug.LIRGenerationDebugContext.getSourceForOperandFromDebugContext;
@@ -41,6 +40,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.Assertions;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
@@ -88,7 +88,7 @@
         debug.dump(DebugContext.VERBOSE_LEVEL, lirGenRes.getLIR(), "Before register allocation");
         computeLocalLiveSets();
         computeGlobalLiveSets();
-        buildIntervals(DetailedAsserts.getValue(allocator.getOptions()));
+        buildIntervals(Assertions.detailedAssertionsEnabled(allocator.getOptions()));
     }
 
     /**
@@ -364,14 +364,14 @@
                 }
             } while (changeOccurred);
 
-            if (DetailedAsserts.getValue(allocator.getOptions())) {
+            if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
                 verifyLiveness();
             }
 
             // check that the liveIn set of the first block is empty
             AbstractBlockBase<?> startBlock = allocator.getLIR().getControlFlowGraph().getStartBlock();
             if (allocator.getBlockData(startBlock).liveIn.cardinality() != 0) {
-                if (DetailedAsserts.getValue(allocator.getOptions())) {
+                if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
                     reportFailure(numBlocks);
                 }
                 // bailout if this occurs in product mode.
@@ -570,7 +570,7 @@
             ValueMoveOp move = ValueMoveOp.asValueMoveOp(op);
             if (optimizeMethodArgument(move.getInput())) {
                 StackSlot slot = asStackSlot(move.getInput());
-                if (DetailedAsserts.getValue(allocator.getOptions())) {
+                if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
                     assert op.id() > 0 : "invalid id";
                     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";
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/bu/BottomUpAllocator.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -26,7 +26,6 @@
 import static jdk.vm.ci.code.ValueUtil.asRegister;
 import static jdk.vm.ci.code.ValueUtil.isIllegal;
 import static jdk.vm.ci.code.ValueUtil.isRegister;
-import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
 import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
 import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
@@ -42,6 +41,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.Assertions;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.InstructionValueProcedure;
@@ -224,7 +224,7 @@
                 debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
             }
 
-            if (DetailedAsserts.getValue(getLIR().getOptions())) {
+            if (Assertions.detailedAssertionsEnabled(getLIR().getOptions())) {
                 assert lir.getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label";
 
                 /*
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceInterval.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceInterval.java	Fri Aug 04 19:59:33 2017 -0700
@@ -26,7 +26,6 @@
 import static jdk.vm.ci.code.ValueUtil.isIllegal;
 import static jdk.vm.ci.code.ValueUtil.isRegister;
 import static jdk.vm.ci.code.ValueUtil.isStackSlot;
-import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVirtualStackSlot;
@@ -39,6 +38,7 @@
 
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.util.Util;
+import org.graalvm.compiler.debug.Assertions;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.lir.LIRInstruction;
 import org.graalvm.compiler.lir.Variable;
@@ -722,7 +722,7 @@
 
         // do not add use positions for precolored intervals because they are never used
         if (registerPriority != RegisterPriority.None) {
-            if (DetailedAsserts.getValue(options)) {
+            if (Assertions.detailedAssertionsEnabled(options)) {
                 for (int i = 0; i < numUsePos(); i++) {
                     assert pos <= getUsePos(i) : "already added a use-position with lower position";
                     if (i > 0) {
@@ -802,7 +802,7 @@
         // split list of use positions
         splitUsePosAt(result, splitPos);
 
-        if (DetailedAsserts.getValue(allocator.getOptions())) {
+        if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
             for (int i = 0; i < numUsePos(); i++) {
                 assert getUsePos(i) < splitPos;
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanEliminateSpillMovePhase.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -23,7 +23,6 @@
 package org.graalvm.compiler.lir.alloc.trace.lsra;
 
 import static jdk.vm.ci.code.ValueUtil.isRegister;
-import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
 import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
@@ -34,6 +33,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.Assertions;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
 import org.graalvm.compiler.lir.LIRInsertionBuffer;
@@ -84,7 +84,7 @@
              * by Interval.spillDefinitionPos.
              */
             TraceInterval interval = allocator.createUnhandledListBySpillPos(spilledIntervals);
-            if (DetailedAsserts.getValue(allocator.getOptions())) {
+            if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
                 checkIntervals(debug, interval);
             }
             if (debug.isLogEnabled()) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanPhase.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -27,7 +27,6 @@
 import static jdk.vm.ci.code.ValueUtil.isIllegal;
 import static jdk.vm.ci.code.ValueUtil.isLegal;
 import static jdk.vm.ci.code.ValueUtil.isRegister;
-import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
 import static org.graalvm.compiler.lir.LIRValueUtil.isVariable;
 
 import java.util.ArrayList;
@@ -39,6 +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.Assertions;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.debug.Indent;
@@ -603,7 +603,7 @@
 
                     TRACE_LINEAR_SCAN_ASSIGN_LOCATIONS_PHASE.apply(target, lirGenRes, trace, spillMoveFactory, registerAllocationConfig, traceBuilderResult, this, false);
 
-                    if (DetailedAsserts.getValue(options)) {
+                    if (Assertions.detailedAssertionsEnabled(options)) {
                         verifyIntervals();
                     }
                 } catch (Throwable e) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/trace/lsra/TraceLinearScanResolveDataFlowPhase.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -23,7 +23,6 @@
 package org.graalvm.compiler.lir.alloc.trace.lsra;
 
 import static jdk.vm.ci.code.ValueUtil.isRegister;
-import static org.graalvm.compiler.core.common.GraalOptions.DetailedAsserts;
 import static org.graalvm.compiler.lir.LIRValueUtil.asConstant;
 import static org.graalvm.compiler.lir.LIRValueUtil.asVariable;
 import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
@@ -36,6 +35,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.Assertions;
 import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.Indent;
@@ -97,7 +97,7 @@
                     debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
                 }
 
-                if (DetailedAsserts.getValue(allocator.getOptions())) {
+                if (Assertions.detailedAssertionsEnabled(allocator.getOptions())) {
                     assert allocator.getLIR().getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label";
 
                     /*
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/asm/CompilationResultBuilder.java	Fri Aug 04 19:59:33 2017 -0700
@@ -171,7 +171,7 @@
         assert frameContext != null;
         this.dataCache = dataCache;
 
-        if (dataBuilder.needDetailedPatchingInformation() || Assertions.ENABLED) {
+        if (dataBuilder.needDetailedPatchingInformation() || Assertions.assertionsEnabled()) {
             /*
              * Always enabled in debug mode, even when the VM does not request detailed information,
              * to increase test coverage.
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/AllocationStage.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/phases/AllocationStage.java	Fri Aug 04 19:59:33 2017 -0700
@@ -24,7 +24,7 @@
 
 import static org.graalvm.compiler.core.common.GraalOptions.TraceRA;
 
-import org.graalvm.compiler.core.common.GraalOptions;
+import org.graalvm.compiler.debug.Assertions;
 import org.graalvm.compiler.lir.alloc.AllocationStageVerifier;
 import org.graalvm.compiler.lir.alloc.lsra.LinearScanPhase;
 import org.graalvm.compiler.lir.alloc.trace.GlobalLivenessAnalysisPhase;
@@ -58,7 +58,7 @@
         // currently we mark locations only if we do register allocation
         appendPhase(new LocationMarkerPhase());
 
-        if (GraalOptions.DetailedAsserts.getValue(options)) {
+        if (Assertions.detailedAssertionsEnabled(options)) {
             appendPhase(new AllocationStageVerifier());
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPartialUnrollPhase.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPartialUnrollPhase.java	Fri Aug 04 19:59:33 2017 -0700
@@ -45,12 +45,12 @@
     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) {
+            boolean changed = true;
+            while (changed) {
+                changed = false;
+                try (Graph.NodeEventScope nes = graph.trackNodeEvents(listener)) {
                     LoopsData dataCounted = new LoopsData(graph);
                     dataCounted.detectedCountedLoops();
-                    changed = false;
                     for (LoopEx loop : dataCounted.countedLoops()) {
                         if (!LoopTransformations.isUnrollableLoop(loop)) {
                             continue;
@@ -60,19 +60,20 @@
                                 // 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);
+                                LoopTransformations.partialUnroll(loop, graph);
                             }
+                            changed = true;
                         }
                     }
                     dataCounted.deleteUnusedNodes();
+
+                    if (!listener.getNodes().isEmpty()) {
+                        canonicalizer.applyIncremental(graph, context, listener.getNodes());
+                        listener.getNodes().clear();
+                    }
                 }
             }
-            if (!listener.getNodes().isEmpty()) {
-                canonicalizer.applyIncremental(graph, context, listener.getNodes());
-                listener.getNodes().clear();
-            }
         }
     }
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopTransformations.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -22,25 +22,13 @@
  */
 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.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;
@@ -59,23 +47,27 @@
 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.SafepointNode;
 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;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+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;
+
 public abstract class LoopTransformations {
 
     private LoopTransformations() {
@@ -154,254 +146,13 @@
         // TODO (gd) probabilities need some amount of fixup.. (probably also in other transforms)
     }
 
-    public static boolean partialUnroll(LoopEx loop, StructuredGraph graph) {
+    public static void 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,
@@ -475,12 +226,12 @@
     public static void insertPrePostLoops(LoopEx loop, StructuredGraph graph) {
         graph.getDebug().log("LoopTransformations.insertPrePostLoops %s", loop);
         LoopFragmentWhole preLoop = loop.whole();
-        CountedLoopInfo preCounted = preLoop.loop().counted();
+        CountedLoopInfo preCounted = loop.counted();
         IfNode preLimit = preCounted.getLimitTest();
         if (preLimit != null) {
             LoopBeginNode preLoopBegin = loop.loopBegin();
             InductionVariable preIv = preCounted.getCounter();
-            LoopExitNode preLoopExitNode = getSingleExitFromLoop(preLoopBegin);
+            LoopExitNode preLoopExitNode = preLoopBegin.getSingleLoopExit();
             FixedNode continuationNode = preLoopExitNode.next();
 
             // Each duplication is inserted after the original, ergo create the post loop first
@@ -497,7 +248,7 @@
 
             EndNode postEndNode = getBlockEndAfterLoopExit(postLoopBegin);
             AbstractMergeNode postMergeNode = postEndNode.merge();
-            LoopExitNode postLoopExitNode = getSingleExitFromLoop(postLoopBegin);
+            LoopExitNode postLoopExitNode = postLoopBegin.getSingleLoopExit();
 
             // Update the main loop phi initialization to carry from the pre loop
             for (PhiNode prePhiNode : preLoopBegin.phis()) {
@@ -511,7 +262,7 @@
 
             // In the case of no Bounds tests, we just flow right into the main loop
             AbstractBeginNode mainLandingNode = BeginNode.begin(postEntryNode);
-            LoopExitNode mainLoopExitNode = getSingleExitFromLoop(mainLoopBegin);
+            LoopExitNode mainLoopExitNode = mainLoopBegin.getSingleLoopExit();
             mainLoopExitNode.setNext(mainLandingNode);
             preLoopExitNode.setNext(mainLoopBegin.forwardEnd());
 
@@ -528,6 +279,14 @@
             preLoopBegin.setLoopFrequency(1);
             mainLoopBegin.setLoopFrequency(Math.max(0.0, mainLoopBegin.loopFrequency() - 2));
             postLoopBegin.setLoopFrequency(Math.max(0.0, postLoopBegin.loopFrequency() - 1));
+
+            // The pre and post loops don't require safepoints at all
+            for (SafepointNode safepoint : preLoop.nodes().filter(SafepointNode.class)) {
+                GraphUtil.removeFixedWithUnusedInputs(safepoint);
+            }
+            for (SafepointNode safepoint : postLoop.nodes().filter(SafepointNode.class)) {
+                GraphUtil.removeFixedWithUnusedInputs(safepoint);
+            }
         }
         graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "InsertPrePostLoops %s", loop);
     }
@@ -573,21 +332,11 @@
         }
     }
 
-    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();
+        FixedNode node = curLoopBegin.getSingleLoopExit().next();
         // Find the last node after the exit blocks starts
         return getBlockEnd(node);
     }
@@ -693,43 +442,18 @@
     }
 
     public static boolean isUnrollableLoop(LoopEx loop) {
-        if (!loop.isCounted()) {
+        if (!loop.isCounted() || !loop.counted().getCounter().isConstantStride()) {
             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;
+                return 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;
+        return false;
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/ReassociateInvariantPhase.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -26,24 +26,34 @@
 import org.graalvm.compiler.loop.LoopEx;
 import org.graalvm.compiler.loop.LoopsData;
 import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
 import org.graalvm.compiler.phases.Phase;
 
+/**
+ * Rearrange {@link BinaryArithmeticNode#isAssociative() associative binary operations} so that
+ * invariant parts of the expression can move outside of the loop.
+ */
 public class ReassociateInvariantPhase extends Phase {
 
     @SuppressWarnings("try")
     @Override
     protected void run(StructuredGraph graph) {
-        if (graph.hasLoops()) {
-            final LoopsData dataReassociate = new LoopsData(graph);
-            DebugContext debug = graph.getDebug();
-            try (DebugContext.Scope s = debug.scope("ReassociateInvariants")) {
+        int iterations = 0;
+        DebugContext debug = graph.getDebug();
+        try (DebugContext.Scope s = debug.scope("ReassociateInvariants")) {
+            boolean changed = true;
+            while (changed) {
+                changed = false;
+                final LoopsData dataReassociate = new LoopsData(graph);
                 for (LoopEx loop : dataReassociate.loops()) {
-                    loop.reassociateInvariants();
+                    changed |= loop.reassociateInvariants();
                 }
-            } catch (Throwable e) {
-                throw debug.handle(e);
+                dataReassociate.deleteUnusedNodes();
+                iterations++;
+                debug.dump(DebugContext.VERBOSE_LEVEL, graph, "after iteration %d", iterations);
             }
-            dataReassociate.deleteUnusedNodes();
+        } catch (Throwable e) {
+            throw debug.handle(e);
         }
     }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java	Fri Aug 04 19:59:33 2017 -0700
@@ -26,13 +26,12 @@
 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) {
+    protected boolean checkMidTierGraph(StructuredGraph graph) {
         NodeIterable<LoopBeginNode> loops = graph.getNodes().filter(LoopBeginNode.class);
         for (LoopBeginNode loop : loops) {
             if (loop.isMainLoop()) {
@@ -45,7 +44,7 @@
     public static long testMultiplySnippet(int arg) {
         long r = 1;
         for (int i = 0; branchProbability(0.99, i < arg); i++) {
-            r *= i;
+            r += r * i;
         }
         return r;
     }
@@ -59,7 +58,24 @@
         int c = 0;
         for (int i = 0; i < d; i++) {
             for (int j = 0; branchProbability(0.99, j < i); j++) {
-                c += j & 0x3;
+                c += c + j & 0x3;
+            }
+        }
+        return c;
+    }
+
+    @Test
+    public void testNestedSumBy2() {
+        for (int i = 0; i < 1000; i++) {
+            test("testNestedSumBy2Snippet", i);
+        }
+    }
+
+    public static int testNestedSumBy2Snippet(int d) {
+        int c = 0;
+        for (int i = 0; i < d; i++) {
+            for (int j = 0; branchProbability(0.99, j < i); j += 2) {
+                c += c + j & 0x3;
             }
         }
         return c;
@@ -75,7 +91,7 @@
     public static int testSumDownSnippet(int d) {
         int c = 0;
         for (int j = d; branchProbability(0.99, j > -4); j--) {
-            c += j & 0x3;
+            c += c + j & 0x3;
         }
         return c;
     }
@@ -83,21 +99,38 @@
     @Test
     public void testSumDown() {
         test("testSumDownSnippet", 1);
-        for (int i = 0; i < 8; i++) {
+        for (int i = 0; i < 160; i++) {
             test("testSumDownSnippet", i);
         }
     }
 
-    @Ignore("Phis which reference the backedge value of other Phis aren't handled properly")
+    public static int testSumDownBy2Snippet(int d) {
+        int c = 0;
+        for (int j = d; branchProbability(0.99, j > -4); j -= 2) {
+            c += c + j & 0x3;
+        }
+        return c;
+    }
+
+    @Test
+    public void testSumDownBy2() {
+        test("testSumDownBy2Snippet", 1);
+        for (int i = 0; i < 160; i++) {
+            test("testSumDownBy2Snippet", i);
+        }
+    }
+
     @Test
     public void testLoopCarried() {
         test("testLoopCarriedSnippet", 1, 2);
+        test("testLoopCarriedSnippet", 0, 4);
+        test("testLoopCarriedSnippet", 4, 0);
     }
 
     public static int testLoopCarriedSnippet(int a, int b) {
         int c = a;
         int d = b;
-        for (int j = 0; j < a; j++) {
+        for (int j = 0; branchProbability(0.99, j < a); j++) {
             d = c;
             c += 1;
         }
@@ -136,4 +169,16 @@
         test("testComplexSnippet", 1000);
     }
 
+    public static long testSignExtensionSnippet(long arg) {
+        long r = 1;
+        for (int i = 0; branchProbability(0.99, i < arg); i++) {
+            r *= i;
+        }
+        return r;
+    }
+
+    @Test
+    public void testSignExtension() {
+        test("testSignExtensionSnippet", 9L);
+    }
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java	Fri Aug 04 19:59:33 2017 -0700
@@ -22,15 +22,13 @@
  */
 package org.graalvm.compiler.loop;
 
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.Queue;
-
+import jdk.vm.ci.code.BytecodeFrame;
 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.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeBitMap;
 import org.graalvm.compiler.graph.iterators.NodePredicate;
@@ -72,7 +70,9 @@
 import org.graalvm.util.EconomicSet;
 import org.graalvm.util.Equivalence;
 
-import jdk.vm.ci.code.BytecodeFrame;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Queue;
 
 public class LoopEx {
     private final Loop<Block> loop;
@@ -164,33 +164,46 @@
 
     private class InvariantPredicate implements NodePredicate {
 
+        private final Graph.Mark mark;
+
+        InvariantPredicate() {
+            this.mark = loopBegin().graph().getMark();
+        }
+
         @Override
         public boolean apply(Node n) {
+            if (loopBegin().graph().isNew(mark, n)) {
+                // Newly created nodes are unknown.
+                return false;
+            }
             return isOutsideLoop(n);
         }
     }
 
-    public void reassociateInvariants() {
+    public boolean reassociateInvariants() {
+        int count = 0;
+        StructuredGraph graph = loopBegin().graph();
         InvariantPredicate invariant = new InvariantPredicate();
-        StructuredGraph graph = loopBegin().graph();
         for (BinaryArithmeticNode<?> binary : whole().nodes().filter(BinaryArithmeticNode.class)) {
             if (!binary.isAssociative()) {
                 continue;
             }
             ValueNode result = BinaryArithmeticNode.reassociate(binary, invariant, binary.getX(), binary.getY());
             if (result != binary) {
+                if (!result.isAlive()) {
+                    assert !result.isDeleted();
+                    result = graph.addOrUniqueWithInputs(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();
-                    result = graph.addOrUniqueWithInputs(result);
-                }
                 binary.replaceAtUsages(result);
                 GraphUtil.killWithUnusedFloatingInputs(binary);
+                count++;
             }
         }
+        return count != 0;
     }
 
     public boolean detectCounted() {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java	Fri Aug 04 19:59:33 2017 -0700
@@ -22,11 +22,7 @@
  */
 package org.graalvm.compiler.loop;
 
-import java.util.ArrayDeque;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.Iterator;
-
+import jdk.vm.ci.meta.TriState;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
@@ -57,7 +53,10 @@
 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
 import org.graalvm.util.EconomicMap;
 
-import jdk.vm.ci.meta.TriState;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Iterator;
 
 public abstract class LoopFragment {
 
@@ -78,7 +77,10 @@
         this.nodesReady = false;
     }
 
-    public LoopEx loop() {
+    /**
+     * Return the original LoopEx for this fragment. For duplicated fragments this returns null.
+     */
+    protected LoopEx loop() {
         return loop;
     }
 
@@ -172,6 +174,8 @@
             NodeIterable<Node> nodesIterable = original().nodes();
             duplicationMap = graph().addDuplicates(nodesIterable, graph(), nodesIterable.count(), dr);
             finishDuplication();
+            nodes = new NodeBitMap(graph());
+            nodes.markAll(duplicationMap.getValues());
             nodesReady = true;
         } else {
             // TODO (gd) apply fix ?
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java	Fri Aug 04 19:59:33 2017 -0700
@@ -22,9 +22,7 @@
  */
 package org.graalvm.compiler.loop;
 
-import java.util.LinkedList;
-import java.util.List;
-
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
 import org.graalvm.compiler.graph.Node;
@@ -34,25 +32,37 @@
 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.EndNode;
+import org.graalvm.compiler.nodes.FixedNode;
+import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.GuardPhiNode;
+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.MergeNode;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
+import org.graalvm.compiler.nodes.SafepointNode;
 import org.graalvm.compiler.nodes.StateSplit;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.ValuePhiNode;
 import org.graalvm.compiler.nodes.VirtualState.NodeClosure;
+import org.graalvm.compiler.nodes.calc.AddNode;
+import org.graalvm.compiler.nodes.calc.CompareNode;
+import org.graalvm.compiler.nodes.calc.SubNode;
 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 java.util.LinkedList;
+import java.util.List;
+
 public class LoopFragmentInside extends LoopFragment {
 
     /**
@@ -133,9 +143,126 @@
     }
 
     public void insertWithinAfter(LoopEx loop) {
-        assert this.isDuplicate() && this.original().loop() == loop;
+        assert isDuplicate() && original().loop() == loop;
 
         patchNodes(dataFixWithinAfter);
+
+        LoopBeginNode mainLoopBegin = loop.loopBegin();
+        for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
+            ValueNode duplicatedNode = getDuplicatedNode(mainPhiNode.valueAt(1));
+            if (duplicatedNode != null) {
+                mainPhiNode.setValueAt(1, duplicatedNode);
+            } else {
+                assert mainLoopBegin.isPhiAtMerge(mainPhiNode.valueAt(1));
+            }
+        }
+
+        placeNewSegmentAndCleanup(loop);
+
+        // Remove any safepoints from the original copy leaving only the duplicated one
+        assert loop.whole().nodes().filter(SafepointNode.class).count() == nodes().filter(SafepointNode.class).count();
+        for (SafepointNode safepoint : loop.whole().nodes().filter(SafepointNode.class)) {
+            GraphUtil.removeFixedWithUnusedInputs(safepoint);
+        }
+
+        int unrollFactor = mainLoopBegin.getUnrollFactor();
+
+        // Now use the previous unrollFactor to update the exit condition to power of two
+        StructuredGraph graph = mainLoopBegin.graph();
+        InductionVariable iv = loop.counted().getCounter();
+        CompareNode compareNode = (CompareNode) loop.counted().getLimitTest().condition();
+        ValueNode compareBound;
+        if (compareNode.getX() == iv.valueNode()) {
+            compareBound = compareNode.getY();
+        } else if (compareNode.getY() == iv.valueNode()) {
+            compareBound = compareNode.getX();
+        } else {
+            throw GraalError.shouldNotReachHere();
+        }
+        if (iv.direction() == InductionVariable.Direction.Up) {
+            ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * iv.constantStride()));
+            ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
+            compareNode.replaceFirstInput(compareBound, newLimit);
+        } else if (iv.direction() == InductionVariable.Direction.Down) {
+            ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * -iv.constantStride()));
+            ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
+            compareNode.replaceFirstInput(compareBound, newLimit);
+        }
+        mainLoopBegin.setUnrollFactor(unrollFactor * 2);
+        mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
+        graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "LoopPartialUnroll %s", loop);
+
+        mainLoopBegin.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "After insertWithinAfter %s", mainLoopBegin);
+    }
+
+    private void placeNewSegmentAndCleanup(LoopEx loop) {
+        CountedLoopInfo mainCounted = loop.counted();
+        LoopBeginNode mainLoopBegin = loop.loopBegin();
+        // Discard the segment entry and its flow, after if merging it into the loop
+        StructuredGraph graph = mainLoopBegin.graph();
+        IfNode loopTest = mainCounted.getLimitTest();
+        IfNode newSegmentTest = 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 = mainLoopBegin.getSingleLoopEnd();
+        FixedWithNextNode lastCodeNode = (FixedWithNextNode) loopEndNode.predecessor();
+        FixedNode newSegmentFirstNode = getDuplicatedNode(firstNode);
+        FixedWithNextNode newSegmentLastNode = getDuplicatedNode(lastCodeNode);
+        graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "Before placing segment");
+        if (firstNode instanceof LoopEndNode) {
+            GraphUtil.killCFG(getDuplicatedNode(mainLoopBegin));
+        } else {
+            newSegmentLastNode.clearSuccessors();
+            startBlockNode.setNext(lastNode);
+            lastCodeNode.replaceFirstSuccessor(loopEndNode, newSegmentFirstNode);
+            newSegmentLastNode.replaceFirstSuccessor(lastNode, loopEndNode);
+            lastCodeNode.setNext(newSegmentFirstNode);
+            newSegmentLastNode.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");
+    }
+
+    private static EndNode getBlockEnd(FixedNode node) {
+        FixedNode curNode = node;
+        while (curNode instanceof FixedWithNextNode) {
+            curNode = ((FixedWithNextNode) curNode).next();
+        }
+        return (EndNode) curNode;
     }
 
     @Override
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java	Fri Aug 04 19:59:33 2017 -0700
@@ -300,6 +300,16 @@
         return begin instanceof LoopExitNode && ((LoopExitNode) begin).loopBegin() == this;
     }
 
+    public LoopExitNode getSingleLoopExit() {
+        assert loopExits().count() == 1;
+        return loopExits().first();
+    }
+
+    public LoopEndNode getSingleLoopEnd() {
+        assert loopEnds().count() == 1;
+        return loopEnds().first();
+    }
+
     public void removeExits() {
         for (LoopExitNode loopexit : loopExits().snapshot()) {
             loopexit.removeProxies();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java	Fri Aug 04 19:59:33 2017 -0700
@@ -35,7 +35,7 @@
 import org.graalvm.compiler.nodes.spi.ValueProxy;
 
 @NodeInfo(cycles = CYCLES_0, size = SIZE_0)
-public final class FixedValueAnchorNode extends FixedWithNextNode implements LIRLowerable, ValueProxy, GuardingNode {
+public class FixedValueAnchorNode extends FixedWithNextNode implements LIRLowerable, ValueProxy, GuardingNode {
     public static final NodeClass<FixedValueAnchorNode> TYPE = NodeClass.create(FixedValueAnchorNode.class);
 
     @Input ValueNode object;
@@ -45,9 +45,13 @@
         return object;
     }
 
+    protected FixedValueAnchorNode(NodeClass<? extends FixedValueAnchorNode> c, ValueNode object) {
+        super(c, object.stamp());
+        this.object = object;
+    }
+
     public FixedValueAnchorNode(ValueNode object) {
-        super(TYPE, object.stamp());
-        this.object = object;
+        this(TYPE, object);
     }
 
     public FixedValueAnchorNode(ValueNode object, Stamp predefinedStamp) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawStoreNode.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawStoreNode.java	Fri Aug 04 19:59:33 2017 -0700
@@ -39,6 +39,7 @@
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.nodes.spi.Virtualizable;
 import org.graalvm.compiler.nodes.spi.VirtualizerTool;
+import org.graalvm.compiler.nodes.type.StampTool;
 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
 import org.graalvm.word.LocationIdentity;
 
@@ -124,7 +125,7 @@
                 int entryIndex = virtual.entryIndexForOffset(off, accessKind());
                 if (entryIndex != -1) {
                     JavaKind entryKind = virtual.entryKind(entryIndex);
-                    boolean canVirtualize = entryKind == accessKind() || entryKind == accessKind().getStackKind();
+                    boolean canVirtualize = entryKind == accessKind() || (entryKind == accessKind().getStackKind() && !StampTool.typeOrNull(object()).isArray());
                     if (!canVirtualize) {
                         /*
                          * Special case: If the entryKind is long, allow arbitrary kinds as long as
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/StateSplitProxyNode.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2011, 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.nodes.extended;
+
+import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0;
+import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0;
+
+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.InputType;
+import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.StateSplit;
+import org.graalvm.compiler.nodes.ValueNode;
+
+/**
+ * This node provides a state split along with the functionality of {@link FixedValueAnchorNode}.
+ */
+@NodeInfo(cycles = CYCLES_0, size = SIZE_0)
+public final class StateSplitProxyNode extends FixedValueAnchorNode implements Canonicalizable, StateSplit {
+
+    public static final NodeClass<StateSplitProxyNode> TYPE = NodeClass.create(StateSplitProxyNode.class);
+
+    @OptionalInput(InputType.State) FrameState stateAfter;
+
+    @Override
+    public FrameState stateAfter() {
+        return stateAfter;
+    }
+
+    @Override
+    public void setStateAfter(FrameState x) {
+        assert x == null || x.isAlive() : "frame state must be in a graph";
+        updateUsages(stateAfter, x);
+        stateAfter = x;
+    }
+
+    @Override
+    public boolean hasSideEffect() {
+        return true;
+    }
+
+    public StateSplitProxyNode(ValueNode object) {
+        super(TYPE, object);
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (object.isConstant()) {
+            return object;
+        }
+        return this;
+    }
+
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java	Fri Aug 04 19:59:33 2017 -0700
@@ -1092,7 +1092,7 @@
         static final Class<?>[][] SIGS;
 
         static {
-            if (!Assertions.ENABLED) {
+            if (!Assertions.assertionsEnabled()) {
                 throw new GraalError("%s must only be used in assertions", Checks.class.getName());
             }
             ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY);
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java	Fri Aug 04 19:59:33 2017 -0700
@@ -90,7 +90,7 @@
 
     @Override
     public boolean inferStamp() {
-        return updateStamp(createStamp(graph().getAssumptions(), array(), elementKind()));
+        return updateStamp(stamp.improveWith(createStamp(graph().getAssumptions(), array(), elementKind())));
     }
 
     @Override
@@ -101,7 +101,13 @@
             ValueNode indexValue = tool.getAlias(index());
             int idx = indexValue.isConstant() ? indexValue.asJavaConstant().asInt() : -1;
             if (idx >= 0 && idx < virtual.entryCount()) {
-                tool.replaceWith(tool.getEntry(virtual, idx));
+                ValueNode entry = tool.getEntry(virtual, idx);
+                if (stamp.isCompatible(entry.stamp())) {
+                    tool.replaceWith(entry);
+                } else {
+                    assert stamp().getStackKind() == JavaKind.Int && (entry.stamp().getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double ||
+                                    entry.getStackKind() == JavaKind.Illegal) : "Can only allow different stack kind two slot marker writes on one stot fields.";
+                }
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.test/src/org/graalvm/compiler/options/test/TestOptionKey.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options.test/src/org/graalvm/compiler/options/test/TestOptionKey.java	Fri Aug 04 19:59:33 2017 -0700
@@ -28,10 +28,12 @@
 
 package org.graalvm.compiler.options.test;
 
+import static org.graalvm.compiler.options.OptionValues.asMap;
 import static org.graalvm.compiler.options.test.TestOptionKey.Options.MyOption;
 import static org.graalvm.compiler.options.test.TestOptionKey.Options.MyOtherOption;
 import static org.junit.Assert.assertEquals;
 
+import org.graalvm.compiler.options.ModifiableOptionValues;
 import org.graalvm.compiler.options.OptionDescriptor;
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionValues;
@@ -65,4 +67,20 @@
         }
         Assert.assertTrue(sawAssertionError);
     }
+
+    /**
+     * Tests that initial values are properly copied.
+     */
+    @Test
+    public void testDerived() {
+        OptionValues initialOptions = new ModifiableOptionValues(asMap(MyOption, "new value 1"));
+        OptionValues derivedOptions = new OptionValues(initialOptions, MyOtherOption, "ignore");
+        Assert.assertEquals("new value 1", MyOption.getValue(derivedOptions));
+
+        initialOptions = new OptionValues(asMap(MyOption, "new value 1"));
+        derivedOptions = new OptionValues(initialOptions, MyOtherOption, "ignore");
+        Assert.assertEquals("new value 1", MyOption.getValue(derivedOptions));
+
+    }
+
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/ModifiableOptionValues.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/ModifiableOptionValues.java	Fri Aug 04 19:59:33 2017 -0700
@@ -48,7 +48,16 @@
     }
 
     /**
+     * Value that can be used in {@link #update(UnmodifiableEconomicMap)} and
+     * {@link #update(OptionKey, Object)} to remove an explicitly set value for a key such that
+     * {@link OptionKey#hasBeenSet(OptionValues)} will return {@code false} for the key.
+     */
+    public static final Object UNSET_KEY = new Object();
+
+    /**
      * Updates this object with the given key/value pair.
+     *
+     * @see #UNSET_KEY
      */
     public void update(OptionKey<?> key, Object value) {
         UnmodifiableEconomicMap<OptionKey<?>, Object> expect;
@@ -56,14 +65,20 @@
         do {
             expect = v.get();
             newMap = EconomicMap.create(Equivalence.IDENTITY, expect);
-            key.update(newMap, value);
-            // Need to do the null encoding here as `key.update()` doesn't do it
-            newMap.put(key, encodeNull(value));
+            if (value == UNSET_KEY) {
+                newMap.removeKey(key);
+            } else {
+                key.update(newMap, value);
+                // Need to do the null encoding here as `key.update()` doesn't do it
+                newMap.put(key, encodeNull(value));
+            }
         } while (!v.compareAndSet(expect, newMap));
     }
 
     /**
      * Updates this object with the key/value pairs in {@code values}.
+     *
+     * @see #UNSET_KEY
      */
     public void update(UnmodifiableEconomicMap<OptionKey<?>, Object> values) {
         if (values.isEmpty()) {
@@ -78,9 +93,13 @@
             while (cursor.advance()) {
                 OptionKey<?> key = cursor.getKey();
                 Object value = cursor.getValue();
-                key.update(newMap, value);
-                // Need to do the null encoding here as `key.update()` doesn't do it
-                newMap.put(key, encodeNull(value));
+                if (value == UNSET_KEY) {
+                    newMap.removeKey(key);
+                } else {
+                    key.update(newMap, value);
+                    // Need to do the null encoding here as `key.update()` doesn't do it
+                    newMap.put(key, encodeNull(value));
+                }
             }
         } while (!v.compareAndSet(expect, newMap));
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionValues.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionValues.java	Fri Aug 04 19:59:33 2017 -0700
@@ -52,7 +52,7 @@
     public OptionValues(OptionValues initialValues, UnmodifiableEconomicMap<OptionKey<?>, Object> extraPairs) {
         EconomicMap<OptionKey<?>, Object> map = newOptionMap();
         if (initialValues != null) {
-            map.putAll(initialValues.values);
+            map.putAll(initialValues.getMap());
         }
         initMap(map, extraPairs);
         this.values = map;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java	Fri Aug 04 19:59:33 2017 -0700
@@ -101,15 +101,12 @@
     private static void processIf(LogicNode x, boolean xNegated, LogicNode y, boolean yNegated, IfNode ifNode, double shortCircuitProbability) {
         AbstractBeginNode trueTarget = ifNode.trueSuccessor();
         AbstractBeginNode falseTarget = ifNode.falseSuccessor();
-        double firstIfProbability = shortCircuitProbability;
-        /*
-         * P(Y | not(X)) = P(Y inter not(X)) / P(not(X)) = (P(X union Y) - P(X)) / (1 - P(X))
-         *
-         * P(X) = shortCircuitProbability
-         *
-         * P(X union Y) = ifNode.probability(trueTarget)
-         */
-        double secondIfProbability = (ifNode.probability(trueTarget) - shortCircuitProbability) / (1 - shortCircuitProbability);
+        // while the first if node is reached by all cases, the true values are split between the
+        // first and the second if
+        double firstIfProbability = ifNode.probability(trueTarget) * shortCircuitProbability;
+        // the second if node is reached by a reduced number of true cases but the same number of
+        // false cases
+        double secondIfProbability = 1 - ifNode.probability(falseTarget) / (1 - firstIfProbability);
         secondIfProbability = Math.min(1.0, Math.max(0.0, secondIfProbability));
         if (Double.isNaN(secondIfProbability)) {
             secondIfProbability = 0.5;
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/UseTrappingNullChecksPhase.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -35,6 +35,7 @@
 import org.graalvm.compiler.nodes.AbstractEndNode;
 import org.graalvm.compiler.nodes.AbstractMergeNode;
 import org.graalvm.compiler.nodes.BeginNode;
+import org.graalvm.compiler.nodes.CompressionNode;
 import org.graalvm.compiler.nodes.DeoptimizeNode;
 import org.graalvm.compiler.nodes.DeoptimizingFixedWithNextNode;
 import org.graalvm.compiler.nodes.DynamicDeoptimizeNode;
@@ -197,6 +198,14 @@
                     AddressNode address = fixedAccessNode.getAddress();
                     ValueNode base = address.getBase();
                     ValueNode index = address.getIndex();
+                    // allow for architectures which cannot fold an
+                    // intervening uncompress out of the address chain
+                    if (base != null && base instanceof CompressionNode) {
+                        base = ((CompressionNode) base).getValue();
+                    }
+                    if (index != null && index instanceof CompressionNode) {
+                        index = ((CompressionNode) index).getValue();
+                    }
                     if (((base == value && index == null) || (base == null && index == value)) && address.getMaxConstantDisplacement() < implicitNullCheckLimit) {
                         // Opportunity for implicit null check as part of an existing read found!
                         fixedAccessNode.setStateBefore(deopt.stateBefore());
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/BasePhase.java	Fri Aug 04 19:59:33 2017 -0700
@@ -86,23 +86,23 @@
 
     public static class BasePhaseStatistics {
         /**
-         * Records time spent in {@link #apply(StructuredGraph, Object, boolean)}.
+         * Records time spent in {@link BasePhase#apply(StructuredGraph, Object, boolean)}.
          */
         private final TimerKey timer;
 
         /**
-         * Counts calls to {@link #apply(StructuredGraph, Object, boolean)}.
+         * Counts calls to {@link BasePhase#apply(StructuredGraph, Object, boolean)}.
          */
         private final CounterKey executionCount;
 
         /**
          * Accumulates the {@linkplain Graph#getNodeCount() live node count} of all graphs sent to
-         * {@link #apply(StructuredGraph, Object, boolean)}.
+         * {@link BasePhase#apply(StructuredGraph, Object, boolean)}.
          */
         private final CounterKey inputNodesCount;
 
         /**
-         * Records memory usage within {@link #apply(StructuredGraph, Object, boolean)}.
+         * Records memory usage within {@link BasePhase#apply(StructuredGraph, Object, boolean)}.
          */
         private final MemUseTrackerKey memUseTracker;
 
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java	Fri Aug 04 19:59:33 2017 -0700
@@ -30,7 +30,6 @@
 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;
@@ -107,7 +106,7 @@
     }
 
     private NodeEventScope verifyImmutableGraph(StructuredGraph graph) {
-        if (immutableGraph && Assertions.ENABLED) {
+        if (immutableGraph && Assertions.assertionsEnabled()) {
             return graph.trackNodeEvents(new NodeEventListener() {
                 @Override
                 public void event(NodeEvent e, Node node) {
@@ -178,7 +177,7 @@
                 sortNodesLatestWithinBlock(cfg, earliestBlockToNodesMap, latestBlockToNodesMap, currentNodeMap, watchListMap, visited);
 
                 assert verifySchedule(cfg, latestBlockToNodesMap, currentNodeMap);
-                assert (!GraalOptions.DetailedAsserts.getValue(graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), latestBlockToNodesMap);
+                assert (!Assertions.detailedAssertionsEnabled(graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), latestBlockToNodesMap);
 
                 this.blockToNodesMap = latestBlockToNodesMap;
 
@@ -880,7 +879,7 @@
                 }
             }
 
-            assert (!GraalOptions.DetailedAsserts.getValue(cfg.graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), blockToNodes);
+            assert (!Assertions.detailedAssertionsEnabled(cfg.graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), blockToNodes);
         }
 
         private static void processStackPhi(NodeStack stack, PhiNode phiNode, NodeMap<MicroBlock> nodeToBlock, NodeBitMap visited) {
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java	Fri Aug 04 19:59:33 2017 -0700
@@ -26,21 +26,18 @@
 import static org.graalvm.compiler.graph.Edges.Type.Successors;
 
 import java.io.IOException;
-import java.nio.ByteBuffer;
 import java.nio.channels.WritableByteChannel;
-import java.nio.charset.Charset;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
+import jdk.vm.ci.meta.ResolvedJavaField;
 
 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.DebugContext;
 import org.graalvm.compiler.debug.DebugOptions;
 import org.graalvm.compiler.graph.CachedGraph;
 import org.graalvm.compiler.graph.Edges;
@@ -48,7 +45,6 @@
 import org.graalvm.compiler.graph.InputEdges;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
-import org.graalvm.compiler.graph.NodeList;
 import org.graalvm.compiler.graph.NodeMap;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.AbstractEndNode;
@@ -59,107 +55,33 @@
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
-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 jdk.vm.ci.meta.JavaType;
-import jdk.vm.ci.meta.ResolvedJavaField;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
 import jdk.vm.ci.meta.Signature;
+import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.NodeSourcePosition;
-
-public class BinaryGraphPrinter implements GraphPrinter {
-
-    private static final int CONSTANT_POOL_MAX_SIZE = 8000;
-
-    private static final int BEGIN_GROUP = 0x00;
-    private static final int BEGIN_GRAPH = 0x01;
-    private static final int CLOSE_GROUP = 0x02;
-
-    private static final int POOL_NEW = 0x00;
-    private static final int POOL_STRING = 0x01;
-    private static final int POOL_ENUM = 0x02;
-    private static final int POOL_CLASS = 0x03;
-    private static final int POOL_METHOD = 0x04;
-    private static final int POOL_NULL = 0x05;
-    private static final int POOL_NODE_CLASS = 0x06;
-    private static final int POOL_FIELD = 0x07;
-    private static final int POOL_SIGNATURE = 0x08;
-    private static final int POOL_NODE_SOURCE_POSITION = 0x09;
-
-    private static final int PROPERTY_POOL = 0x00;
-    private static final int PROPERTY_INT = 0x01;
-    private static final int PROPERTY_LONG = 0x02;
-    private static final int PROPERTY_DOUBLE = 0x03;
-    private static final int PROPERTY_FLOAT = 0x04;
-    private static final int PROPERTY_TRUE = 0x05;
-    private static final int PROPERTY_FALSE = 0x06;
-    private static final int PROPERTY_ARRAY = 0x07;
-    private static final int PROPERTY_SUBGRAPH = 0x08;
-
-    private static final int KLASS = 0x00;
-    private static final int ENUM_KLASS = 0x01;
-
-    static final int CURRENT_MAJOR_VERSION = 4;
-    static final int CURRENT_MINOR_VERSION = 0;
-
-    static final byte[] MAGIC_BYTES = {'B', 'I', 'G', 'V'};
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.phases.schedule.SchedulePhase;
+import org.graalvm.graphio.GraphBlocks;
+import org.graalvm.graphio.GraphElements;
+import org.graalvm.graphio.GraphOutput;
+import org.graalvm.graphio.GraphStructure;
+import org.graalvm.graphio.GraphTypes;
 
-    private void writeVersion() throws IOException {
-        writeBytesRaw(MAGIC_BYTES);
-        writeByte(CURRENT_MAJOR_VERSION);
-        writeByte(CURRENT_MINOR_VERSION);
-    }
-
-    private static final class ConstantPool extends LinkedHashMap<Object, Character> {
-
-        private final LinkedList<Character> availableIds;
-        private char nextId;
-        private static final long serialVersionUID = -2676889957907285681L;
-
-        ConstantPool() {
-            super(50, 0.65f);
-            availableIds = new LinkedList<>();
-        }
-
-        @Override
-        protected boolean removeEldestEntry(java.util.Map.Entry<Object, Character> eldest) {
-            if (size() > CONSTANT_POOL_MAX_SIZE) {
-                availableIds.addFirst(eldest.getValue());
-                return true;
-            }
-            return false;
-        }
-
-        private Character nextAvailableId() {
-            if (!availableIds.isEmpty()) {
-                return availableIds.removeFirst();
-            }
-            return nextId++;
-        }
-
-        public char add(Object obj) {
-            Character id = nextAvailableId();
-            put(obj, id);
-            return id;
-        }
-    }
-
-    private final ConstantPool constantPool;
-    private final ByteBuffer buffer;
-    private final WritableByteChannel channel;
+public class BinaryGraphPrinter implements
+                GraphStructure<BinaryGraphPrinter.GraphInfo, Node, NodeClass<?>, Edges>,
+                GraphBlocks<BinaryGraphPrinter.GraphInfo, Block, Node>,
+                GraphElements<ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition>,
+                GraphTypes, GraphPrinter {
     private final SnippetReflectionProvider snippetReflection;
-
-    private static final Charset utf8 = Charset.forName("UTF-8");
+    private final GraphOutput<BinaryGraphPrinter.GraphInfo, ResolvedJavaMethod> output;
 
     public BinaryGraphPrinter(WritableByteChannel channel, SnippetReflectionProvider snippetReflection) throws IOException {
-        constantPool = new ConstantPool();
+        this.output = GraphOutput.newBuilder(this).blocks(this).elements(this).types(this).build(channel);
         this.snippetReflection = snippetReflection;
-        buffer = ByteBuffer.allocateDirect(256 * 1024);
-        this.channel = channel;
-        writeVersion();
     }
 
     @Override
@@ -167,339 +89,174 @@
         return snippetReflection;
     }
 
-    @SuppressWarnings("all")
     @Override
-    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(debug, a);
-            }
-        } else {
-            writePoolObject(id + ": " + String.format(format, simplifyClassArgs(args)));
-        }
-        writeGraph(debug, graph, properties);
-        flush();
-    }
-
-    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(debug, properties);
-        writeNodes(debug, graph, nodeToBlocks, cfg);
-        writeBlocks(blocks, blockToNodes);
+    public void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException {
+        output.beginGroup(new GraphInfo(debug, null), name, shortName, method, bci, properties);
     }
 
-    private void flush() throws IOException {
-        buffer.flip();
-        /*
-         * 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();
-        try {
-            channel.write(buffer);
-        } finally {
-            if (interrupted) {
-                Thread.currentThread().interrupt();
-            }
-        }
-        buffer.compact();
-    }
-
-    private void ensureAvailable(int i) throws IOException {
-        assert buffer.capacity() >= i : "Can not make " + i + " bytes available, buffer is too small";
-        while (buffer.remaining() < i) {
-            flush();
-        }
-    }
-
-    private void writeByte(int b) throws IOException {
-        ensureAvailable(1);
-        buffer.put((byte) b);
-    }
-
-    private void writeInt(int b) throws IOException {
-        ensureAvailable(4);
-        buffer.putInt(b);
-    }
-
-    private void writeLong(long b) throws IOException {
-        ensureAvailable(8);
-        buffer.putLong(b);
-    }
-
-    private void writeDouble(double b) throws IOException {
-        ensureAvailable(8);
-        buffer.putDouble(b);
-    }
-
-    private void writeFloat(float b) throws IOException {
-        ensureAvailable(4);
-        buffer.putFloat(b);
+    @Override
+    public void endGroup() throws IOException {
+        output.endGroup();
     }
 
-    private void writeShort(char b) throws IOException {
-        ensureAvailable(2);
-        buffer.putChar(b);
-    }
-
-    private void writeString(String str) throws IOException {
-        byte[] bytes = str.getBytes(utf8);
-        writeBytes(bytes);
-    }
-
-    private void writeBytes(byte[] b) throws IOException {
-        if (b == null) {
-            writeInt(-1);
-        } else {
-            writeInt(b.length);
-            writeBytesRaw(b);
-        }
-    }
-
-    private void writeBytesRaw(byte[] b) throws IOException {
-        int bytesWritten = 0;
-        while (bytesWritten < b.length) {
-            int toWrite = Math.min(b.length - bytesWritten, buffer.capacity());
-            ensureAvailable(toWrite);
-            buffer.put(b, bytesWritten, toWrite);
-            bytesWritten += toWrite;
-        }
+    @Override
+    public void close() {
+        output.close();
     }
 
-    private void writeInts(int[] b) throws IOException {
-        if (b == null) {
-            writeInt(-1);
-        } else {
-            writeInt(b.length);
-            int sizeInBytes = b.length * 4;
-            ensureAvailable(sizeInBytes);
-            buffer.asIntBuffer().put(b);
-            buffer.position(buffer.position() + sizeInBytes);
-        }
-    }
-
-    private void writeDoubles(double[] b) throws IOException {
-        if (b == null) {
-            writeInt(-1);
+    @Override
+    public ResolvedJavaMethod method(Object object) {
+        if (object instanceof Bytecode) {
+            return ((Bytecode) object).getMethod();
+        } else if (object instanceof ResolvedJavaMethod) {
+            return ((ResolvedJavaMethod) object);
         } else {
-            writeInt(b.length);
-            int sizeInBytes = b.length * 8;
-            ensureAvailable(sizeInBytes);
-            buffer.asDoubleBuffer().put(b);
-            buffer.position(buffer.position() + sizeInBytes);
-        }
-    }
-
-    private void writePoolObject(Object object) throws IOException {
-        if (object == null) {
-            writeByte(POOL_NULL);
-            return;
-        }
-        Character id = constantPool.get(object);
-        if (id == null) {
-            addPoolEntry(object);
-        } else {
-            if (object instanceof Enum<?>) {
-                writeByte(POOL_ENUM);
-            } else if (object instanceof Class<?> || object instanceof JavaType) {
-                writeByte(POOL_CLASS);
-            } else if (object instanceof NodeClass) {
-                writeByte(POOL_NODE_CLASS);
-            } else if (object instanceof ResolvedJavaMethod || object instanceof Bytecode) {
-                writeByte(POOL_METHOD);
-            } else if (object instanceof ResolvedJavaField) {
-                writeByte(POOL_FIELD);
-            } else if (object instanceof Signature) {
-                writeByte(POOL_SIGNATURE);
-            } else if (CURRENT_MAJOR_VERSION >= 4 && object instanceof NodeSourcePosition) {
-                writeByte(POOL_NODE_SOURCE_POSITION);
-            } else {
-                writeByte(POOL_STRING);
-            }
-            writeShort(id.charValue());
+            return null;
         }
     }
 
-    private static String getClassName(Class<?> klass) {
-        if (!klass.isArray()) {
-            return klass.getName();
+    @Override
+    public NodeClass<?> nodeClass(Object obj) {
+        if (obj instanceof NodeClass<?>) {
+            return (NodeClass<?>) obj;
         }
-        return getClassName(klass.getComponentType()) + "[]";
+        if (obj instanceof Node) {
+            return ((Node) obj).getNodeClass();
+        }
+        return null;
     }
 
-    @SuppressWarnings("all")
-    private void addPoolEntry(Object object) throws IOException {
-        char index = constantPool.add(object);
-        writeByte(POOL_NEW);
-        writeShort(index);
-        if (object instanceof Class<?>) {
-            Class<?> klass = (Class<?>) object;
-            writeByte(POOL_CLASS);
-            writeString(getClassName(klass));
-            if (klass.isEnum()) {
-                writeByte(ENUM_KLASS);
-                Object[] enumConstants = klass.getEnumConstants();
-                writeInt(enumConstants.length);
-                for (Object o : enumConstants) {
-                    writePoolObject(((Enum<?>) o).name());
-                }
-            } else {
-                writeByte(KLASS);
-            }
-        } else if (object instanceof Enum<?>) {
-            writeByte(POOL_ENUM);
-            writePoolObject(object.getClass());
-            writeInt(((Enum<?>) object).ordinal());
-        } else if (object instanceof JavaType) {
-            JavaType type = (JavaType) object;
-            writeByte(POOL_CLASS);
-            writeString(type.toJavaName());
-            writeByte(KLASS);
-        } else if (object instanceof NodeClass) {
-            NodeClass<?> nodeClass = (NodeClass<?>) object;
-            writeByte(POOL_NODE_CLASS);
-            if (CURRENT_MAJOR_VERSION >= 3) {
-                writePoolObject(nodeClass.getJavaClass());
-                writeString(nodeClass.getNameTemplate());
-            } else {
-                writeString(nodeClass.getJavaClass().getSimpleName());
-                String nameTemplate = nodeClass.getNameTemplate();
-                writeString(nameTemplate.isEmpty() ? nodeClass.shortName() : nameTemplate);
-            }
-            writeEdgesInfo(nodeClass, Inputs);
-            writeEdgesInfo(nodeClass, Successors);
-        } else if (object instanceof ResolvedJavaMethod || object instanceof Bytecode) {
-            writeByte(POOL_METHOD);
-            ResolvedJavaMethod method;
-            if (object instanceof Bytecode) {
-                method = ((Bytecode) object).getMethod();
-            } else {
-                method = ((ResolvedJavaMethod) object);
-            }
-            writePoolObject(method.getDeclaringClass());
-            writePoolObject(method.getName());
-            writePoolObject(method.getSignature());
-            writeInt(method.getModifiers());
-            writeBytes(method.getCode());
-        } else if (object instanceof ResolvedJavaField) {
-            writeByte(POOL_FIELD);
-            ResolvedJavaField field = ((ResolvedJavaField) object);
-            writePoolObject(field.getDeclaringClass());
-            writePoolObject(field.getName());
-            writePoolObject(field.getType().getName());
-            writeInt(field.getModifiers());
-        } else if (object instanceof Signature) {
-            writeByte(POOL_SIGNATURE);
-            Signature signature = ((Signature) object);
-            int args = signature.getParameterCount(false);
-            writeShort((char) args);
-            for (int i = 0; i < args; i++) {
-                writePoolObject(signature.getParameterType(i, null).getName());
-            }
-            writePoolObject(signature.getReturnType(null).getName());
-        } else if (CURRENT_MAJOR_VERSION >= 4 && object instanceof NodeSourcePosition) {
-            writeByte(POOL_NODE_SOURCE_POSITION);
-            NodeSourcePosition pos = (NodeSourcePosition) object;
-            ResolvedJavaMethod method = pos.getMethod();
-            writePoolObject(method);
-            final int bci = pos.getBCI();
-            writeInt(bci);
-            StackTraceElement ste = method.asStackTraceElement(bci);
-            if (ste != null) {
-                String fn = ste.getFileName();
-                writePoolObject(fn);
-                if (fn != null) {
-                    writeInt(ste.getLineNumber());
-                }
-            } else {
-                writePoolObject(null);
-            }
-            writePoolObject(pos.getCaller());
+    @Override
+    public Object nodeClassType(NodeClass<?> node) {
+        return node.getJavaClass();
+    }
+
+    @Override
+    public String nameTemplate(NodeClass<?> nodeClass) {
+        return nodeClass.getNameTemplate();
+    }
+
+    @Override
+    public final GraphInfo graph(GraphInfo currrent, Object obj) {
+        if (obj instanceof Graph) {
+            return new GraphInfo(currrent.debug, (Graph) obj);
+        } else if (obj instanceof CachedGraph) {
+            return new GraphInfo(currrent.debug, ((CachedGraph<?>) obj).getReadonlyCopy());
         } else {
-            writeByte(POOL_STRING);
-            writeString(object.toString());
+            return null;
         }
     }
 
-    private void writeEdgesInfo(NodeClass<?> nodeClass, Edges.Type type) throws IOException {
-        Edges edges = nodeClass.getEdges(type);
-        writeShort((char) edges.getCount());
-        for (int i = 0; i < edges.getCount(); i++) {
-            writeByte(i < edges.getDirectCount() ? 0 : 1);
-            writePoolObject(edges.getName(i));
-            if (type == Inputs) {
-                writePoolObject(((InputEdges) edges).getInputType(i));
-            }
-        }
+    @Override
+    public int nodeId(Node n) {
+        return getNodeId(n);
     }
 
-    private void writePropertyObject(DebugContext debug, Object obj) throws IOException {
-        if (obj instanceof Integer) {
-            writeByte(PROPERTY_INT);
-            writeInt(((Integer) obj).intValue());
-        } else if (obj instanceof Long) {
-            writeByte(PROPERTY_LONG);
-            writeLong(((Long) obj).longValue());
-        } else if (obj instanceof Double) {
-            writeByte(PROPERTY_DOUBLE);
-            writeDouble(((Double) obj).doubleValue());
-        } else if (obj instanceof Float) {
-            writeByte(PROPERTY_FLOAT);
-            writeFloat(((Float) obj).floatValue());
-        } else if (obj instanceof Boolean) {
-            if (((Boolean) obj).booleanValue()) {
-                writeByte(PROPERTY_TRUE);
-            } else {
-                writeByte(PROPERTY_FALSE);
-            }
-        } else if (obj instanceof Graph) {
-            writeByte(PROPERTY_SUBGRAPH);
-            writeGraph(debug, (Graph) obj, null);
-        } else if (obj instanceof CachedGraph) {
-            writeByte(PROPERTY_SUBGRAPH);
-            writeGraph(debug, ((CachedGraph<?>) obj).getReadonlyCopy(), null);
-        } else if (obj != null && obj.getClass().isArray()) {
-            Class<?> componentType = obj.getClass().getComponentType();
-            if (componentType.isPrimitive()) {
-                if (componentType == Double.TYPE) {
-                    writeByte(PROPERTY_ARRAY);
-                    writeByte(PROPERTY_DOUBLE);
-                    writeDoubles((double[]) obj);
-                } else if (componentType == Integer.TYPE) {
-                    writeByte(PROPERTY_ARRAY);
-                    writeByte(PROPERTY_INT);
-                    writeInts((int[]) obj);
-                } else {
-                    writeByte(PROPERTY_POOL);
-                    writePoolObject(obj);
-                }
-            } else {
-                writeByte(PROPERTY_ARRAY);
-                writeByte(PROPERTY_POOL);
-                Object[] array = (Object[]) obj;
-                writeInt(array.length);
-                for (Object o : array) {
-                    writePoolObject(o);
-                }
-            }
-        } else {
-            writeByte(PROPERTY_POOL);
-            writePoolObject(obj);
-        }
+    @Override
+    public Edges portInputs(NodeClass<?> nodeClass) {
+        return nodeClass.getEdges(Inputs);
+    }
+
+    @Override
+    public Edges portOutputs(NodeClass<?> nodeClass) {
+        return nodeClass.getEdges(Successors);
     }
 
     @SuppressWarnings("deprecation")
     private static int getNodeId(Node node) {
-        return node.getId();
+        return node == null ? -1 : node.getId();
+    }
+
+    @Override
+    public List<Node> blockNodes(GraphInfo info, Block block) {
+        List<Node> nodes = info.blockToNodes.get(block);
+        if (nodes == null) {
+            return null;
+        }
+        List<Node> extraNodes = new LinkedList<>();
+        for (Node node : nodes) {
+            findExtraNodes(node, extraNodes);
+        }
+        extraNodes.removeAll(nodes);
+        extraNodes.addAll(0, nodes);
+        return extraNodes;
+    }
+
+    @Override
+    public int blockId(Block sux) {
+        return sux.getId();
+    }
+
+    @Override
+    public List<Block> blockSuccessors(Block block) {
+        return Arrays.asList(block.getSuccessors());
+    }
+
+    @Override
+    public Iterable<Node> nodes(GraphInfo info) {
+        return info.graph.getNodes();
+    }
+
+    @Override
+    public int nodesCount(GraphInfo info) {
+        return info.graph.getNodeCount();
+    }
+
+    @Override
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public void nodeProperties(GraphInfo info, Node node, Map<String, Object> props) {
+        node.getDebugProperties((Map) props);
+        Graph graph = info.graph;
+        ControlFlowGraph cfg = info.cfg;
+        NodeMap<Block> nodeToBlocks = info.nodeToBlocks;
+        if (cfg != null && DebugOptions.PrintGraphProbabilities.getValue(graph.getOptions()) && node instanceof FixedNode) {
+            try {
+                props.put("probability", cfg.blockFor(node).probability());
+            } catch (Throwable t) {
+                props.put("probability", 0.0);
+                props.put("probability-exception", t);
+            }
+        }
+
+        try {
+            props.put("NodeCost-Size", node.estimatedNodeSize());
+            props.put("NodeCost-Cycles", node.estimatedNodeCycles());
+        } catch (Throwable t) {
+            props.put("node-cost-exception", t.getMessage());
+        }
+
+        if (nodeToBlocks != null) {
+            Object block = getBlockForNode(node, nodeToBlocks);
+            if (block != null) {
+                props.put("node-to-block", block);
+            }
+        }
+
+        if (node instanceof ControlSinkNode) {
+            props.put("category", "controlSink");
+        } else if (node instanceof ControlSplitNode) {
+            props.put("category", "controlSplit");
+        } else if (node instanceof AbstractMergeNode) {
+            props.put("category", "merge");
+        } else if (node instanceof AbstractBeginNode) {
+            props.put("category", "begin");
+        } else if (node instanceof AbstractEndNode) {
+            props.put("category", "end");
+        } else if (node instanceof FixedNode) {
+            props.put("category", "fixed");
+        } else if (node instanceof VirtualState) {
+            props.put("category", "state");
+        } else if (node instanceof PhiNode) {
+            props.put("category", "phi");
+        } else if (node instanceof ProxyNode) {
+            props.put("category", "proxy");
+        } else {
+            if (node instanceof ConstantNode) {
+                ConstantNode cn = (ConstantNode) node;
+                updateStringPropertiesForConstant((Map) props, cn);
+            }
+            props.put("category", "floating");
+        }
     }
 
     private Object getBlockForNode(Node node, NodeMap<Block> nodeToBlocks) {
@@ -516,181 +273,243 @@
         return null;
     }
 
-    private void writeNodes(DebugContext debug, Graph graph, NodeMap<Block> nodeToBlocks, ControlFlowGraph cfg) throws IOException {
-        Map<Object, Object> props = new HashMap<>();
-
-        writeInt(graph.getNodeCount());
-
-        for (Node node : graph.getNodes()) {
-            NodeClass<?> nodeClass = node.getNodeClass();
-            node.getDebugProperties(props);
-            if (cfg != null && DebugOptions.PrintGraphProbabilities.getValue(graph.getOptions()) && node instanceof FixedNode) {
-                try {
-                    props.put("probability", cfg.blockFor(node).probability());
-                } catch (Throwable t) {
-                    props.put("probability", 0.0);
-                    props.put("probability-exception", t);
-                }
-            }
-
-            try {
-                props.put("NodeCost-Size", node.estimatedNodeSize());
-                props.put("NodeCost-Cycles", node.estimatedNodeCycles());
-            } catch (Throwable t) {
-                props.put("node-cost-exception", t.getMessage());
-            }
-
-            if (nodeToBlocks != null) {
-                Object block = getBlockForNode(node, nodeToBlocks);
-                if (block != null) {
-                    props.put("node-to-block", block);
-                }
-            }
-
-            if (node instanceof ControlSinkNode) {
-                props.put("category", "controlSink");
-            } else if (node instanceof ControlSplitNode) {
-                props.put("category", "controlSplit");
-            } else if (node instanceof AbstractMergeNode) {
-                props.put("category", "merge");
-            } else if (node instanceof AbstractBeginNode) {
-                props.put("category", "begin");
-            } else if (node instanceof AbstractEndNode) {
-                props.put("category", "end");
-            } else if (node instanceof FixedNode) {
-                props.put("category", "fixed");
-            } else if (node instanceof VirtualState) {
-                props.put("category", "state");
-            } else if (node instanceof PhiNode) {
-                props.put("category", "phi");
-            } else if (node instanceof ProxyNode) {
-                props.put("category", "proxy");
-            } else {
-                if (node instanceof ConstantNode) {
-                    ConstantNode cn = (ConstantNode) node;
-                    updateStringPropertiesForConstant(props, cn);
-                }
-                props.put("category", "floating");
-            }
-
-            writeInt(getNodeId(node));
-            writePoolObject(nodeClass);
-            writeByte(node.predecessor() == null ? 0 : 1);
-            writeProperties(debug, props);
-            writeEdges(node, Inputs);
-            writeEdges(node, Successors);
-
-            props.clear();
-        }
-    }
-
-    private void writeProperties(DebugContext debug, Map<Object, Object> props) throws IOException {
-        if (props == null) {
-            writeShort((char) 0);
-            return;
-        }
-        // properties
-        writeShort((char) props.size());
-        for (Entry<Object, Object> entry : props.entrySet()) {
-            String key = entry.getKey().toString();
-            writePoolObject(key);
-            writePropertyObject(debug, entry.getValue());
-        }
-    }
-
-    private void writeEdges(Node node, Edges.Type type) throws IOException {
-        NodeClass<?> nodeClass = node.getNodeClass();
-        Edges edges = nodeClass.getEdges(type);
-        final long[] curOffsets = edges.getOffsets();
-        for (int i = 0; i < edges.getDirectCount(); i++) {
-            writeNodeRef(Edges.getNode(node, curOffsets, i));
-        }
-        for (int i = edges.getDirectCount(); i < edges.getCount(); i++) {
-            NodeList<Node> list = Edges.getNodeList(node, curOffsets, i);
-            if (list == null) {
-                writeShort((char) 0);
-            } else {
-                int listSize = list.count();
-                assert listSize == ((char) listSize);
-                writeShort((char) listSize);
-                for (Node edge : list) {
-                    writeNodeRef(edge);
-                }
+    private static void findExtraNodes(Node node, Collection<? super Node> extraNodes) {
+        if (node instanceof AbstractMergeNode) {
+            AbstractMergeNode merge = (AbstractMergeNode) node;
+            for (PhiNode phi : merge.phis()) {
+                extraNodes.add(phi);
             }
         }
     }
 
-    private void writeNodeRef(Node edge) throws IOException {
-        if (edge != null) {
-            writeInt(getNodeId(edge));
-        } else {
-            writeInt(-1);
-        }
+    @Override
+    public boolean nodeHasPredecessor(Node node) {
+        return node.predecessor() != null;
+    }
+
+    @Override
+    public List<Block> blocks(GraphInfo graph) {
+        return graph.blocks;
+    }
+
+    @Override
+    public void print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException {
+        output.print(new GraphInfo(debug, graph), properties, id, format, args);
+    }
+
+    @Override
+    public int portSize(Edges port) {
+        return port.getCount();
     }
 
-    private void writeBlocks(List<Block> blocks, BlockMap<List<Node>> blockToNodes) throws IOException {
-        if (blocks != null && blockToNodes != null) {
-            for (Block block : blocks) {
-                List<Node> nodes = blockToNodes.get(block);
-                if (nodes == null) {
-                    writeInt(0);
-                    return;
-                }
-            }
-            writeInt(blocks.size());
-            for (Block block : blocks) {
-                List<Node> nodes = blockToNodes.get(block);
-                List<Node> extraNodes = new LinkedList<>();
-                writeInt(block.getId());
-                for (Node node : nodes) {
-                    if (node instanceof AbstractMergeNode) {
-                        AbstractMergeNode merge = (AbstractMergeNode) node;
-                        for (PhiNode phi : merge.phis()) {
-                            if (!nodes.contains(phi)) {
-                                extraNodes.add(phi);
-                            }
-                        }
-                    }
-                }
-                writeInt(nodes.size() + extraNodes.size());
-                for (Node node : nodes) {
-                    writeInt(getNodeId(node));
-                }
-                for (Node node : extraNodes) {
-                    writeInt(getNodeId(node));
-                }
-                writeInt(block.getSuccessors().length);
-                for (Block sux : block.getSuccessors()) {
-                    writeInt(sux.getId());
-                }
-            }
+    @Override
+    public boolean edgeDirect(Edges port, int index) {
+        return index < port.getDirectCount();
+    }
+
+    @Override
+    public String edgeName(Edges port, int index) {
+        return port.getName(index);
+    }
+
+    @Override
+    public Object edgeType(Edges port, int index) {
+        return ((InputEdges) port).getInputType(index);
+    }
+
+    @Override
+    public Collection<? extends Node> edgeNodes(GraphInfo graph, Node node, Edges port, int i) {
+        if (i < port.getDirectCount()) {
+            Node single = Edges.getNode(node, port.getOffsets(), i);
+            return Collections.singletonList(single);
         } else {
-            writeInt(0);
+            return Edges.getNodeList(node, port.getOffsets(), i);
         }
     }
 
     @Override
-    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(debug, properties);
+    public Object enumClass(Object enumValue) {
+        if (enumValue instanceof Enum) {
+            return enumValue.getClass();
+        }
+        return null;
+    }
+
+    @Override
+    public int enumOrdinal(Object obj) {
+        if (obj instanceof Enum<?>) {
+            return ((Enum<?>) obj).ordinal();
+        }
+        return -1;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public String[] enumTypeValues(Object clazz) {
+        if (clazz instanceof Class<?>) {
+            Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) clazz;
+            Enum<?>[] constants = enumClass.getEnumConstants();
+            if (constants != null) {
+                String[] names = new String[constants.length];
+                for (int i = 0; i < constants.length; i++) {
+                    names[i] = constants[i].name();
+                }
+                return names;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String typeName(Object obj) {
+        if (obj instanceof Class<?>) {
+            return ((Class<?>) obj).getName();
+        }
+        if (obj instanceof ResolvedJavaType) {
+            return ((ResolvedJavaType) obj).getName();
+        }
+        return null;
+    }
+
+    @Override
+    public byte[] methodCode(ResolvedJavaMethod method) {
+        return method.getCode();
+    }
+
+    @Override
+    public int methodModifiers(ResolvedJavaMethod method) {
+        return method.getModifiers();
+    }
+
+    @Override
+    public Signature methodSignature(ResolvedJavaMethod method) {
+        return method.getSignature();
+    }
+
+    @Override
+    public String methodName(ResolvedJavaMethod method) {
+        return method.getName();
+    }
+
+    @Override
+    public Object methodDeclaringClass(ResolvedJavaMethod method) {
+        return method.getDeclaringClass();
+    }
+
+    @Override
+    public int fieldModifiers(ResolvedJavaField field) {
+        return field.getModifiers();
+    }
+
+    @Override
+    public String fieldTypeName(ResolvedJavaField field) {
+        return field.getType().getName();
+    }
+
+    @Override
+    public String fieldName(ResolvedJavaField field) {
+        return field.getName();
+    }
+
+    @Override
+    public Object fieldDeclaringClass(ResolvedJavaField field) {
+        return field.getDeclaringClass();
     }
 
     @Override
-    public void endGroup() throws IOException {
-        writeByte(CLOSE_GROUP);
+    public ResolvedJavaField field(Object object) {
+        if (object instanceof ResolvedJavaField) {
+            return (ResolvedJavaField) object;
+        }
+        return null;
+    }
+
+    @Override
+    public Signature signature(Object object) {
+        if (object instanceof Signature) {
+            return (Signature) object;
+        }
+        return null;
+    }
+
+    @Override
+    public int signatureParameterCount(Signature signature) {
+        return signature.getParameterCount(false);
+    }
+
+    @Override
+    public String signatureParameterTypeName(Signature signature, int index) {
+        return signature.getParameterType(index, null).getName();
+    }
+
+    @Override
+    public String signatureReturnTypeName(Signature signature) {
+        return signature.getReturnType(null).getName();
+    }
+
+    @Override
+    public NodeSourcePosition nodeSourcePosition(Object object) {
+        if (object instanceof NodeSourcePosition) {
+            return (NodeSourcePosition) object;
+        }
+        return null;
+    }
+
+    @Override
+    public ResolvedJavaMethod nodeSourcePositionMethod(NodeSourcePosition pos) {
+        return pos.getMethod();
     }
 
     @Override
-    public void close() {
-        try {
-            flush();
-            channel.close();
-        } catch (IOException ex) {
-            throw new Error(ex);
+    public NodeSourcePosition nodeSourcePositionCaller(NodeSourcePosition pos) {
+        return pos.getCaller();
+    }
+
+    @Override
+    public int nodeSourcePositionBCI(NodeSourcePosition pos) {
+        return pos.getBCI();
+    }
+
+    @Override
+    public StackTraceElement methodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos) {
+        return method.asStackTraceElement(bci);
+    }
+
+    static final class GraphInfo {
+        final DebugContext debug;
+        final Graph graph;
+        final ControlFlowGraph cfg;
+        final BlockMap<List<Node>> blockToNodes;
+        final NodeMap<Block> nodeToBlocks;
+        final List<Block> blocks;
+
+        private GraphInfo(DebugContext debug, Graph graph) {
+            this.debug = debug;
+            this.graph = graph;
+            StructuredGraph.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 (DebugOptions.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) {
+                        }
+                    }
+
+                }
+            }
+            cfg = scheduleResult == null ? debug.contextLookup(ControlFlowGraph.class) : scheduleResult.getCFG();
+            blockToNodes = scheduleResult == null ? null : scheduleResult.getBlockToNodesMap();
+            nodeToBlocks = scheduleResult == null ? null : scheduleResult.getNodeToBlockMap();
+            blocks = cfg == null ? null : Arrays.asList(cfg.getBlocks());
         }
     }
+
 }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugHandlersFactory.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/GraalDebugHandlersFactory.java	Fri Aug 04 19:59:33 2017 -0700
@@ -48,6 +48,7 @@
 
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
 import org.graalvm.compiler.core.common.CompilationIdentifier;
+import org.graalvm.compiler.debug.Assertions;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.debug.DebugDumpHandler;
 import org.graalvm.compiler.debug.DebugHandler;
@@ -203,7 +204,7 @@
         }
         String ext = PathUtilities.formatExtension(extension);
         Path result = createUnique(DebugOptions.getDumpDirectory(options), id, label, ext, createDirectory);
-        if (ShowDumpFiles.getValue(options)) {
+        if (ShowDumpFiles.getValue(options) || Assertions.assertionsEnabled()) {
             TTY.println("Dumping debug output to %s", result.toAbsolutePath().toString());
         }
         return result;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, Red Hat Inc. 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.replacements.aarch64;
+
+import jdk.vm.ci.aarch64.AArch64Kind;
+import org.graalvm.compiler.core.aarch64.AArch64ArithmeticLIRGenerator;
+import org.graalvm.compiler.core.aarch64.AArch64LIRGenerator;
+import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.graph.NodeClass;
+import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
+import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.calc.SignExtendNode;
+import org.graalvm.compiler.nodes.calc.ZeroExtendNode;
+import org.graalvm.compiler.nodes.extended.GuardingNode;
+import org.graalvm.compiler.nodes.memory.ReadNode;
+import org.graalvm.compiler.nodes.memory.address.AddressNode;
+import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
+import org.graalvm.word.LocationIdentity;
+
+/**
+ * AArch64-specific subclass of ReadNode that knows how to merge ZeroExtend and SignExtend into the
+ * read.
+ */
+
+@NodeInfo
+public class AArch64ReadNode extends ReadNode {
+    public static final NodeClass<AArch64ReadNode> TYPE = NodeClass.create(AArch64ReadNode.class);
+    private final IntegerStamp accessStamp;
+    private final boolean isSigned;
+
+    public AArch64ReadNode(AddressNode address, LocationIdentity location, Stamp stamp, GuardingNode guard, BarrierType barrierType, boolean nullCheck,
+                    FrameState stateBefore, IntegerStamp accessStamp, boolean isSigned) {
+        super(TYPE, address, location, stamp, guard, barrierType, nullCheck, stateBefore);
+        this.accessStamp = accessStamp;
+        this.isSigned = isSigned;
+    }
+
+    @Override
+    public void generate(NodeLIRBuilderTool gen) {
+        AArch64LIRGenerator lirgen = (AArch64LIRGenerator) gen.getLIRGeneratorTool();
+        AArch64ArithmeticLIRGenerator arithgen = (AArch64ArithmeticLIRGenerator) lirgen.getArithmetic();
+        AArch64Kind readKind = (AArch64Kind) lirgen.getLIRKind(accessStamp).getPlatformKind();
+        int resultBits = ((IntegerStamp) stamp()).getBits();
+        gen.setResult(this, arithgen.emitExtendMemory(isSigned, readKind, resultBits, (AArch64AddressValue) gen.operand(getAddress()), gen.state(this)));
+    }
+
+    /**
+     * replace a ReadNode with an AArch64-specific variant which knows how to merge a downstream
+     * zero or sign extend into the read operation.
+     *
+     * @param readNode
+     */
+    public static void replace(ReadNode readNode) {
+        assert readNode.getUsageCount() == 1;
+        assert readNode.getUsageAt(0) instanceof ZeroExtendNode || readNode.getUsageAt(0) instanceof SignExtendNode;
+
+        ValueNode usage = (ValueNode) readNode.getUsageAt(0);
+        boolean isSigned = usage instanceof SignExtendNode;
+        IntegerStamp accessStamp = ((IntegerStamp) readNode.getAccessStamp());
+
+        AddressNode address = readNode.getAddress();
+        LocationIdentity location = readNode.getLocationIdentity();
+        Stamp stamp = usage.stamp();
+        GuardingNode guard = readNode.getGuard();
+        BarrierType barrierType = readNode.getBarrierType();
+        boolean nullCheck = readNode.getNullCheck();
+        FrameState stateBefore = readNode.stateBefore();
+        AArch64ReadNode clone = new AArch64ReadNode(address, location, stamp, guard, barrierType, nullCheck, stateBefore, accessStamp, isSigned);
+        StructuredGraph graph = readNode.graph();
+        graph.add(clone);
+        // splice out the extend node
+        usage.replaceAtUsagesAndDelete(readNode);
+        // swap the clone for the read
+        graph.replaceFixedWithFixed(readNode, clone);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadReplacementPhase.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, Red Hat Inc. 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.replacements.aarch64;
+
+import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.calc.SignExtendNode;
+import org.graalvm.compiler.nodes.calc.ZeroExtendNode;
+import org.graalvm.compiler.nodes.memory.ReadNode;
+import org.graalvm.compiler.phases.Phase;
+
+/**
+ * AArch64-specific phase which substitutes certain read nodes with arch-specific variants in order
+ * to allow merging of zero and sign extension into the read operation.
+ */
+
+public class AArch64ReadReplacementPhase extends Phase {
+    @Override
+    protected void run(StructuredGraph graph) {
+        for (Node node : graph.getNodes()) {
+            // don't process nodes we just added
+            if (node instanceof AArch64ReadNode) {
+                continue;
+            }
+            if (node instanceof ReadNode) {
+                ReadNode readNode = (ReadNode) node;
+                if (readNode.getUsageCount() == 1) {
+                    Node usage = readNode.getUsageAt(0);
+                    if (usage instanceof ZeroExtendNode || usage instanceof SignExtendNode) {
+                        AArch64ReadNode.replace(readNode);
+                    }
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnVolatileReadTest.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2011, 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.replacements.test;
+
+import org.graalvm.compiler.api.directives.GraalDirectives;
+import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.junit.Test;
+
+import jdk.vm.ci.code.InstalledCode;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+/**
+ * Tests that deoptimization upon volatile read will not roll back the read. The test cases rely on
+ * the fact that invocations to {@link GraalDirectives} utilities are substituted and not valid
+ * targets for deoptimization.
+ */
+public class DeoptimizeOnVolatileReadTest extends GraalCompilerTest {
+
+    static class Dummy {
+        boolean f1 = false;
+        volatile boolean f2 = false;
+    }
+
+    public static int test1Snippet(Dummy dummy) {
+        if (GraalDirectives.injectBranchProbability(0, GraalDirectives.inCompiledCode() & dummy.f1)) {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    @Test
+    public void test1() {
+        ResolvedJavaMethod method = getResolvedJavaMethod("test1Snippet");
+
+        Dummy dummy = new Dummy();
+        Result expected = executeExpected(method, null, dummy);
+        assertEquals(new Result(0, null), expected);
+
+        dummy.f1 = true;
+
+        InstalledCode code = getCode(method);
+        Result actual;
+
+        try {
+            actual = new Result(code.executeVarargs(dummy), null);
+        } catch (Exception e) {
+            actual = new Result(null, e);
+        }
+
+        // The code should get deoptimized, and resume execution at the beginning of the method.
+        // Therefore it does not re-enter the branch as inCompiledCode() is false.
+        assertEquals(new Result(0, null), actual);
+        assertFalse(code.isValid());
+    }
+
+    public static int test2Snippet(Dummy dummy) {
+        if (GraalDirectives.injectBranchProbability(0, GraalDirectives.inCompiledCode() & dummy.f2)) {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    @Test
+    public void test2() {
+        ResolvedJavaMethod method = getResolvedJavaMethod("test2Snippet");
+
+        Dummy dummy = new Dummy();
+        Result expected = executeExpected(method, null, dummy);
+        assertEquals(new Result(0, null), expected);
+
+        dummy.f2 = true;
+
+        InstalledCode code = getCode(method);
+        Result actual;
+
+        try {
+            actual = new Result(code.executeVarargs(dummy), null);
+        } catch (Exception e) {
+            actual = new Result(null, e);
+        }
+
+        // The code should get deoptimized, and resume execution at the then-branch.
+        assertEquals(new Result(1, null), actual);
+        assertFalse(code.isValid());
+    }
+
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsUtil.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsUtil.java	Fri Aug 04 19:59:33 2017 -0700
@@ -31,7 +31,7 @@
         // empty
     }
 
-    public static final boolean REPLACEMENTS_ASSERTIONS_ENABLED = Assertions.ENABLED;
+    public static final boolean REPLACEMENTS_ASSERTIONS_ENABLED = Assertions.assertionsEnabled();
 
     /**
      * Asserts that condition evaluates to true by the time compilation is finished. This is
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java	Fri Aug 04 19:59:33 2017 -0700
@@ -320,6 +320,7 @@
                 ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp());
                 if (argumentType == null || (argumentType.isAssignableFrom(targetType.getType()) && !argumentType.equals(targetType.getType()))) {
                     LogicNode inst = InstanceOfNode.createAllowNull(targetType, argument, null, null);
+                    assert !inst.isAlive();
                     if (!inst.isTautology()) {
                         inst = adder.add(inst);
                         AnchoringNode guardAnchor = adder.getGuardAnchor();
@@ -337,8 +338,6 @@
                         }
                         ValueNode valueNode = adder.add(PiNode.create(argument, StampFactory.object(targetType), guard.asNode()));
                         arguments[index] = valueNode;
-                    } else {
-                        inst.safeDelete();
                     }
                 }
             }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -36,6 +36,8 @@
 import org.graalvm.util.Equivalence;
 import org.graalvm.word.LocationIdentity;
 
+import jdk.vm.ci.meta.JavaKind;
+
 public final class PEReadEliminationBlockState extends PartialEscapeBlockState<PEReadEliminationBlockState> {
 
     final EconomicMap<ReadCacheEntry, ValueNode> readCache;
@@ -45,17 +47,20 @@
         public final LocationIdentity identity;
         public final ValueNode object;
         public final int index;
+        public final JavaKind kind;
 
-        ReadCacheEntry(LocationIdentity identity, ValueNode object, int index) {
+        ReadCacheEntry(LocationIdentity identity, ValueNode object, int index, JavaKind kind) {
             this.identity = identity;
             this.object = object;
             this.index = index;
+            this.kind = kind;
         }
 
         @Override
         public int hashCode() {
             int result = 31 + ((identity == null) ? 0 : identity.hashCode());
             result = 31 * result + ((object == null) ? 0 : System.identityHashCode(object));
+            result = 31 * result + kind.ordinal();
             return result * 31 + index;
         }
 
@@ -65,12 +70,12 @@
                 return false;
             }
             ReadCacheEntry other = (ReadCacheEntry) obj;
-            return identity.equals(other.identity) && object == other.object && index == other.index;
+            return identity.equals(other.identity) && object == other.object && index == other.index && kind == other.kind;
         }
 
         @Override
         public String toString() {
-            return index == -1 ? (object + ":" + identity) : (object + "[" + index + "]:" + identity);
+            return index == -1 ? (object + ":" + kind + "<" + identity + ">") : (object + "[" + index + "]:" + kind + "<" + identity + ">");
         }
     }
 
@@ -94,7 +99,7 @@
         if (virtual instanceof VirtualInstanceNode) {
             VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
             for (int i = 0; i < instance.entryCount(); i++) {
-                readCache.put(new ReadCacheEntry(new FieldLocationIdentity(instance.field(i)), representation, -1), values.get(i));
+                readCache.put(new ReadCacheEntry(new FieldLocationIdentity(instance.field(i)), representation, -1, instance.field(i).getJavaKind()), values.get(i));
             }
         }
     }
@@ -107,7 +112,7 @@
         return super.equivalentTo(other);
     }
 
-    public void addReadCache(ValueNode object, LocationIdentity identity, int index, ValueNode value, PartialEscapeClosure<?> closure) {
+    public void addReadCache(ValueNode object, LocationIdentity identity, int index, JavaKind kind, ValueNode value, PartialEscapeClosure<?> closure) {
         ValueNode cacheObject;
         ObjectState obj = closure.getObjectState(this, object);
         if (obj != null) {
@@ -116,10 +121,10 @@
         } else {
             cacheObject = object;
         }
-        readCache.put(new ReadCacheEntry(identity, cacheObject, index), value);
+        readCache.put(new ReadCacheEntry(identity, cacheObject, index, kind), value);
     }
 
-    public ValueNode getReadCache(ValueNode object, LocationIdentity identity, int index, PartialEscapeClosure<?> closure) {
+    public ValueNode getReadCache(ValueNode object, LocationIdentity identity, int index, JavaKind kind, PartialEscapeClosure<?> closure) {
         ValueNode cacheObject;
         ObjectState obj = closure.getObjectState(this, object);
         if (obj != null) {
@@ -128,7 +133,7 @@
         } else {
             cacheObject = object;
         }
-        ValueNode cacheValue = readCache.get(new ReadCacheEntry(identity, cacheObject, index));
+        ValueNode cacheValue = readCache.get(new ReadCacheEntry(identity, cacheObject, index, kind));
         obj = closure.getObjectState(this, cacheValue);
         if (obj != null) {
             assert !obj.isVirtual();
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -31,6 +31,7 @@
 
 import org.graalvm.compiler.core.common.cfg.Loop;
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.FieldLocationIdentity;
@@ -130,9 +131,9 @@
         return false;
     }
 
-    private boolean processStore(FixedNode store, ValueNode object, LocationIdentity identity, int index, ValueNode value, PEReadEliminationBlockState state, GraphEffectList effects) {
+    private boolean processStore(FixedNode store, ValueNode object, LocationIdentity identity, int index, JavaKind kind, ValueNode value, PEReadEliminationBlockState state, GraphEffectList effects) {
         ValueNode unproxiedObject = GraphUtil.unproxify(object);
-        ValueNode cachedValue = state.getReadCache(object, identity, index, this);
+        ValueNode cachedValue = state.getReadCache(object, identity, index, kind, this);
 
         ValueNode finalValue = getScalarAlias(value);
         boolean result = false;
@@ -141,15 +142,17 @@
             result = true;
         }
         state.killReadCache(identity, index);
-        state.addReadCache(unproxiedObject, identity, index, finalValue, this);
+        state.addReadCache(unproxiedObject, identity, index, kind, finalValue, this);
         return result;
     }
 
-    private boolean processLoad(FixedNode load, ValueNode object, LocationIdentity identity, int index, PEReadEliminationBlockState state, GraphEffectList effects) {
+    private boolean processLoad(FixedNode load, ValueNode object, LocationIdentity identity, int index, JavaKind kind, PEReadEliminationBlockState state, GraphEffectList effects) {
         ValueNode unproxiedObject = GraphUtil.unproxify(object);
-        ValueNode cachedValue = state.getReadCache(unproxiedObject, identity, index, this);
+        ValueNode cachedValue = state.getReadCache(unproxiedObject, identity, index, kind, this);
         if (cachedValue != null) {
-            if (!load.stamp().isCompatible(cachedValue.stamp())) {
+            Stamp loadStamp = load.stamp();
+            Stamp cachedValueStamp = cachedValue.stamp();
+            if (!loadStamp.isCompatible(cachedValueStamp)) {
                 /*
                  * Can either be the first field of a two slot write to a one slot field which would
                  * have a non compatible stamp or the second load which will see Illegal.
@@ -164,7 +167,7 @@
                 return true;
             }
         } else {
-            state.addReadCache(unproxiedObject, identity, index, load, this);
+            state.addReadCache(unproxiedObject, identity, index, kind, load, this);
             return false;
         }
     }
@@ -177,13 +180,13 @@
                 int index = VirtualArrayNode.entryIndexForOffset(offset, load.accessKind(), type.getComponentType(), Integer.MAX_VALUE);
                 ValueNode object = GraphUtil.unproxify(load.object());
                 LocationIdentity location = NamedLocationIdentity.getArrayLocation(type.getComponentType().getJavaKind());
-                ValueNode cachedValue = state.getReadCache(object, location, index, this);
+                ValueNode cachedValue = state.getReadCache(object, location, index, load.accessKind(), this);
                 if (cachedValue != null && load.stamp().isCompatible(cachedValue.stamp())) {
                     effects.replaceAtUsages(load, cachedValue, load);
                     addScalarAlias(load, cachedValue);
                     return true;
                 } else {
-                    state.addReadCache(object, location, index, load, this);
+                    state.addReadCache(object, location, index, load.accessKind(), load, this);
                 }
             }
         }
@@ -197,7 +200,7 @@
             if (store.offset().isConstant()) {
                 long offset = store.offset().asJavaConstant().asLong();
                 int index = VirtualArrayNode.entryIndexForOffset(offset, store.accessKind(), type.getComponentType(), Integer.MAX_VALUE);
-                return processStore(store, store.object(), location, index, store.value(), state, effects);
+                return processStore(store, store.object(), location, index, store.accessKind(), store.value(), state, effects);
             } else {
                 processIdentity(state, location);
             }
@@ -208,7 +211,7 @@
     }
 
     private boolean processArrayLength(ArrayLengthNode length, PEReadEliminationBlockState state, GraphEffectList effects) {
-        return processLoad(length, length.array(), ARRAY_LENGTH_LOCATION, -1, state, effects);
+        return processLoad(length, length.array(), ARRAY_LENGTH_LOCATION, -1, JavaKind.Int, state, effects);
     }
 
     private boolean processStoreField(StoreFieldNode store, PEReadEliminationBlockState state, GraphEffectList effects) {
@@ -216,7 +219,7 @@
             state.killReadCache();
             return false;
         }
-        return processStore(store, store.object(), new FieldLocationIdentity(store.field()), -1, store.value(), state, effects);
+        return processStore(store, store.object(), new FieldLocationIdentity(store.field()), -1, store.field().getJavaKind(), store.value(), state, effects);
     }
 
     private boolean processLoadField(LoadFieldNode load, PEReadEliminationBlockState state, GraphEffectList effects) {
@@ -224,14 +227,14 @@
             state.killReadCache();
             return false;
         }
-        return processLoad(load, load.object(), new FieldLocationIdentity(load.field()), -1, state, effects);
+        return processLoad(load, load.object(), new FieldLocationIdentity(load.field()), -1, load.field().getJavaKind(), state, effects);
     }
 
     private boolean processStoreIndexed(StoreIndexedNode store, PEReadEliminationBlockState state, GraphEffectList effects) {
         LocationIdentity arrayLocation = NamedLocationIdentity.getArrayLocation(store.elementKind());
         if (store.index().isConstant()) {
             int index = ((JavaConstant) store.index().asConstant()).asInt();
-            return processStore(store, store.array(), arrayLocation, index, store.value(), state, effects);
+            return processStore(store, store.array(), arrayLocation, index, store.elementKind(), store.value(), state, effects);
         } else {
             state.killReadCache(arrayLocation, -1);
         }
@@ -242,13 +245,13 @@
         if (load.index().isConstant()) {
             int index = ((JavaConstant) load.index().asConstant()).asInt();
             LocationIdentity arrayLocation = NamedLocationIdentity.getArrayLocation(load.elementKind());
-            return processLoad(load, load.array(), arrayLocation, index, state, effects);
+            return processLoad(load, load.array(), arrayLocation, index, load.elementKind(), state, effects);
         }
         return false;
     }
 
     private boolean processUnbox(UnboxNode unbox, PEReadEliminationBlockState state, GraphEffectList effects) {
-        return processLoad(unbox, unbox.getValue(), UNBOX_LOCATIONS.get(unbox.getBoxingKind()), -1, state, effects);
+        return processLoad(unbox, unbox.getValue(), UNBOX_LOCATIONS.get(unbox.getBoxingKind()), -1, unbox.getBoxingKind(), state, effects);
     }
 
     private static void processIdentity(PEReadEliminationBlockState state, LocationIdentity identity) {
@@ -290,7 +293,7 @@
                     if (object != null) {
                         Pair<ValueNode, Object> pair = firstValueSet.get(object);
                         while (pair != null) {
-                            initialState.addReadCache(pair.getLeft(), entry.identity, entry.index, initialState.getReadCache().get(entry), this);
+                            initialState.addReadCache(pair.getLeft(), entry.identity, entry.index, entry.kind, initialState.getReadCache().get(entry), this);
                             pair = (Pair<ValueNode, Object>) pair.getRight();
                         }
                     }
@@ -307,7 +310,7 @@
             MapCursor<ReadCacheEntry, ValueNode> entry = exitState.getReadCache().getEntries();
             while (entry.advance()) {
                 if (initialState.getReadCache().get(entry.getKey()) != entry.getValue()) {
-                    ValueNode value = exitState.getReadCache(entry.getKey().object, entry.getKey().identity, entry.getKey().index, this);
+                    ValueNode value = exitState.getReadCache(entry.getKey().object, entry.getKey().identity, entry.getKey().index, entry.getKey().kind, this);
                     assert value != null : "Got null from read cache, entry's value:" + entry.getValue();
                     if (!(value instanceof ProxyNode) || ((ProxyNode) value).proxyPoint() != exitNode) {
                         ProxyNode proxy = new ValueProxyNode(value, exitNode);
@@ -366,7 +369,7 @@
                     PhiNode phiNode = getPhi(key, value.stamp().unrestricted());
                     mergeEffects.addFloatingNode(phiNode, "mergeReadCache");
                     for (int i = 0; i < states.size(); i++) {
-                        ValueNode v = states.get(i).getReadCache(key.object, key.identity, key.index, PEReadEliminationClosure.this);
+                        ValueNode v = states.get(i).getReadCache(key.object, key.identity, key.index, key.kind, PEReadEliminationClosure.this);
                         assert phiNode.stamp().isCompatible(v.stamp()) : "Cannot create read elimination phi for inputs with incompatible stamps.";
                         setPhiInput(phiNode, i, v);
                     }
@@ -383,19 +386,19 @@
                 if (phi.getStackKind() == JavaKind.Object) {
                     for (ReadCacheEntry entry : states.get(0).readCache.getKeys()) {
                         if (entry.object == getPhiValueAt(phi, 0)) {
-                            mergeReadCachePhi(phi, entry.identity, entry.index, states);
+                            mergeReadCachePhi(phi, entry.identity, entry.index, entry.kind, states);
                         }
                     }
                 }
             }
         }
 
-        private void mergeReadCachePhi(PhiNode phi, LocationIdentity identity, int index, List<PEReadEliminationBlockState> states) {
+        private void mergeReadCachePhi(PhiNode phi, LocationIdentity identity, int index, JavaKind kind, List<PEReadEliminationBlockState> states) {
             ValueNode[] values = new ValueNode[states.size()];
-            values[0] = states.get(0).getReadCache(getPhiValueAt(phi, 0), identity, index, PEReadEliminationClosure.this);
+            values[0] = states.get(0).getReadCache(getPhiValueAt(phi, 0), identity, index, kind, PEReadEliminationClosure.this);
             if (values[0] != null) {
                 for (int i = 1; i < states.size(); i++) {
-                    ValueNode value = states.get(i).getReadCache(getPhiValueAt(phi, i), identity, index, PEReadEliminationClosure.this);
+                    ValueNode value = states.get(i).getReadCache(getPhiValueAt(phi, i), identity, index, kind, PEReadEliminationClosure.this);
                     // e.g. unsafe loads / stores with same identity and different access kinds see
                     // mergeReadCache(states)
                     if (value == null || !values[i - 1].stamp().isCompatible(value.stamp())) {
@@ -404,12 +407,12 @@
                     values[i] = value;
                 }
 
-                PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi, index), values[0].stamp().unrestricted());
+                PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi, index, kind), values[0].stamp().unrestricted());
                 mergeEffects.addFloatingNode(phiNode, "mergeReadCachePhi");
                 for (int i = 0; i < values.length; i++) {
                     setPhiInput(phiNode, i, values[i]);
                 }
-                newState.readCache.put(new ReadCacheEntry(identity, phi, index), phiNode);
+                newState.readCache.put(new ReadCacheEntry(identity, phi, index, kind), phiNode);
             }
         }
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeBlockState.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -24,9 +24,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
@@ -54,6 +52,19 @@
      */
     private ObjectState[] objectStates;
 
+    public boolean contains(VirtualObjectNode value) {
+        for (ObjectState state : objectStates) {
+            if (state != null && state.isVirtual() && state.getEntries() != null) {
+                for (ValueNode entry : state.getEntries()) {
+                    if (entry == value) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
     private static class RefCount {
         private int refCount = 1;
     }
@@ -310,41 +321,6 @@
         return true;
     }
 
-    protected static <K, V> boolean compareMaps(Map<K, V> left, Map<K, V> right) {
-        if (left.size() != right.size()) {
-            return false;
-        }
-        return compareMapsNoSize(left, right);
-    }
-
-    protected static <K, V> boolean compareMapsNoSize(Map<K, V> left, Map<K, V> right) {
-        if (left == right) {
-            return true;
-        }
-        for (Map.Entry<K, V> entry : right.entrySet()) {
-            K key = entry.getKey();
-            V value = entry.getValue();
-            assert value != null;
-            V otherValue = left.get(key);
-            if (otherValue != value && !value.equals(otherValue)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    protected static <U, V> void meetMaps(Map<U, V> target, Map<U, V> source) {
-        Iterator<Map.Entry<U, V>> iter = target.entrySet().iterator();
-        while (iter.hasNext()) {
-            Map.Entry<U, V> entry = iter.next();
-            if (source.containsKey(entry.getKey())) {
-                assert source.get(entry.getKey()) == entry.getValue();
-            } else {
-                iter.remove();
-            }
-        }
-    }
-
     public void resetObjectStates(int size) {
         objectStates = new ObjectState[size];
     }
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java	Sat Aug 05 03:05:06 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 Aug 04 19:59:33 2017 -0700
@@ -1009,12 +1009,7 @@
                     for (int i = 0; i < states.length; i++) {
                         VirtualObjectNode virtual = virtualObjs[i];
 
-                        boolean identitySurvives = virtual.hasIdentity() &&
-                                        // check whether we trivially see that this is the only
-                                        // reference to this allocation
-                                        !isSingleUsageAllocation(getPhiValueAt(phi, i));
-
-                        if (identitySurvives || !firstVirtual.type().equals(virtual.type()) || firstVirtual.entryCount() != virtual.entryCount()) {
+                        if (!firstVirtual.type().equals(virtual.type()) || firstVirtual.entryCount() != virtual.entryCount()) {
                             compatible = false;
                             break;
                         }
@@ -1024,6 +1019,18 @@
                         }
                     }
                     if (compatible) {
+                        for (int i = 0; i < states.length; i++) {
+                            VirtualObjectNode virtual = virtualObjs[i];
+                            /*
+                             * check whether we trivially see that this is the only reference to
+                             * this allocation
+                             */
+                            if (virtual.hasIdentity() && !isSingleUsageAllocation(getPhiValueAt(phi, i), virtualObjs, states[i])) {
+                                compatible = false;
+                            }
+                        }
+                    }
+                    if (compatible) {
                         VirtualObjectNode virtual = getValueObjectVirtual(phi, virtualObjs[0]);
                         mergeEffects.addFloatingNode(virtual, "valueObjectNode");
                         mergeEffects.deleteNode(phi);
@@ -1069,14 +1076,34 @@
             return materialized;
         }
 
-        private boolean isSingleUsageAllocation(ValueNode value) {
+        private boolean isSingleUsageAllocation(ValueNode value, VirtualObjectNode[] virtualObjs, PartialEscapeBlockState<?> state) {
             /*
              * If the phi input is an allocation, we know that it is a "fresh" value, i.e., that
              * this is a value that will only appear through this source, and cannot appear anywhere
              * else. If the phi is also the only usage of this input, we know that no other place
              * can check object identity against it, so it is safe to lose the object identity here.
              */
-            return value instanceof AllocatedObjectNode && value.hasExactlyOneUsage();
+            if (!(value instanceof AllocatedObjectNode && value.hasExactlyOneUsage())) {
+                return false;
+            }
+
+            /*
+             * Check that the state only references the one virtual object from the Phi.
+             */
+            VirtualObjectNode singleVirtual = null;
+            for (int v = 0; v < virtualObjs.length; v++) {
+                if (state.contains(virtualObjs[v])) {
+                    if (singleVirtual == null) {
+                        singleVirtual = virtualObjs[v];
+                    } else if (singleVirtual != virtualObjs[v]) {
+                        /*
+                         * More than one virtual object is visible in the object state.
+                         */
+                        return false;
+                    }
+                }
+            }
+            return true;
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/DefaultGraphBlocks.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011, 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.graphio;
+
+import java.util.Collection;
+import java.util.Collections;
+
+final class DefaultGraphBlocks implements GraphBlocks<Object, Object, Object> {
+    private static final DefaultGraphBlocks DEFAULT = new DefaultGraphBlocks();
+
+    private DefaultGraphBlocks() {
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <G, B, N> GraphBlocks<G, B, N> empty() {
+        return (GraphBlocks<G, B, N>) DEFAULT;
+    }
+
+    @Override
+    public Collection<? extends Void> blocks(Object graph) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public int blockId(Object block) {
+        return -1;
+    }
+
+    @Override
+    public Collection<? extends Object> blockNodes(Object info, Object block) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Collection<? extends Object> blockSuccessors(Object block) {
+        return Collections.emptyList();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/DefaultGraphTypes.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011, 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.graphio;
+
+final class DefaultGraphTypes implements GraphTypes {
+    static final GraphTypes DEFAULT = new DefaultGraphTypes();
+
+    private DefaultGraphTypes() {
+    }
+
+    @Override
+    public Class<?> enumClass(Object enumValue) {
+        if (enumValue instanceof Enum<?>) {
+            return enumValue.getClass();
+        }
+        return null;
+    }
+
+    @Override
+    public int enumOrdinal(Object obj) {
+        if (obj instanceof Enum<?>) {
+            return ((Enum<?>) obj).ordinal();
+        }
+        return -1;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public String[] enumTypeValues(Object clazz) {
+        if (clazz instanceof Class<?>) {
+            Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) clazz;
+            Enum<?>[] constants = enumClass.getEnumConstants();
+            if (constants != null) {
+                String[] names = new String[constants.length];
+                for (int i = 0; i < constants.length; i++) {
+                    names[i] = constants[i].name();
+                }
+                return names;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String typeName(Object clazz) {
+        if (clazz instanceof Class<?>) {
+            return ((Class<?>) clazz).getName();
+        }
+        return null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphBlocks.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011, 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.graphio;
+
+import java.util.Collection;
+
+/**
+ * Special support for dealing with blocks.
+ *
+ * @param <G> the type that represents the graph
+ * @param <B> the type that represents the block
+ * @param <N> the type of the node
+ */
+public interface GraphBlocks<G, B, N> {
+    /**
+     * All blocks in the graph.
+     *
+     * @param graph the graph
+     * @return collection of blocks in the graph
+     */
+    Collection<? extends B> blocks(G graph);
+
+    /**
+     * Unique id of a block.
+     *
+     * @param block the block
+     * @return the id of the block
+     */
+    int blockId(B block);
+
+    Collection<? extends N> blockNodes(G info, B block);
+
+    Collection<? extends B> blockSuccessors(B block);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphElements.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2011, 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.graphio;
+
+/**
+ * Representation of methods, fields, their signatures and code locations.
+ *
+ * @param <M> type representing methods
+ * @param <F> type representing fields
+ * @param <S> type representing signature
+ * @param <P> type representing source code location
+ */
+public interface GraphElements<M, F, S, P> {
+    /**
+     * Recognize method. Can the object be seen as a method?
+     *
+     * @param obj the object to check
+     * @return <code>null</code> if the object isn't a method, non-null value otherwise
+     */
+    M method(Object obj);
+
+    /**
+     * Bytecode for a method.
+     *
+     * @param method the method
+     * @return bytecode of the method
+     */
+    byte[] methodCode(M method);
+
+    /**
+     * Method modifiers.
+     *
+     * @param method the method
+     * @return its modifiers
+     */
+    int methodModifiers(M method);
+
+    /**
+     * Method's signature.
+     *
+     * @param method the method
+     * @return signature of the method
+     */
+    S methodSignature(M method);
+
+    /**
+     * Method name.
+     *
+     * @param method the method
+     * @return name of the method
+     */
+    String methodName(M method);
+
+    /**
+     * Method's declaring class. The returned object shall be a {@link Class} or be recognizable by
+     * {@link GraphTypes#typeName(java.lang.Object)} method.
+     *
+     * @param method the method
+     * @return object representing class that defined the method
+     */
+    Object methodDeclaringClass(M method);
+
+    /**
+     * Recognizes a field. Can the object be seen as a field?
+     *
+     * @param object the object to check
+     * @return <code>null</code> if the object isn't a field, non-null value otherwise
+     */
+    F field(Object object);
+
+    /**
+     * Field modifiers.
+     *
+     * @param field the field
+     * @return field modifiers
+     */
+    int fieldModifiers(F field);
+
+    /**
+     * Type name of the field.
+     *
+     * @param field the field
+     * @return the name of the field's type
+     */
+    String fieldTypeName(F field);
+
+    /**
+     * Name of a field.
+     *
+     * @param field the field
+     * @return the name of the field
+     */
+    String fieldName(F field);
+
+    /**
+     * Field's declaring class. The returned object shall be a {@link Class} or be recognizable by
+     * {@link GraphTypes#typeName(java.lang.Object)} method.
+     *
+     * @param field the field
+     * @return object representing class that defined the field
+     */
+    Object fieldDeclaringClass(F field);
+
+    /**
+     * Recognizes signature. Can the object be seen as a signature?
+     *
+     * @param object the object to check
+     * @return <code>null</code> if the object isn't a signature, non-null value otherwise
+     */
+    S signature(Object object);
+
+    /**
+     * Number of parameters of a signature.
+     *
+     * @param signature the signature
+     * @return number of parameters
+     */
+    int signatureParameterCount(S signature);
+
+    /**
+     * Type name of a signature parameter.
+     *
+     * @param signature the signature
+     * @param index index from 0 to {@link #signatureParameterCount(java.lang.Object)} - 1
+     * @return the type name
+     */
+    String signatureParameterTypeName(S signature, int index);
+
+    /**
+     * Type name of a return type.
+     *
+     * @param signature the signature
+     * @return the type name
+     */
+    String signatureReturnTypeName(S signature);
+
+    /**
+     * Recognize a source position. Can the object be seen as a position?
+     *
+     * @param object the object to check
+     * @return <code>null</code> if the object isn't a position, non-null otherwise
+     */
+    P nodeSourcePosition(Object object);
+
+    /**
+     * Method for a position.
+     *
+     * @param pos the position
+     * @return the method at the position
+     */
+    M nodeSourcePositionMethod(P pos);
+
+    /**
+     * Caller of a position.
+     *
+     * @param pos the position
+     * @return <code>null</code> or another position
+     */
+    P nodeSourcePositionCaller(P pos);
+
+    /**
+     * Byte code index of a position.
+     *
+     * @param pos the position
+     * @return the BCI of the position
+     */
+    int nodeSourcePositionBCI(P pos);
+
+    /**
+     * Stack trace element for a method, index and position.
+     *
+     * @param method the method
+     * @param bci the index
+     * @param pos the position
+     * @return stack trace element for the method, index and position
+     */
+    StackTraceElement methodStackTraceElement(M method, int bci, P pos);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphOutput.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2011, 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.graphio;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.nio.channels.WritableByteChannel;
+import java.util.Map;
+
+/**
+ * Instance of output to dump informations about a compiler compilations.
+ *
+ * @param <G> the type of graph this instance handles
+ * @param <M> the type of methods this instance handles
+ */
+public final class GraphOutput<G, M> implements Closeable {
+    private final GraphProtocol<G, ?, ?, ?, ?, M, ?, ?, ?> printer;
+
+    private GraphOutput(GraphProtocol<G, ?, ?, ?, ?, M, ?, ?, ?> p) {
+        this.printer = p;
+    }
+
+    /**
+     * Creates new builder to configure a future instance of {@link GraphOutput}.
+     *
+     * @param <G> the type of the graph
+     * @param <N> the type of the nodes
+     * @param <C> the type of the node classes
+     * @param <P> the type of the ports
+     *
+     * @param structure description of the structure of the graph
+     * @return the builder to configure
+     */
+    public static <G, N, C, P> Builder<G, N, ?> newBuilder(GraphStructure<G, N, C, P> structure) {
+        return new Builder<>(structure);
+    }
+
+    /**
+     * Begins a compilation group.
+     *
+     * @param forGraph
+     * @param name
+     * @param shortName
+     * @param method
+     * @param bci
+     * @param properties
+     * @throws IOException
+     */
+    public void beginGroup(G forGraph, String name, String shortName, M method, int bci, Map<? extends Object, ? extends Object> properties) throws IOException {
+        printer.beginGroup(forGraph, name, shortName, method, bci, properties);
+    }
+
+    /**
+     * Prints a single graph.
+     *
+     * @param graph
+     * @param properties
+     * @param id
+     * @param format
+     * @param args
+     * @throws IOException
+     */
+    public void print(G graph, Map<? extends Object, ? extends Object> properties, int id, String format, Object... args) throws IOException {
+        printer.print(graph, properties, id, format, args);
+    }
+
+    /**
+     * Ends compilation group.
+     *
+     * @throws IOException
+     */
+    public void endGroup() throws IOException {
+        printer.endGroup();
+    }
+
+    /**
+     * Closes the output. Closes allocated resources and associated output channel.
+     */
+    @Override
+    public void close() {
+        printer.close();
+    }
+
+    /**
+     * Builder to configure and create an instance of {@link GraphOutput}.
+     *
+     * @param <G> the type of the (root element of) graph
+     * @param <N> the type of the nodes
+     * @param <M> the type of the methods
+     */
+    public static final class Builder<G, N, M> {
+        private final GraphStructure<G, N, ?, ?> structure;
+        private GraphElements<M, ?, ?, ?> elements = null;
+        private GraphTypes types = DefaultGraphTypes.DEFAULT;
+        private GraphBlocks<G, ?, N> blocks = DefaultGraphBlocks.empty();
+
+        Builder(GraphStructure<G, N, ?, ?> structure) {
+            this.structure = structure;
+        }
+
+        /**
+         * Associates different implementation of types.
+         *
+         * @param graphTypes implementation of types and enum recognition
+         * @return this builder
+         */
+        public Builder<G, N, M> types(GraphTypes graphTypes) {
+            this.types = graphTypes;
+            return this;
+        }
+
+        /**
+         * Associates implementation of blocks.
+         *
+         * @param graphBlocks the blocks implementation
+         * @return this builder
+         */
+        public Builder<G, N, M> blocks(GraphBlocks<G, ?, N> graphBlocks) {
+            this.blocks = graphBlocks;
+            return this;
+        }
+
+        /**
+         * Associates implementation of graph elements.
+         *
+         * @param graphElements the elements implementation
+         * @return this builder
+         */
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        public <E> Builder<G, N, E> elements(GraphElements<E, ?, ?, ?> graphElements) {
+            this.elements = (GraphElements) graphElements;
+            return (Builder<G, N, E>) this;
+        }
+
+        /**
+         * Creates new {@link GraphOutput} to output to provided channel. The output will use
+         * interfaces currently associated with this builder.
+         *
+         * @param channel the channel to output to
+         * @return new graph output
+         * @throws IOException if something goes wrong when writing to the channel
+         */
+        public GraphOutput<G, M> build(WritableByteChannel channel) throws IOException {
+            ProtocolImpl<G, N, ?, ?, ?, M, ?, ?, ?> p = new ProtocolImpl<>(structure, types, blocks, elements, channel);
+            return new GraphOutput<>(p);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 2011, 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.graphio;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+abstract class GraphProtocol<Graph, Node, NodeClass, Edges, Block, ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition> implements Closeable {
+    private static final Charset UTF8 = Charset.forName("UTF-8");
+
+    private static final int CONSTANT_POOL_MAX_SIZE = 8000;
+
+    private static final int BEGIN_GROUP = 0x00;
+    private static final int BEGIN_GRAPH = 0x01;
+    private static final int CLOSE_GROUP = 0x02;
+
+    private static final int POOL_NEW = 0x00;
+    private static final int POOL_STRING = 0x01;
+    private static final int POOL_ENUM = 0x02;
+    private static final int POOL_CLASS = 0x03;
+    private static final int POOL_METHOD = 0x04;
+    private static final int POOL_NULL = 0x05;
+    private static final int POOL_NODE_CLASS = 0x06;
+    private static final int POOL_FIELD = 0x07;
+    private static final int POOL_SIGNATURE = 0x08;
+    private static final int POOL_NODE_SOURCE_POSITION = 0x09;
+
+    private static final int PROPERTY_POOL = 0x00;
+    private static final int PROPERTY_INT = 0x01;
+    private static final int PROPERTY_LONG = 0x02;
+    private static final int PROPERTY_DOUBLE = 0x03;
+    private static final int PROPERTY_FLOAT = 0x04;
+    private static final int PROPERTY_TRUE = 0x05;
+    private static final int PROPERTY_FALSE = 0x06;
+    private static final int PROPERTY_ARRAY = 0x07;
+    private static final int PROPERTY_SUBGRAPH = 0x08;
+
+    private static final int KLASS = 0x00;
+    private static final int ENUM_KLASS = 0x01;
+
+    private static final byte[] MAGIC_BYTES = {'B', 'I', 'G', 'V'};
+
+    private final ConstantPool constantPool;
+    private final ByteBuffer buffer;
+    private final WritableByteChannel channel;
+    private final int versionMajor;
+    private final int versionMinor;
+
+    protected GraphProtocol(WritableByteChannel channel) throws IOException {
+        this(channel, 4, 0);
+    }
+
+    private GraphProtocol(WritableByteChannel channel, int major, int minor) throws IOException {
+        if (major > 4) {
+            throw new IllegalArgumentException();
+        }
+        if (major == 4 && minor > 0) {
+            throw new IllegalArgumentException();
+        }
+        this.versionMajor = major;
+        this.versionMinor = minor;
+        this.constantPool = new ConstantPool();
+        this.buffer = ByteBuffer.allocateDirect(256 * 1024);
+        this.channel = channel;
+        writeVersion();
+    }
+
+    @SuppressWarnings("all")
+    public final void print(Graph graph, Map<? extends Object, ? extends Object> properties, int id, String format, Object... args) throws IOException {
+        writeByte(BEGIN_GRAPH);
+        if (versionMajor >= 3) {
+            writeInt(id);
+            writeString(format);
+            writeInt(args.length);
+            for (Object a : args) {
+                writePropertyObject(graph, a);
+            }
+        } else {
+            writePoolObject(formatTitle(graph, id, format, args));
+        }
+        writeGraph(graph, properties);
+        flush();
+    }
+
+    public final void beginGroup(Graph noGraph, String name, String shortName, ResolvedJavaMethod method, int bci, Map<? extends Object, ? extends Object> properties) throws IOException {
+        writeByte(BEGIN_GROUP);
+        writePoolObject(name);
+        writePoolObject(shortName);
+        writePoolObject(method);
+        writeInt(bci);
+        writeProperties(noGraph, properties);
+    }
+
+    public final void endGroup() throws IOException {
+        writeByte(CLOSE_GROUP);
+    }
+
+    @Override
+    public final void close() {
+        try {
+            flush();
+            channel.close();
+        } catch (IOException ex) {
+            throw new Error(ex);
+        }
+    }
+
+    protected abstract Graph findGraph(Graph current, Object obj);
+
+    protected abstract ResolvedJavaMethod findMethod(Object obj);
+
+    protected abstract NodeClass findNodeClass(Object obj);
+
+    /**
+     * Find a Java class. The returned object must be acceptable by
+     * {@link #findJavaTypeName(java.lang.Object)} and return valid name for the class.
+     *
+     * @param clazz node class object
+     * @return object representing the class, for example {@link Class}
+     */
+    protected abstract Object findJavaClass(NodeClass clazz);
+
+    protected abstract Object findEnumClass(Object enumValue);
+
+    protected abstract String findNameTemplate(NodeClass clazz);
+
+    protected abstract Edges findClassEdges(NodeClass nodeClass, boolean dumpInputs);
+
+    protected abstract int findNodeId(Node n);
+
+    protected abstract void findExtraNodes(Node node, Collection<? super Node> extraNodes);
+
+    protected abstract boolean hasPredecessor(Node node);
+
+    protected abstract int findNodesCount(Graph info);
+
+    protected abstract Iterable<? extends Node> findNodes(Graph info);
+
+    protected abstract void findNodeProperties(Node node, Map<String, Object> props, Graph info);
+
+    protected abstract Collection<? extends Node> findBlockNodes(Graph info, Block block);
+
+    protected abstract int findBlockId(Block sux);
+
+    protected abstract Collection<? extends Block> findBlocks(Graph graph);
+
+    protected abstract Collection<? extends Block> findBlockSuccessors(Block block);
+
+    protected abstract String formatTitle(Graph graph, int id, String format, Object... args);
+
+    protected abstract int findSize(Edges edges);
+
+    protected abstract boolean isDirect(Edges edges, int i);
+
+    protected abstract String findName(Edges edges, int i);
+
+    protected abstract Object findType(Edges edges, int i);
+
+    protected abstract Collection<? extends Node> findNodes(Graph graph, Node node, Edges edges, int i);
+
+    protected abstract int findEnumOrdinal(Object obj);
+
+    protected abstract String[] findEnumTypeValues(Object clazz);
+
+    protected abstract String findJavaTypeName(Object obj);
+
+    protected abstract byte[] findMethodCode(ResolvedJavaMethod method);
+
+    protected abstract int findMethodModifiers(ResolvedJavaMethod method);
+
+    protected abstract Signature findMethodSignature(ResolvedJavaMethod method);
+
+    protected abstract String findMethodName(ResolvedJavaMethod method);
+
+    protected abstract Object findMethodDeclaringClass(ResolvedJavaMethod method);
+
+    protected abstract int findFieldModifiers(ResolvedJavaField field);
+
+    protected abstract String findFieldTypeName(ResolvedJavaField field);
+
+    protected abstract String findFieldName(ResolvedJavaField field);
+
+    protected abstract Object findFieldDeclaringClass(ResolvedJavaField field);
+
+    protected abstract ResolvedJavaField findJavaField(Object object);
+
+    protected abstract Signature findSignature(Object object);
+
+    protected abstract int findSignatureParameterCount(Signature signature);
+
+    protected abstract String findSignatureParameterTypeName(Signature signature, int index);
+
+    protected abstract String findSignatureReturnTypeName(Signature signature);
+
+    protected abstract NodeSourcePosition findNodeSourcePosition(Object object);
+
+    protected abstract ResolvedJavaMethod findNodeSourcePositionMethod(NodeSourcePosition pos);
+
+    protected abstract NodeSourcePosition findNodeSourcePositionCaller(NodeSourcePosition pos);
+
+    protected abstract int findNodeSourcePositionBCI(NodeSourcePosition pos);
+
+    protected abstract StackTraceElement findMethodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos);
+
+    private void writeVersion() throws IOException {
+        writeBytesRaw(MAGIC_BYTES);
+        writeByte(versionMajor);
+        writeByte(versionMinor);
+    }
+
+    private void flush() throws IOException {
+        buffer.flip();
+        /*
+         * Try not to let interrupted threads aborting 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();
+        try {
+            channel.write(buffer);
+        } finally {
+            if (interrupted) {
+                Thread.currentThread().interrupt();
+            }
+        }
+        buffer.compact();
+    }
+
+    private void ensureAvailable(int i) throws IOException {
+        assert buffer.capacity() >= i : "Can not make " + i + " bytes available, buffer is too small";
+        while (buffer.remaining() < i) {
+            flush();
+        }
+    }
+
+    private void writeByte(int b) throws IOException {
+        ensureAvailable(1);
+        buffer.put((byte) b);
+    }
+
+    private void writeInt(int b) throws IOException {
+        ensureAvailable(4);
+        buffer.putInt(b);
+    }
+
+    private void writeLong(long b) throws IOException {
+        ensureAvailable(8);
+        buffer.putLong(b);
+    }
+
+    private void writeDouble(double b) throws IOException {
+        ensureAvailable(8);
+        buffer.putDouble(b);
+    }
+
+    private void writeFloat(float b) throws IOException {
+        ensureAvailable(4);
+        buffer.putFloat(b);
+    }
+
+    private void writeShort(char b) throws IOException {
+        ensureAvailable(2);
+        buffer.putChar(b);
+    }
+
+    private void writeString(String str) throws IOException {
+        byte[] bytes = str.getBytes(UTF8);
+        writeBytes(bytes);
+    }
+
+    private void writeBytes(byte[] b) throws IOException {
+        if (b == null) {
+            writeInt(-1);
+        } else {
+            writeInt(b.length);
+            writeBytesRaw(b);
+        }
+    }
+
+    private void writeBytesRaw(byte[] b) throws IOException {
+        int bytesWritten = 0;
+        while (bytesWritten < b.length) {
+            int toWrite = Math.min(b.length - bytesWritten, buffer.capacity());
+            ensureAvailable(toWrite);
+            buffer.put(b, bytesWritten, toWrite);
+            bytesWritten += toWrite;
+        }
+    }
+
+    private void writeInts(int[] b) throws IOException {
+        if (b == null) {
+            writeInt(-1);
+        } else {
+            writeInt(b.length);
+            int sizeInBytes = b.length * 4;
+            ensureAvailable(sizeInBytes);
+            buffer.asIntBuffer().put(b);
+            buffer.position(buffer.position() + sizeInBytes);
+        }
+    }
+
+    private void writeDoubles(double[] b) throws IOException {
+        if (b == null) {
+            writeInt(-1);
+        } else {
+            writeInt(b.length);
+            int sizeInBytes = b.length * 8;
+            ensureAvailable(sizeInBytes);
+            buffer.asDoubleBuffer().put(b);
+            buffer.position(buffer.position() + sizeInBytes);
+        }
+    }
+
+    private void writePoolObject(Object object) throws IOException {
+        if (object == null) {
+            writeByte(POOL_NULL);
+            return;
+        }
+        Character id = constantPool.get(object);
+        if (id == null) {
+            addPoolEntry(object);
+        } else {
+            if (object instanceof Enum<?> || findEnumOrdinal(object) >= 0) {
+                writeByte(POOL_ENUM);
+            } else if (object instanceof Class<?> || findJavaTypeName(object) != null) {
+                writeByte(POOL_CLASS);
+            } else if (findJavaField(object) != null) {
+                writeByte(POOL_FIELD);
+            } else if (findSignature(object) != null) {
+                writeByte(POOL_SIGNATURE);
+            } else if (versionMajor >= 4 && findNodeSourcePosition(object) != null) {
+                writeByte(POOL_NODE_SOURCE_POSITION);
+            } else {
+                if (findNodeClass(object) != null) {
+                    writeByte(POOL_NODE_CLASS);
+                } else if (findMethod(object) != null) {
+                    writeByte(POOL_METHOD);
+                } else {
+                    writeByte(POOL_STRING);
+                }
+            }
+            writeShort(id.charValue());
+        }
+    }
+
+    private void writeGraph(Graph graph, Map<? extends Object, ? extends Object> properties) throws IOException {
+        writeProperties(graph, properties);
+        writeNodes(graph);
+        writeBlocks(findBlocks(graph), graph);
+    }
+
+    private void writeNodes(Graph info) throws IOException {
+        Map<String, Object> props = new HashMap<>();
+
+        final int size = findNodesCount(info);
+        writeInt(size);
+        int cnt = 0;
+        for (Node node : findNodes(info)) {
+            NodeClass nodeClass = findNodeClass(node);
+            if (nodeClass == null) {
+                throw new IOException("No class for " + node);
+            }
+            findNodeProperties(node, props, info);
+
+            writeInt(findNodeId(node));
+            writePoolObject(nodeClass);
+            writeByte(hasPredecessor(node) ? 1 : 0);
+            writeProperties(info, props);
+            writeEdges(info, node, true);
+            writeEdges(info, node, false);
+
+            props.clear();
+            cnt++;
+        }
+        if (size != cnt) {
+            throw new IOException("Expecting " + size + " nodes, but found " + cnt);
+        }
+    }
+
+    private void writeEdges(Graph graph, Node node, boolean dumpInputs) throws IOException {
+        NodeClass clazz = findNodeClass(node);
+        Edges edges = findClassEdges(clazz, dumpInputs);
+        int size = findSize(edges);
+        for (int i = 0; i < size; i++) {
+            Collection<? extends Node> list = findNodes(graph, node, edges, i);
+            if (isDirect(edges, i)) {
+                if (list != null && list.size() != 1) {
+                    throw new IOException("Edge " + i + " in " + edges + " is direct, but list isn't singleton: " + list);
+                }
+                Node n = null;
+                if (list != null && !list.isEmpty()) {
+                    n = list.iterator().next();
+                }
+                writeNodeRef(n);
+            } else {
+                if (list == null) {
+                    writeShort((char) 0);
+                } else {
+                    int listSize = list.size();
+                    assert listSize == ((char) listSize);
+                    writeShort((char) listSize);
+                    for (Node edge : list) {
+                        writeNodeRef(edge);
+                    }
+                }
+            }
+        }
+    }
+
+    private void writeNodeRef(Node node) throws IOException {
+        writeInt(findNodeId(node));
+    }
+
+    private void writeBlocks(Collection<? extends Block> blocks, Graph info) throws IOException {
+        if (blocks != null) {
+            for (Block block : blocks) {
+                Collection<? extends Node> nodes = findBlockNodes(info, block);
+                if (nodes == null) {
+                    writeInt(0);
+                    return;
+                }
+            }
+            writeInt(blocks.size());
+            for (Block block : blocks) {
+                Collection<? extends Node> nodes = findBlockNodes(info, block);
+                writeInt(findBlockId(block));
+                writeInt(nodes.size());
+                for (Node node : nodes) {
+                    writeInt(findNodeId(node));
+                }
+                final Collection<? extends Block> successors = findBlockSuccessors(block);
+                writeInt(successors.size());
+                for (Block sux : successors) {
+                    writeInt(findBlockId(sux));
+                }
+            }
+        } else {
+            writeInt(0);
+        }
+    }
+
+    private void writeEdgesInfo(NodeClass nodeClass, boolean dumpInputs) throws IOException {
+        Edges edges = findClassEdges(nodeClass, dumpInputs);
+        int size = findSize(edges);
+        writeShort((char) size);
+        for (int i = 0; i < size; i++) {
+            writeByte(isDirect(edges, i) ? 0 : 1);
+            writePoolObject(findName(edges, i));
+            if (dumpInputs) {
+                writePoolObject(findType(edges, i));
+            }
+        }
+    }
+
+    @SuppressWarnings("all")
+    private void addPoolEntry(Object object) throws IOException {
+        ResolvedJavaField field;
+        String typeName;
+        Signature signature;
+        NodeSourcePosition pos;
+        int enumOrdinal;
+        char index = constantPool.add(object);
+        writeByte(POOL_NEW);
+        writeShort(index);
+        if ((typeName = findJavaTypeName(object)) != null) {
+            writeByte(POOL_CLASS);
+            writeString(typeName);
+            String[] enumValueNames = findEnumTypeValues(object);
+            if (enumValueNames != null) {
+                writeByte(ENUM_KLASS);
+                writeInt(enumValueNames.length);
+                for (String o : enumValueNames) {
+                    writePoolObject(o);
+                }
+            } else {
+                writeByte(KLASS);
+            }
+        } else if ((enumOrdinal = findEnumOrdinal(object)) >= 0) {
+            writeByte(POOL_ENUM);
+            writePoolObject(findEnumClass(object));
+            writeInt(enumOrdinal);
+        } else if ((field = findJavaField(object)) != null) {
+            writeByte(POOL_FIELD);
+            writePoolObject(findFieldDeclaringClass(field));
+            writePoolObject(findFieldName(field));
+            writePoolObject(findFieldTypeName(field));
+            writeInt(findFieldModifiers(field));
+        } else if ((signature = findSignature(object)) != null) {
+            writeByte(POOL_SIGNATURE);
+            int args = findSignatureParameterCount(signature);
+            writeShort((char) args);
+            for (int i = 0; i < args; i++) {
+                writePoolObject(findSignatureParameterTypeName(signature, i));
+            }
+            writePoolObject(findSignatureReturnTypeName(signature));
+        } else if (versionMajor >= 4 && (pos = findNodeSourcePosition(object)) != null) {
+            writeByte(POOL_NODE_SOURCE_POSITION);
+            ResolvedJavaMethod method = findNodeSourcePositionMethod(pos);
+            writePoolObject(method);
+            final int bci = findNodeSourcePositionBCI(pos);
+            writeInt(bci);
+            StackTraceElement ste = findMethodStackTraceElement(method, bci, pos);
+            if (ste != null) {
+                writePoolObject(ste.getFileName());
+                writeInt(ste.getLineNumber());
+            } else {
+                writePoolObject(null);
+            }
+            writePoolObject(findNodeSourcePositionCaller(pos));
+        } else {
+            NodeClass nodeClass = findNodeClass(object);
+            if (nodeClass != null) {
+                writeByte(POOL_NODE_CLASS);
+                final Object clazz = findJavaClass(nodeClass);
+                if (versionMajor >= 3) {
+                    writePoolObject(clazz);
+                    writeString(findNameTemplate(nodeClass));
+                } else {
+                    writeString(((Class<?>) clazz).getSimpleName());
+                    String nameTemplate = findNameTemplate(nodeClass);
+                    writeString(nameTemplate);
+                }
+                writeEdgesInfo(nodeClass, true);
+                writeEdgesInfo(nodeClass, false);
+                return;
+            }
+            ResolvedJavaMethod method = findMethod(object);
+            if (method == null) {
+                writeByte(POOL_STRING);
+                writeString(object.toString());
+                return;
+            }
+            writeByte(POOL_METHOD);
+            writePoolObject(findMethodDeclaringClass(method));
+            writePoolObject(findMethodName(method));
+            writePoolObject(findMethodSignature(method));
+            writeInt(findMethodModifiers(method));
+            writeBytes(findMethodCode(method));
+        }
+    }
+
+    private void writePropertyObject(Graph graph, Object obj) throws IOException {
+        if (obj instanceof Integer) {
+            writeByte(PROPERTY_INT);
+            writeInt(((Integer) obj).intValue());
+        } else if (obj instanceof Long) {
+            writeByte(PROPERTY_LONG);
+            writeLong(((Long) obj).longValue());
+        } else if (obj instanceof Double) {
+            writeByte(PROPERTY_DOUBLE);
+            writeDouble(((Double) obj).doubleValue());
+        } else if (obj instanceof Float) {
+            writeByte(PROPERTY_FLOAT);
+            writeFloat(((Float) obj).floatValue());
+        } else if (obj instanceof Boolean) {
+            if (((Boolean) obj).booleanValue()) {
+                writeByte(PROPERTY_TRUE);
+            } else {
+                writeByte(PROPERTY_FALSE);
+            }
+        } else if (obj != null && obj.getClass().isArray()) {
+            Class<?> componentType = obj.getClass().getComponentType();
+            if (componentType.isPrimitive()) {
+                if (componentType == Double.TYPE) {
+                    writeByte(PROPERTY_ARRAY);
+                    writeByte(PROPERTY_DOUBLE);
+                    writeDoubles((double[]) obj);
+                } else if (componentType == Integer.TYPE) {
+                    writeByte(PROPERTY_ARRAY);
+                    writeByte(PROPERTY_INT);
+                    writeInts((int[]) obj);
+                } else {
+                    writeByte(PROPERTY_POOL);
+                    writePoolObject(obj);
+                }
+            } else {
+                writeByte(PROPERTY_ARRAY);
+                writeByte(PROPERTY_POOL);
+                Object[] array = (Object[]) obj;
+                writeInt(array.length);
+                for (Object o : array) {
+                    writePoolObject(o);
+                }
+            }
+        } else {
+            Graph g = findGraph(graph, obj);
+            if (g == null) {
+                writeByte(PROPERTY_POOL);
+                writePoolObject(obj);
+            } else {
+                writeByte(PROPERTY_SUBGRAPH);
+                writeGraph(g, null);
+            }
+        }
+    }
+
+    private void writeProperties(Graph graph, Map<? extends Object, ? extends Object> props) throws IOException {
+        if (props == null) {
+            writeShort((char) 0);
+            return;
+        }
+        final int size = props.size();
+        // properties
+        writeShort((char) size);
+        int cnt = 0;
+        for (Map.Entry<? extends Object, ? extends Object> entry : props.entrySet()) {
+            String key = entry.getKey().toString();
+            writePoolObject(key);
+            writePropertyObject(graph, entry.getValue());
+            cnt++;
+        }
+        if (size != cnt) {
+            throw new IOException("Expecting " + size + " properties, but found only " + cnt);
+        }
+    }
+
+    private static final class ConstantPool extends LinkedHashMap<Object, Character> {
+
+        private final LinkedList<Character> availableIds;
+        private char nextId;
+        private static final long serialVersionUID = -2676889957907285681L;
+
+        ConstantPool() {
+            super(50, 0.65f);
+            availableIds = new LinkedList<>();
+        }
+
+        @Override
+        protected boolean removeEldestEntry(java.util.Map.Entry<Object, Character> eldest) {
+            if (size() > CONSTANT_POOL_MAX_SIZE) {
+                availableIds.addFirst(eldest.getValue());
+                return true;
+            }
+            return false;
+        }
+
+        private Character nextAvailableId() {
+            if (!availableIds.isEmpty()) {
+                return availableIds.removeFirst();
+            }
+            return nextId++;
+        }
+
+        public char add(Object obj) {
+            Character id = nextAvailableId();
+            put(obj, id);
+            return id;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphStructure.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2011, 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.graphio;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Interface that defines structure of a compiler graph. The structure of a graph is composed from
+ * nodes with properties, the classes of individual nodes, and ports associated with each node that
+ * may contain edges to other nodes. The structure of a graph is assumed to be immutable for the
+ * time of {@link GraphOutput operations} on it.
+ *
+ * @param <G> the type of the (root node of a) graph
+ * @param <N> the type of nodes
+ * @param <C> the type of node classes
+ * @param <P> the type of node ports
+ */
+public interface GraphStructure<G, N, C, P> {
+    /**
+     * Casts the provided object to graph, if possible. If the given object <code>obj</code> can be
+     * seen as a graph or sub-graph of a graph, then return the properly typed instance. Otherwise
+     * return <code>null</code>
+     *
+     * @param currentGraph the currently processed graph
+     * @param obj an object to check and view as a graph
+     * @return appropriate graph object or <code>null</code> if the object doesn't represent a graph
+     */
+    G graph(G currentGraph, Object obj);
+
+    /**
+     * Nodes of a graph. Each graph is composed from a fixed set of nodes. This method returns an
+     * iterable which provides access to all of them - the number of nodes provided by the iterable
+     * must match the number returned by {@link #nodesCount(java.lang.Object)} method.
+     *
+     * @see #nodesCount(java.lang.Object)
+     * @param graph the graph to query for nodes
+     * @return iterable with all the graph's nodes
+     */
+    Iterable<? extends N> nodes(G graph);
+
+    /**
+     * Number of nodes in a graph. The number must match the content returned by
+     * {@link #nodes(java.lang.Object)} method.
+     *
+     * @param graph the graph to query
+     * @return the number of nodes that will be returned by {@link #nodes(java.lang.Object)}
+     */
+    int nodesCount(G graph);
+
+    /**
+     * Id of a node. Each node in the graph is uniquely identified by a integer value. If two nodes
+     * have the same id, then they shall be <code>==</code> to each other.
+     *
+     * @param node the node to query for an id
+     * @return the id of the node
+     */
+    int nodeId(N node);
+
+    /**
+     * Checks if there is a predecessor for a node.
+     *
+     * @param node the node to check
+     * @return <code>true</code> if it has a predecessor, <code>false</code> otherwise
+     */
+    boolean nodeHasPredecessor(N node);
+
+    /**
+     * Collects node properties. Each node can be associated with additional properties identified
+     * by their name. This method shall copy them into the provided map.
+     *
+     * @param graph the current graph
+     * @param node the node to collect properties for
+     * @param properties the map to put the properties to
+     */
+    void nodeProperties(G graph, N node, Map<String, ? super Object> properties);
+
+    /**
+     * Finds the node class for the provided object, if possible. If the given object
+     * <code>obj</code> can be seen as an instance of node class or it is a node in this graph,
+     * return the properly typed instance of the node class. Otherwise return <code>null</code>
+     *
+     * @param obj an object to find node class for
+     * @return appropriate graph object or <code>null</code> if the object doesn't represent a graph
+     */
+    C nodeClass(Object obj);
+
+    /**
+     * The template used to build the name of nodes of this class. The template may use references
+     * to inputs (&#123;i#inputName&#125;) and its properties (&#123;p#propertyName&#125;).
+     *
+     * @param nodeClass the node class to find name template for
+     * @return the string representing the template
+     */
+    String nameTemplate(C nodeClass);
+
+    /**
+     * Java class for a node class.
+     *
+     * @param nodeClass the node class
+     * @return the {@link Class} or other type representation of the node class
+     */
+    Object nodeClassType(C nodeClass);
+
+    /**
+     * Input ports of a node class. Each node class has a fixed set of ports where individual edges
+     * can attach to.
+     *
+     * @param nodeClass the node class
+     * @return input ports for the node class
+     */
+    P portInputs(C nodeClass);
+
+    /**
+     * Output ports of a node class. Each node class has a fixed set of ports from where individual
+     * edges can point to other nodes.
+     *
+     * @param nodeClass the node class
+     * @return output ports for the node class
+     */
+    P portOutputs(C nodeClass);
+
+    /**
+     * The number of edges in a port. The protocol will then call methods
+     * {@link #edgeDirect(java.lang.Object, int)}, {@link #edgeName(java.lang.Object, int)},
+     * {@link #edgeType(java.lang.Object, int)} and
+     * {@link #edgeNodes(java.lang.Object, java.lang.Object, java.lang.Object, int)} for indexes
+     * from <code>0</code> to <code>portSize - 1</code>
+     *
+     * @param port the port
+     * @return number of edges in this port
+     */
+    int portSize(P port);
+
+    /**
+     * Checks whether an edge is direct. Direct edge shall have exactly one
+     * {@linkplain #edgeNodes(java.lang.Object, java.lang.Object, java.lang.Object, int) node} - it
+     * is an error to return more than one for such an edge from the
+     * {@linkplain #edgeNodes(java.lang.Object, java.lang.Object, java.lang.Object, int) method}.
+     *
+     * @param port the port
+     * @param index index from <code>0</code> to {@link #portSize(java.lang.Object)} minus
+     *            <code>1</code>
+     * @return <code>true</code> if only one node can be returned from
+     *         {@link #edgeNodes(java.lang.Object, java.lang.Object, java.lang.Object, int)} method
+     */
+    boolean edgeDirect(P port, int index);
+
+    /**
+     * The name of an edge.
+     *
+     * @param port the port
+     * @param index index from <code>0</code> to {@link #portSize(java.lang.Object)} minus
+     *            <code>1</code>
+     * @return the name of the edge
+     */
+    String edgeName(P port, int index);
+
+    /**
+     * Type of an edge. The type must be a graph
+     * <q>enum</q> - e.g. either real instance of {@link Enum} subclass, or something that the
+     * {@link GraphOutput.Builder} can recognize as
+     * <q>enum</q>.
+     *
+     * @param port
+     * @param index index from <code>0</code> to {@link #portSize(java.lang.Object)} minus
+     *            <code>1</code>
+     * @return any {@link Enum} representing type of the edge
+     */
+    Object edgeType(P port, int index);
+
+    /**
+     * Nodes where the edges for a port lead to/from. This method is called for both
+     * {@link #edgeDirect(java.lang.Object, int) direct/non-direct edges}. In case of a direct edge
+     * the returned collection must have exactly one element.
+     *
+     * @param graph the graph
+     * @param node the node in the graph
+     * @param port port of the node class
+     * @param index index from <code>0</code> to {@link #portSize(java.lang.Object)} minus
+     *            <code>1</code>
+     * @return <code>null</code> if there are no edges associated with given port or collection of
+     *         nodes where to/from the edges lead to
+     */
+    Collection<? extends N> edgeNodes(G graph, N node, P port, int index);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphTypes.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2011, 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.graphio;
+
+/**
+ * Special support for dealing with enums. Normally one can represent various {@link GraphOutput
+ * graph} enum values with real {@link Enum} instances. In case this is not possible, the
+ * {@link GraphOutput.Builder} allows one to
+ * {@link GraphOutput.Builder#types(org.graalvm.graphio.GraphTypes) register} an implementation of
+ * this interface to treat them specially.
+ */
+public interface GraphTypes {
+    /**
+     * Recognizes an
+     * <q>enum</q> object. If the <code>enumValue</code> object represents an enum, then an object
+     * that represents its class shall be returned.
+     *
+     * @param enumValue the value to test
+     * @return <code>null</code> if the value isn't enum, otherwise its class
+     */
+    Object enumClass(Object enumValue);
+
+    /**
+     * Ordinal of an enum. If the <code>obj</code> represents an enum, then return its ordinal
+     * number otherwise return <code>-1</code>
+     *
+     * @param obj the value to test
+     * @return <code>-1</code> if the obj isn't enum, otherwise its ordinal number
+     */
+    int enumOrdinal(Object obj);
+
+    /**
+     * All possible values of an enum. If the provided <code>maybeEnumClass</code> object represents
+     * an enum, then compute enum value names in ordinal order and return them as a string array.
+     * Otherwise return <code>null</code>
+     *
+     * @param maybeEnumClass the class to test
+     * @return <code>null</code> if the clazz isn't an enum, otherwise names of its values
+     */
+    String[] enumTypeValues(Object maybeEnumClass);
+
+    /**
+     * Finds Java type name for a given class.
+     *
+     * @param maybeClass object representing the class
+     * @return the type name of the class or <code>null</code> if the parameter doesn't represent a
+     *         class
+     */
+    String typeName(Object maybeClass);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/ProtocolImpl.java	Fri Aug 04 19:59:33 2017 -0700
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2011, 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.graphio;
+
+import java.io.IOException;
+import java.nio.channels.WritableByteChannel;
+import java.util.Collection;
+import java.util.Map;
+
+final class ProtocolImpl<Graph, Node, NodeClass, Port, Block, ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition>
+                extends GraphProtocol<Graph, Node, NodeClass, Port, Block, ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition> {
+    private final GraphStructure<Graph, Node, NodeClass, Port> structure;
+    private final GraphTypes types;
+    private final GraphBlocks<Graph, Block, Node> blocks;
+    private final GraphElements<ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition> elements;
+
+    ProtocolImpl(GraphStructure<Graph, Node, NodeClass, Port> structure, GraphTypes enums, GraphBlocks<Graph, Block, Node> blocks,
+                    GraphElements<ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition> elements, WritableByteChannel channel) throws IOException {
+        super(channel);
+        this.structure = structure;
+        this.types = enums;
+        this.blocks = blocks;
+        this.elements = elements;
+    }
+
+    @Override
+    protected Graph findGraph(Graph current, Object obj) {
+        return structure.graph(current, obj);
+    }
+
+    @Override
+    protected NodeClass findNodeClass(Object obj) {
+        return structure.nodeClass(obj);
+    }
+
+    @Override
+    protected String findNameTemplate(NodeClass clazz) {
+        return structure.nameTemplate(clazz);
+    }
+
+    @Override
+    protected int findNodeId(Node n) {
+        return structure.nodeId(n);
+    }
+
+    @Override
+    protected boolean hasPredecessor(Node node) {
+        return structure.nodeHasPredecessor(node);
+    }
+
+    @Override
+    protected int findNodesCount(Graph info) {
+        return structure.nodesCount(info);
+    }
+
+    @Override
+    protected Iterable<? extends Node> findNodes(Graph info) {
+        return structure.nodes(info);
+    }
+
+    @Override
+    protected void findNodeProperties(Node node, Map<String, Object> props, Graph info) {
+        structure.nodeProperties(info, node, props);
+    }
+
+    @Override
+    protected Port findClassEdges(NodeClass nodeClass, boolean dumpInputs) {
+        if (dumpInputs) {
+            return structure.portInputs(nodeClass);
+        } else {
+            return structure.portOutputs(nodeClass);
+        }
+    }
+
+    @Override
+    protected int findSize(Port edges) {
+        return structure.portSize(edges);
+    }
+
+    @Override
+    protected boolean isDirect(Port edges, int i) {
+        return structure.edgeDirect(edges, i);
+    }
+
+    @Override
+    protected String findName(Port edges, int i) {
+        return structure.edgeName(edges, i);
+    }
+
+    @Override
+    protected Object findType(Port edges, int i) {
+        return structure.edgeType(edges, i);
+    }
+
+    @Override
+    protected Collection<? extends Node> findNodes(Graph graph, Node node, Port port, int i) {
+        return structure.edgeNodes(graph, node, port, i);
+    }
+
+    @Override
+    protected Object findJavaClass(NodeClass clazz) {
+        return structure.nodeClassType(clazz);
+    }
+
+    @Override
+    protected Object findEnumClass(Object enumValue) {
+        return types.enumClass(enumValue);
+    }
+
+    @Override
+    protected int findEnumOrdinal(Object obj) {
+        return types.enumOrdinal(obj);
+    }
+
+    @Override
+    protected String[] findEnumTypeValues(Object clazz) {
+        return types.enumTypeValues(clazz);
+    }
+
+    @Override
+    protected String findJavaTypeName(Object obj) {
+        return types.typeName(obj);
+    }
+
+    @Override
+    protected Collection<? extends Node> findBlockNodes(Graph info, Block block) {
+        return blocks.blockNodes(info, block);
+    }
+
+    @Override
+    protected int findBlockId(Block block) {
+        return blocks.blockId(block);
+    }
+
+    @Override
+    protected Collection<? extends Block> findBlocks(Graph graph) {
+        return blocks.blocks(graph);
+    }
+
+    @Override
+    protected Collection<? extends Block> findBlockSuccessors(Block block) {
+        return blocks.blockSuccessors(block);
+    }
+
+    @Override
+    protected ResolvedJavaMethod findMethod(Object obj) {
+        return elements == null ? null : elements.method(obj);
+    }
+
+    @Override
+    protected byte[] findMethodCode(ResolvedJavaMethod method) {
+        return elements.methodCode(method);
+    }
+
+    @Override
+    protected int findMethodModifiers(ResolvedJavaMethod method) {
+        return elements.methodModifiers(method);
+    }
+
+    @Override
+    protected Signature findMethodSignature(ResolvedJavaMethod method) {
+        return elements.methodSignature(method);
+    }
+
+    @Override
+    protected String findMethodName(ResolvedJavaMethod method) {
+        return elements.methodName(method);
+    }
+
+    @Override
+    protected Object findMethodDeclaringClass(ResolvedJavaMethod method) {
+        return elements.methodDeclaringClass(method);
+    }
+
+    @Override
+    protected int findFieldModifiers(ResolvedJavaField field) {
+        return elements.fieldModifiers(field);
+    }
+
+    @Override
+    protected String findFieldTypeName(ResolvedJavaField field) {
+        return elements.fieldTypeName(field);
+    }
+
+    @Override
+    protected String findFieldName(ResolvedJavaField field) {
+        return elements.fieldName(field);
+    }
+
+    @Override
+    protected Object findFieldDeclaringClass(ResolvedJavaField field) {
+        return elements.fieldDeclaringClass(field);
+    }
+
+    @Override
+    protected ResolvedJavaField findJavaField(Object object) {
+        return elements == null ? null : elements.field(object);
+    }
+
+    @Override
+    protected Signature findSignature(Object object) {
+        return elements == null ? null : elements.signature(object);
+    }
+
+    @Override
+    protected int findSignatureParameterCount(Signature signature) {
+        return elements.signatureParameterCount(signature);
+    }
+
+    @Override
+    protected String findSignatureParameterTypeName(Signature signature, int index) {
+        return elements.signatureParameterTypeName(signature, index);
+    }
+
+    @Override
+    protected String findSignatureReturnTypeName(Signature signature) {
+        return elements.signatureReturnTypeName(signature);
+    }
+
+    @Override
+    protected NodeSourcePosition findNodeSourcePosition(Object object) {
+        return elements == null ? null : elements.nodeSourcePosition(object);
+    }
+
+    @Override
+    protected ResolvedJavaMethod findNodeSourcePositionMethod(NodeSourcePosition pos) {
+        return elements.nodeSourcePositionMethod(pos);
+    }
+
+    @Override
+    protected NodeSourcePosition findNodeSourcePositionCaller(NodeSourcePosition pos) {
+        return elements.nodeSourcePositionCaller(pos);
+    }
+
+    @Override
+    protected int findNodeSourcePositionBCI(NodeSourcePosition pos) {
+        return elements.nodeSourcePositionBCI(pos);
+    }
+
+    @Override
+    protected StackTraceElement findMethodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos) {
+        return elements.methodStackTraceElement(method, bci, pos);
+    }
+
+    @Override
+    protected void findExtraNodes(Node node, Collection<? super Node> extraNodes) {
+    }
+
+    @Override
+    protected String formatTitle(Graph graph, int id, String format, Object... args) {
+        return String.format(format, args) + " [" + id + "]";
+    }
+}
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java	Sat Aug 05 03:05:06 2017 +0200
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java	Fri Aug 04 19:59:33 2017 -0700
@@ -151,15 +151,17 @@
     public static <T> Builder newBuilder(OptionKey<T> key, String name) {
         Objects.requireNonNull(key);
         Objects.requireNonNull(name);
-        return new Builder(key, name);
+        return EMPTY.new Builder(key, name);
     }
 
+    private static final OptionDescriptor EMPTY = new OptionDescriptor(null, null, null, null, false);
+
     /**
      * Represents an option descriptor builder.
      *
      * @since 1.0
      */
-    public static final class Builder {
+    public final class Builder {
 
         private final OptionKey<?> key;
         private final String name;