Merge unixdomainchannels
authormichaelm
Mon, 04 Nov 2019 11:25:55 +0000
branchunixdomainchannels
changeset 58911 2c777f25cfff
parent 58857 12b08b510fd0 (current diff)
parent 58910 f61eea1869e4 (diff)
child 58933 57a2e3ed90ec
Merge
make/GensrcModuleInfo.gmk
src/hotspot/share/jfr/recorder/repository/jfrChunkState.cpp
src/hotspot/share/jfr/recorder/repository/jfrChunkState.hpp
src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java
src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/MethodSubstitutionRegistry.java
src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/MemoryScheduleVerification.java
src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/ExportingClassLoader.java
src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/ModuleSupport.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/ChunkParser.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/ConstantMap.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/LongMap.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectFactory.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/Parser.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/ParserFactory.java
src/jdk.jfr/share/classes/jdk/jfr/consumer/TimeConverter.java
src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInternals.java
test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java
test/jdk/java/nio/channels/DatagramChannel/AdaptDatagramSocket.java
--- a/.hgtags	Wed Oct 30 12:25:22 2019 +0000
+++ b/.hgtags	Mon Nov 04 11:25:55 2019 +0000
@@ -593,3 +593,4 @@
 e84d8379815ba0d3e50fb096d28c25894cb50b8c jdk-14+18
 9b67dd88a9313e982ec5f710a7747161bc8f0c23 jdk-14+19
 54ffb15c48399dd59922ee22bb592d815307e77c jdk-14+20
+c16ac7a2eba4e73cb4f7ee9294dd647860eebff0 jdk-14+21
--- a/doc/building.html	Wed Oct 30 12:25:22 2019 +0000
+++ b/doc/building.html	Mon Nov 04 11:25:55 2019 +0000
@@ -385,10 +385,10 @@
 <p>On Linux you can also get a JDK from the Linux distribution. On apt-based distros (like Debian and Ubuntu), <code>sudo apt-get install openjdk-&lt;VERSION&gt;-jdk</code> is typically enough to install a JDK &lt;VERSION&gt;. On rpm-based distros (like Fedora and Red Hat), try <code>sudo yum install java-&lt;VERSION&gt;-openjdk-devel</code>.</p>
 <h2 id="external-library-requirements">External Library Requirements</h2>
 <p>Different platforms require different external libraries. In general, libraries are not optional - that is, they are either required or not used.</p>
-<p>If a required library is not detected by <code>configure</code>, you need to provide the path to it. There are two forms of the <code>configure</code> arguments to point to an external library: <code>--with-&lt;LIB&gt;=&lt;path&gt;</code> or <code>--with-&lt;LIB&gt;-include=&lt;path to include&gt; --with-&lt;LIB&gt;-lib=&lt;path to lib&gt;</code>. The first variant is more concise, but require the include files an library files to reside in a default hierarchy under this directory. In most cases, it works fine.</p>
+<p>If a required library is not detected by <code>configure</code>, you need to provide the path to it. There are two forms of the <code>configure</code> arguments to point to an external library: <code>--with-&lt;LIB&gt;=&lt;path&gt;</code> or <code>--with-&lt;LIB&gt;-include=&lt;path to include&gt; --with-&lt;LIB&gt;-lib=&lt;path to lib&gt;</code>. The first variant is more concise, but require the include files and library files to reside in a default hierarchy under this directory. In most cases, it works fine.</p>
 <p>As a fallback, the second version allows you to point to the include directory and the lib directory separately.</p>
 <h3 id="freetype">FreeType</h3>
-<p>FreeType2 from <a href="http://www.freetype.org/">The FreeType Project</a> is not required on any platform. The exception is on Unix-based platforms when configuring such that the build artifacts will reference a system installed library, rather than bundling the JDK’s own copy.</p>
+<p>FreeType2 from <a href="http://www.freetype.org/">The FreeType Project</a> is not required on any platform. The exception is on Unix-based platforms when configuring such that the build artifacts will reference a system installed library, rather than bundling the JDK's own copy.</p>
 <ul>
 <li>To install on an apt-based Linux, try running <code>sudo apt-get install libfreetype6-dev</code>.</li>
 <li>To install on an rpm-based Linux, try running <code>sudo yum install freetype-devel</code>.</li>
@@ -449,7 +449,7 @@
 <p>To build the JDK, you need a &quot;configuration&quot;, which consists of a directory where to store the build output, coupled with information about the platform, the specific build machine, and choices that affect how the JDK is built.</p>
 <p>The configuration is created by the <code>configure</code> script. The basic invocation of the <code>configure</code> script looks like this:</p>
 <pre><code>bash configure [options]</code></pre>
-<p>This will create an output directory containing the configuration and setup an area for the build result. This directory typically looks like <code>build/linux-x64-normal-server-release</code>, but the actual name depends on your specific configuration. (It can also be set directly, see <a href="#using-multiple-configurations">Using Multiple Configurations</a>). This directory is referred to as <code>$BUILD</code> in this documentation.</p>
+<p>This will create an output directory containing the configuration and setup an area for the build result. This directory typically looks like <code>build/linux-x64-server-release</code>, but the actual name depends on your specific configuration. (It can also be set directly, see <a href="#using-multiple-configurations">Using Multiple Configurations</a>). This directory is referred to as <code>$BUILD</code> in this documentation.</p>
 <p><code>configure</code> will try to figure out what system you are running on and where all necessary build components are. If you have all prerequisites for building installed, it should find everything. If it fails to detect any component automatically, it will exit and inform you about the problem.</p>
 <p>Some command line examples:</p>
 <ul>
--- a/doc/building.md	Wed Oct 30 12:25:22 2019 +0000
+++ b/doc/building.md	Mon Nov 04 11:25:55 2019 +0000
@@ -473,8 +473,8 @@
 path to it. There are two forms of the `configure` arguments to point to an
 external library: `--with-<LIB>=<path>` or `--with-<LIB>-include=<path to
 include> --with-<LIB>-lib=<path to lib>`. The first variant is more concise,
-but require the include files an library files to reside in a default hierarchy
-under this directory. In most cases, it works fine.
+but require the include files and library files to reside in a default
+hierarchy under this directory. In most cases, it works fine.
 
 As a fallback, the second version allows you to point to the include directory
 and the lib directory separately.
@@ -484,7 +484,7 @@
 FreeType2 from [The FreeType Project](http://www.freetype.org/) is not required
 on any platform. The exception is on Unix-based platforms when configuring such
 that the build artifacts will reference a system installed library,
-rather than bundling the JDK’s own copy.
+rather than bundling the JDK's own copy.
 
   * To install on an apt-based Linux, try running `sudo apt-get install
     libfreetype6-dev`.
@@ -623,8 +623,8 @@
 
 This will create an output directory containing the configuration and setup an
 area for the build result. This directory typically looks like
-`build/linux-x64-normal-server-release`, but the actual name depends on your
-specific configuration. (It can also be set directly, see [Using Multiple
+`build/linux-x64-server-release`, but the actual name depends on your specific
+configuration. (It can also be set directly, see [Using Multiple
 Configurations](#using-multiple-configurations)). This directory is referred to
 as `$BUILD` in this documentation.
 
--- a/make/Bundles.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/Bundles.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -314,6 +314,27 @@
 
 ################################################################################
 
+ifneq ($(filter static-libs-bundles, $(MAKECMDGOALS)), )
+  STATIC_LIBS_BUNDLE_FILES := $(call FindFiles, $(STATIC_LIBS_IMAGE_DIR))
+
+  ifeq ($(OPENJDK_TARGET_OS)-$(DEBUG_LEVEL), macosx-release)
+    STATIC_LIBS_BUNDLE_SUBDIR := $(JDK_MACOSX_CONTENTS_SUBDIR)/Home
+  else
+    STATIC_LIBS_BUNDLE_SUBDIR := $(JDK_BUNDLE_SUBDIR)
+  endif
+
+  $(eval $(call SetupBundleFile, BUILD_STATIC_LIBS_BUNDLE, \
+      BUNDLE_NAME := $(STATIC_LIBS_BUNDLE_NAME), \
+      FILES := $(STATIC_LIBS_BUNDLE_FILES), \
+      BASE_DIRS := $(STATIC_LIBS_IMAGE_DIR), \
+      SUBDIR := $(STATIC_LIBS_BUNDLE_SUBDIR), \
+  ))
+
+  STATIC_LIBS_TARGETS += $(BUILD_STATIC_LIBS_BUNDLE)
+endif
+
+################################################################################
+
 # Hook to include the corresponding custom file, if present.
 $(eval $(call IncludeCustomExtension, Bundles.gmk))
 
@@ -323,6 +344,8 @@
 legacy-bundles: $(LEGACY_TARGETS)
 test-bundles: $(TEST_TARGETS)
 docs-bundles: $(DOCS_TARGETS)
+static-libs-bundles: $(STATIC_LIBS_TARGETS)
 jcov-bundles: $(JCOV_TARGETS)
 
-.PHONY: all default product-bundles test-bundles docs-bundles jcov-bundles
+.PHONY: all default product-bundles test-bundles docs-bundles \
+    static-libs-bundles jcov-bundles
--- a/make/CompileToolsJdk.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/CompileToolsJdk.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/Coverage.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/Coverage.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2019, 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
--- a/make/GenerateLinkOptData.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/GenerateLinkOptData.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, 2019, 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
--- a/make/GensrcModuleInfo.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-#
-# Copyright (c) 2015, 2018, 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.  Oracle designates this
-# particular file as subject to the "Classpath" exception as provided
-# by Oracle in the LICENSE file that accompanied this code.
-#
-# 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.
-#
-
-################################################################################
-# This file makes modifications to module-info.java files based on the build
-# configuration.
-#
-# Depending on build platform, imported modules and optional parts of the build
-# being active, some modules need to have extra exports, provides or uses
-# declarations added to them. These optional extras are defined in .extra files:
-#
-# src/<module>/<share,platform>/classes/module-info.java.extra
-#
-# The contents of the .extra files are simply extra lines that could fit into
-# the module-info file.
-#
-# This makefile is called once for each from-module with the variable
-# MODULE naming the from-module.
-#
-# The modified module-info.java files are put in the gensrc directory where
-# they will automatically override the static versions in the src tree.
-#
-################################################################################
-
-default: all
-
-include $(SPEC)
-include MakeBase.gmk
-include Modules.gmk
-
-################################################################################
-# Define this here since jdk/make/Tools.gmk cannot be included from the top
-# make directory. Should probably move some tools away from the jdk repo.
-TOOL_GENMODULEINFOSOURCE = $(JAVA_SMALL) \
-    $(INTERIM_LANGTOOLS_ARGS) \
-    -cp "$(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes" \
-    build.tools.module.GenModuleInfoSource
-
-################################################################################
-
-# Name of modification file.
-MOD_FILENAME := module-info.java.extra
-
-# Construct all possible src directories for the module.
-MODULE_CLASSES_DIRS := $(call FindModuleSrcDirs, $(MODULE))
-
-# Find all the .extra files in the src dirs.
-MOD_FILES := $(wildcard $(foreach f, $(MOD_FILENAME), $(addsuffix /$(f), \
-    $(MODULE_CLASSES_DIRS))))
-
-ifneq ($(MOD_FILES), )
-  # Only make this call if modification files are found for this module
-  ALL_MODULES := $(call FindAllModules)
-
-  $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/module-info.java: \
-      $(firstword $(call FindAllModuleInfos, $(MODULE))) \
-      $(BUILD_TOOLS_JDK) \
-      $(MOD_FILES) \
-      $(call DependOnVariable, ALL_MODULES)
-		$(call MakeTargetDir)
-		$(RM) $@ $@.tmp
-		$(TOOL_GENMODULEINFOSOURCE) -o $@.tmp \
-		    --source-file $< \
-		    --modules $(call CommaList, $(ALL_MODULES)) \
-		    $(MOD_FILES)
-		$(MV) $@.tmp $@
-
-  TARGETS += $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/module-info.java
-
-else
-  # If no modifications are found for this module, remove any module-info.java
-  # created by a previous build since that is no longer valid.
-  ifneq ($(wildcard $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/module-info.java), )
-    $(shell $(RM) $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/module-info.java)
-  endif
-endif
-
-################################################################################
-
-all: $(TARGETS)
--- a/make/Help.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/Help.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -43,7 +43,7 @@
 	$(info $(_) make images            # Create a complete jdk image)
 	$(info $(_)                        # (alias for product-images))
 	$(info $(_) make <name>-image      # Build just the image for any of: )
-	$(info $(_)                        # jdk, test, docs, symbols, legacy-jre)
+	$(info $(_)                        # jdk, test, docs, symbols, legacy-jre, static-libs)
 	$(info $(_) make <phase>           # Build the specified phase and everything it depends on)
 	$(info $(_)                        # (gensrc, java, copy, libs, launchers, gendata, rmic))
 	$(info $(_) make *-only            # Applies to most targets and disables building the)
--- a/make/Main.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/Main.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -77,7 +77,7 @@
 
 interim-cldrconverter:
 	+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f CopyInterimCLDRConverter.gmk)
- 
+
 interim-tzdb:
 	+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f CopyInterimTZDB.gmk)
 
@@ -133,7 +133,7 @@
 define DeclareModuleInfoRecipe
   $1-gensrc-moduleinfo:
 	+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) \
-	    -f GensrcModuleInfo.gmk MODULE=$1)
+	    -f gensrc/GensrcModuleInfo.gmk MODULE=$1)
 
   $1-gensrc: $1-gensrc-moduleinfo
 endef
@@ -218,6 +218,21 @@
 ALL_TARGETS += $(LIBS_TARGETS)
 
 ################################################################################
+# Targets for compiling static versions of certain native libraries. These do
+# not end up in the jmods or the normal JDK image, but are instead bundled into
+# a special deliverable.
+$(eval $(call DeclareRecipesForPhase, STATIC_LIBS, \
+    TARGET_SUFFIX := static-libs, \
+    FILE_PREFIX := Lib, \
+    MAKE_SUBDIR := lib, \
+    CHECK_MODULES := $(STATIC_LIBS_MODULES), \
+    USE_WRAPPER := true, \
+    EXTRA_ARGS := STATIC_LIBS=true, \
+))
+
+ALL_TARGETS += $(STATIC_LIBS_TARGETS)
+
+################################################################################
 # Targets for compiling native executables
 $(eval $(call DeclareRecipesForPhase, LAUNCHER, \
     TARGET_SUFFIX := launchers, \
@@ -377,6 +392,9 @@
 symbols-image:
 	+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f Images.gmk symbols)
 
+static-libs-image:
+	+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f StaticLibsImage.gmk)
+
 mac-jdk-bundle:
 	+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f MacBundles.gmk jdk-bundle)
 
@@ -396,7 +414,7 @@
 
 ALL_TARGETS += store-source-revision create-source-revision-tracker bootcycle-images zip-security \
     zip-source jrtfs-jar jdk-image legacy-jre-image \
-    symbols-image mac-jdk-bundle mac-legacy-jre-bundle \
+    symbols-image static-libs-image mac-jdk-bundle mac-legacy-jre-bundle \
     release-file exploded-image-optimize jcov-image
 
 ################################################################################
@@ -614,12 +632,16 @@
 docs-bundles:
 	+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f Bundles.gmk docs-bundles)
 
+static-libs-bundles:
+	+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f Bundles.gmk static-libs-bundles)
+
 ifeq ($(JCOV_ENABLED), true)
   jcov-bundles:
 	+($(CD) $(TOPDIR)/make && $(MAKE) $(MAKE_ARGS) -f Bundles.gmk jcov-bundles)
 endif
 
-ALL_TARGETS += product-bundles legacy-bundles test-bundles docs-bundles jcov-bundles
+ALL_TARGETS += product-bundles legacy-bundles test-bundles docs-bundles \
+    static-libs-bundles jcov-bundles
 
 ################################################################################
 # Install targets
@@ -796,6 +818,10 @@
   vscode-project-rtags: compile-commands
   vscode-project-ccls: compile-commands
 
+  # The -static-libs targets depend on -java as well as java.base-copy.
+  $(foreach m, $(filter $(JAVA_MODULES), $(STATIC_LIBS_MODULES)), \
+    $(eval $m-static-libs: $m-java java.base-copy))
+
   # Jmods cannot be created until we have the jmod tool ready to run. During
   # a normal build we run it from the exploded image, but when cross compiling
   # it's run from the buildjdk, which is either created at build time or user
@@ -858,6 +884,8 @@
   legacy-jre-image: jmods release-file
   symbols-image: $(LIBS_TARGETS) $(LAUNCHER_TARGETS)
 
+  static-libs-image: $(STATIC_LIBS_TARGETS)
+
   mac-jdk-bundle: jdk-image
   mac-legacy-jre-bundle: legacy-jre-image
 
@@ -946,6 +974,8 @@
 
   jcov-bundles: jcov-image
 
+  static-libs-bundles: static-libs-image
+
   generate-summary: jmods buildtools-modules
 
   update-x11wrappers: java.base-copy buildtools-jdk
@@ -984,6 +1014,8 @@
 
 libs: $(LIBS_TARGETS)
 
+static-libs: $(STATIC_LIBS_TARGETS)
+
 launchers: $(LAUNCHER_TARGETS)
 
 jmods: $(JMOD_TARGETS)
@@ -1094,10 +1126,10 @@
 all-images: product-images test-image docs-image
 
 # all-bundles packages all our deliverables as tar.gz bundles.
-all-bundles: product-bundles test-bundles docs-bundles
+all-bundles: product-bundles test-bundles docs-bundles static-libs-bundles
 
 ALL_TARGETS += buildtools hotspot hotspot-libs hotspot-gensrc gensrc gendata \
-    copy java rmic libs launchers jmods \
+    copy java rmic libs static-libs launchers jmods \
     jdk.jdwp.agent-gensrc $(ALL_MODULES) demos \
     exploded-image-base exploded-image \
     create-buildjdk docs-jdk-api docs-javase-api docs-reference-api docs-jdk \
--- a/make/MainSupport.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/MainSupport.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
@@ -133,7 +133,7 @@
 	      $$(addprefix -I, $$(PHASE_MAKEDIRS) \
 	          $$(addsuffix /$$($1_MAKE_SUBDIR), $$(PHASE_MAKEDIRS)) \
 	      ) \
-	      MODULE=$2 MAKEFILE_PREFIX=$$($1_FILE_PREFIX))
+	      MODULE=$2 MAKEFILE_PREFIX=$$($1_FILE_PREFIX) $$($1_EXTRA_ARGS))
         else
 	  +($(CD) $$(dir $$(firstword $$(wildcard $$(addsuffix \
 	      /$$($1_MAKE_SUBDIR)/$$($1_FILE_PREFIX)-$2.gmk, $$(PHASE_MAKEDIRS))))) \
@@ -142,7 +142,7 @@
 	      $$(addprefix -I, $$(PHASE_MAKEDIRS) \
 	          $$(addsuffix /$$($1_MAKE_SUBDIR), $$(PHASE_MAKEDIRS)) \
 	      ) \
-	      MODULE=$2 \
+	      MODULE=$2 $$($1_EXTRA_ARGS) \
 	  )
         endif
 
@@ -182,12 +182,13 @@
 # CHECK_MODULES : List of modules to try
 # MULTIPLE_MAKEFILES : Set to true to handle makefiles for the same module and
 #                      phase in multiple repos
+# EXTRA_ARGS : Add extra make args to each makefile call
 # Exported variables:
 # $1_MODULES : All modules that had rules generated
 # $1_TARGETS : All targets generated
 define DeclareRecipesForPhase
-  $(foreach i,2 3 4 5 6 7, $(if $(strip $($i)),$(strip $1)_$(strip $($i)))$(NEWLINE))
-  $(if $(8),$(error Internal makefile error: Too many arguments to \
+  $(foreach i,2 3 4 5 6 7 8, $(if $(strip $($i)),$(strip $1)_$(strip $($i)))$(NEWLINE))
+  $(if $(9),$(error Internal makefile error: Too many arguments to \
       DeclareRecipesForPhase, please update MakeHelper.gmk))
 
   $$(foreach m, $$($(strip $1)_CHECK_MODULES), \
--- a/make/ModuleWrapper.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/ModuleWrapper.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, 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
--- a/make/RunTestsPrebuiltSpec.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/RunTestsPrebuiltSpec.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 2019, 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/StaticLibsImage.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,54 @@
+#
+# Copyright (c) 2019, 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# 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.
+#
+
+# This makefile creates an image of the optional static versions of certain JDK
+# libraries.
+
+default: all
+
+include $(SPEC)
+include MakeBase.gmk
+include Modules.gmk
+
+################################################################################
+
+TARGETS :=
+
+$(foreach m, $(STATIC_LIBS_MODULES), \
+  $(eval $(call SetupCopyFiles, COPY_STATIC_LIBS_$m, \
+      FLATTEN := true, \
+      SRC := $(SUPPORT_OUTPUTDIR)/native/$m, \
+      DEST := $(STATIC_LIBS_IMAGE_DIR)/lib, \
+      FILES := $(filter %$(STATIC_LIBRARY_SUFFIX), \
+          $(call FindFiles, $(SUPPORT_OUTPUTDIR)/native/$m/*/static)), \
+  )) \
+  $(eval TARGETS += $$(COPY_STATIC_LIBS_$m)) \
+)
+
+################################################################################
+
+all: $(TARGETS)
+
+.PHONY: all
--- a/make/TestImage.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/TestImage.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 2019, 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
--- a/make/UpdateBuildDocs.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/UpdateBuildDocs.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 2019, 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
@@ -45,29 +45,13 @@
 
 DOCS_DIR := $(TOPDIR)/doc
 
-$(eval $(call SetupProcessMarkdown, building, \
-  FILES := $(DOCS_DIR)/building.md, \
+$(eval $(call SetupProcessMarkdown, md_docs, \
+  FILES := $(call FindFiles, $(DOCS_DIR), *.md), \
   DEST := $(DOCS_DIR), \
   CSS := $(GLOBAL_SPECS_DEFAULT_CSS_FILE), \
   OPTIONS := --toc, \
 ))
-TARGETS += $(building)
-
-$(eval $(call SetupProcessMarkdown, testing, \
-  FILES := $(DOCS_DIR)/testing.md, \
-  DEST := $(DOCS_DIR), \
-  CSS := $(GLOBAL_SPECS_DEFAULT_CSS_FILE), \
-  OPTIONS := --toc, \
-))
-TARGETS += $(testing)
-
-$(eval $(call SetupProcessMarkdown, ide, \
-  FILES := $(DOCS_DIR)/ide.md, \
-  DEST := $(DOCS_DIR), \
-  CSS := $(GLOBAL_SPECS_DEFAULT_CSS_FILE), \
-  OPTIONS := --toc, \
-))
-TARGETS += $(ide)
+TARGETS += $(md_docs)
 
 ################################################################################
 
--- a/make/ZipSecurity.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/ZipSecurity.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, 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
--- a/make/autoconf/configure.ac	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/autoconf/configure.ac	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
-SRC#
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+#
+# Copyright (c) 2011, 2019, 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
--- a/make/autoconf/flags-cflags.m4	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/autoconf/flags-cflags.m4	Mon Nov 04 11:25:55 2019 +0000
@@ -597,7 +597,7 @@
     LANGSTD_CFLAGS="-xc99=all,no_lib"
   elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then
     # MSVC doesn't support C99/C11 explicitly, unless you compile as C++:
-    # LANGSTD_CFLAGS="/TP"
+    # LANGSTD_CFLAGS="-TP"
     # but that requires numerous changes to the sources files. So we are limited
     # to C89/C90 plus whatever extensions Visual Studio has decided to implement.
     # This is the lowest bar for shared code.
@@ -694,6 +694,20 @@
       OS_CFLAGS_JVM="$OS_CFLAGS_JVM -DNEEDS_LIBRT"
     fi
   fi
+
+  # Extra flags needed when building optional static versions of certain
+  # JDK libraries.
+  STATIC_LIBS_CFLAGS="-DSTATIC_BUILD=1"
+  if test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang; then
+    STATIC_LIBS_CFLAGS="$STATIC_LIBS_CFLAGS -ffunction-sections -fdata-sections"
+  fi
+  if test "x$TOOLCHAIN_TYPE" = xgcc; then
+    # Disable relax-relocation to enable compatibility with older linkers
+    RELAX_RELOCATIONS_FLAG="-Xassembler -mrelax-relocations=no"
+    FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${RELAX_RELOCATIONS_FLAG}],
+        IF_TRUE: [STATIC_LIBS_CFLAGS="$STATIC_LIBS_CFLAGS ${RELAX_RELOCATIONS_FLAG}"])
+  fi
+  AC_SUBST(STATIC_LIBS_CFLAGS)
 ])
 
 ################################################################################
--- a/make/autoconf/jdk-version.m4	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/autoconf/jdk-version.m4	Mon Nov 04 11:25:55 2019 +0000
@@ -162,7 +162,9 @@
     AC_MSG_ERROR([--with-vendor-vm-bug-url must have a value])
   elif [ ! [[ $with_vendor_vm_bug_url =~ ^[[:print:]]*$ ]] ]; then
     AC_MSG_ERROR([--with-vendor-vm-bug-url contains non-printing characters: $with_vendor_vm_bug_url])
-  else
+  elif test "x$with_vendor_vm_bug_url" != x; then
+    # Only set VENDOR_URL_VM_BUG if '--with-vendor-vm-bug-url' was used and is not empty.
+    # Otherwise we will use the value from "version-numbers" included above.
     VENDOR_URL_VM_BUG="$with_vendor_vm_bug_url"
   fi
   AC_SUBST(VENDOR_URL_VM_BUG)
--- a/make/autoconf/spec.gmk.in	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/autoconf/spec.gmk.in	Mon Nov 04 11:25:55 2019 +0000
@@ -376,6 +376,8 @@
 LIBFFI_LIB_FILE:=@LIBFFI_LIB_FILE@
 GRAALUNIT_LIB := @GRAALUNIT_LIB@
 
+STATIC_LIBS_CFLAGS := @STATIC_LIBS_CFLAGS@
+
 JMH_CORE_JAR := @JMH_CORE_JAR@
 JMH_GENERATOR_JAR := @JMH_GENERATOR_JAR@
 JMH_JOPT_SIMPLE_JAR := @JMH_JOPT_SIMPLE_JAR@
@@ -892,6 +894,10 @@
 # Output docs directly into image
 DOCS_OUTPUTDIR := $(DOCS_IMAGE_DIR)
 
+# Static libs image
+STATIC_LIBS_IMAGE_SUBDIR := static-libs
+STATIC_LIBS_IMAGE_DIR := $(IMAGES_OUTPUTDIR)/$(STATIC_LIBS_IMAGE_SUBDIR)
+
 # Macosx bundles directory definitions
 JDK_MACOSX_BUNDLE_SUBDIR=jdk-bundle
 JRE_MACOSX_BUNDLE_SUBDIR=jre-bundle
@@ -920,6 +926,7 @@
 TEST_DEMOS_BUNDLE_NAME := jdk-$(BASE_NAME)_bin-tests-demos$(DEBUG_PART).tar.gz
 TEST_BUNDLE_NAME := jdk-$(BASE_NAME)_bin-tests$(DEBUG_PART).tar.gz
 DOCS_BUNDLE_NAME := jdk-$(BASE_NAME)_doc-api-spec$(DEBUG_PART).tar.gz
+STATIC_LIBS_BUNDLE_NAME := jdk-$(BASE_NAME)_bin-static-libs$(DEBUG_PART).tar.gz
 JCOV_BUNDLE_NAME := jdk-jcov-$(BASE_NAME)_bin$(DEBUG_PART).$(JDK_BUNDLE_EXTENSION)
 
 JDK_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JDK_BUNDLE_NAME)
--- a/make/autoconf/version-numbers	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/autoconf/version-numbers	Mon Nov 04 11:25:55 2019 +0000
@@ -23,7 +23,8 @@
 # questions.
 #
 
-# Default version numbers to use unless overridden by configure
+# Default version, product, and vendor information to use,
+# unless overridden by configure
 
 DEFAULT_VERSION_FEATURE=14
 DEFAULT_VERSION_INTERIM=0
@@ -47,6 +48,7 @@
 HOTSPOT_VM_DISTRO="OpenJDK"
 VENDOR_URL=https://openjdk.java.net/
 VENDOR_URL_BUG=https://bugreport.java.com/bugreport/
+VENDOR_URL_VM_BUG=https://bugreport.java.com/bugreport/crash.jsp
 
 # Might need better names for these
 MACOSX_BUNDLE_NAME_BASE="OpenJDK"
--- a/make/common/JavaCompilation.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/common/JavaCompilation.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -222,7 +222,7 @@
     ) \
   )
   $$(call MakeDir,$$($1_BIN))
-  # Order src files according to the order of the src dirs. Correct odering is
+  # Order src files according to the order of the src dirs. Correct ordering is
   # needed for correct overriding between different source roots.
   $1_ALL_SRC_RAW := $$(call FindFiles, $$($1_SRC))
   $1_ALL_SRCS := $$($1_EXTRA_FILES) \
--- a/make/common/JdkNativeCompilation.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/common/JdkNativeCompilation.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2019, 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
@@ -47,7 +47,8 @@
       $(TOPDIR)/src/$(strip $1)/$(OPENJDK_TARGET_OS_TYPE)/native/$(strip $2) \
       $(TOPDIR)/src/$(strip $1)/share/native/$(strip $2)))
 
-# Find a library
+# Find a library. Used for declaring dependencies on libraries in different
+# modules.
 # Param 1 - module name
 # Param 2 - library name
 # Param 3 - optional subdir for library
@@ -69,6 +70,12 @@
   FindLib =
   FindStaticLib =
 endif
+# If building static versions of libraries, make these return empty to avoid
+# declaring dependencies.
+ifeq ($(STATIC_LIBS), true)
+  FindLib =
+  FindStaticLib =
+endif
 
 GetJavaHeaderDir = \
   $(wildcard $(SUPPORT_OUTPUTDIR)/headers/$(strip $1))
--- a/make/common/Modules.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/common/Modules.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -198,6 +198,21 @@
     jdk.internal.vm.compiler.management \
     #
 
+# The native dynamic libraries in these modules will also get built into static
+# libraries for consumption by downstream projects that need to statically link
+# the JDK libraries. Those static libraries are not part of the main JDK
+# distribution. 
+STATIC_LIBS_MODULES := \
+    java.base \
+    jdk.crypto.ec \
+    jdk.security.auth \
+    java.prefs \
+    java.security.jgss \
+    java.smartcardio \
+    jdk.crypto.cryptoki \
+    jdk.net \
+    #
+
 ################################################################################
 # Some platforms don't have the serviceability agent
 
--- a/make/common/NativeCompilation.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/common/NativeCompilation.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -453,6 +453,28 @@
     endif
   endif
 
+  $$(call SetIfEmpty, $1_COMPILE_WITH_DEBUG_SYMBOLS, $$(COMPILE_WITH_DEBUG_SYMBOLS))
+
+  # STATIC_LIBS is set from Main.gmk when building static versions of certain
+  # native libraries.
+  ifeq ($(STATIC_LIBS), true)
+    $1_TYPE := STATIC_LIBRARY
+    # The static versions need to be redirected to different output dirs, both
+    # to not interfere with the main build as well as to not end up inside the
+    # jmods.
+    $1_OBJECT_DIR := $$($1_OBJECT_DIR)/static
+    $1_OUTPUT_DIR := $$($1_OBJECT_DIR)
+    # For release builds where debug symbols are configured to be moved to
+    # separate debuginfo files, disable debug symbols for static libs instead.
+    # We don't currently support this configuration and we don't want symbol
+    # information in release builds unless explicitly asked to provide it.
+    ifeq ($(DEBUG_LEVEL), release)
+      ifeq ($(COPY_DEBUG_SYMBOLS), true)
+        $1_COMPILE_WITH_DEBUG_SYMBOLS := false
+      endif
+    endif
+  endif
+
   ifeq ($$($1_TYPE), EXECUTABLE)
     $1_PREFIX :=
     ifeq ($$($1_SUFFIX), )
@@ -592,6 +614,9 @@
     $1_EXTRA_CFLAGS += $$($1_CFLAGS_$(OPENJDK_TARGET_OS)_release)
     $1_EXTRA_CFLAGS += $$($1_CFLAGS_$(OPENJDK_TARGET_OS)_$(OPENJDK_TARGET_CPU)_release)
   endif
+  ifeq ($(STATIC_LIBS), true)
+    $1_EXTRA_CFLAGS += $$(STATIC_LIBS_CFLAGS)
+  endif
 
   # Pickup extra OPENJDK_TARGET_OS_TYPE and/or OPENJDK_TARGET_OS dependent variables for CXXFLAGS.
   $1_EXTRA_CXXFLAGS := $$($1_CXXFLAGS_$(OPENJDK_TARGET_OS_TYPE)) $$($1_CXXFLAGS_$(OPENJDK_TARGET_OS))
@@ -605,6 +630,9 @@
     $1_EXTRA_CXXFLAGS += $$($1_CXXFLAGS_$(OPENJDK_TARGET_OS_TYPE)_release)
     $1_EXTRA_CXXFLAGS += $$($1_CXXFLAGS_$(OPENJDK_TARGET_OS)_release)
   endif
+  ifeq ($(STATIC_LIBS), true)
+    $1_EXTRA_CXXFLAGS += $$(STATIC_LIB_CFLAGS)
+  endif
 
   # If no C++ flags are explicitly set, default to using the C flags.
   # After that, we can set additional C++ flags that should not interfere
@@ -616,7 +644,7 @@
     $1_EXTRA_CXXFLAGS := $$($1_EXTRA_CFLAGS)
   endif
 
-  ifeq ($(COMPILE_WITH_DEBUG_SYMBOLS), true)
+  ifeq ($$($1_COMPILE_WITH_DEBUG_SYMBOLS), true)
     $1_EXTRA_CFLAGS += $$(CFLAGS_DEBUG_SYMBOLS)
     $1_EXTRA_CXXFLAGS += $$(CFLAGS_DEBUG_SYMBOLS)
     $1_EXTRA_ASFLAGS += $$(ASFLAGS_DEBUG_SYMBOLS)
--- a/make/common/ProcessMarkdown.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/common/ProcessMarkdown.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,4 +1,4 @@
-# Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 2019, 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
--- a/make/common/RMICompilation.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/common/RMICompilation.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/common/SetupJavaCompilers.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/common/SetupJavaCompilers.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/conf/jib-profiles.js	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/conf/jib-profiles.js	Mon Nov 04 11:25:55 2019 +0000
@@ -247,7 +247,7 @@
     // These are the base setttings for all the main build profiles.
     common.main_profile_base = {
         dependencies: ["boot_jdk", "gnumake", "jtreg", "jib", "autoconf", "jmh", "jcov"],
-        default_make_targets: ["product-bundles", "test-bundles"],
+        default_make_targets: ["product-bundles", "test-bundles", "static-libs-bundles"],
         configure_args: concat(["--enable-jtreg-failure-handler"],
             "--with-exclude-translations=de,es,fr,it,ko,pt_BR,sv,ca,tr,cs,sk,ja_JP_A,ja_JP_HA,ja_JP_HI,ja_JP_I,zh_TW,zh_HK",
             "--disable-manpages",
@@ -320,6 +320,14 @@
                     subdir: jdk_subdir,
                     exploded: "images/jdk"
                 },
+                static_libs: {
+                    local: "bundles/\\(jdk.*bin-static-libs.tar.gz\\)",
+                    remote: [
+                        "bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-static-libs.tar.gz",
+                        "bundles/" + pf + "/\\1"
+                    ],
+                    subdir: jdk_subdir,
+                },
             }
         };
     };
@@ -361,6 +369,14 @@
                     subdir: jdk_subdir,
                     exploded: "images/jdk"
                 },
+                static_libs: {
+                    local: "bundles/\\(jdk.*bin-static-libs-debug.tar.gz\\)",
+                    remote: [
+                        "bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-static-libs-debug.tar.gz",
+                        "bundles/" + pf + "/\\1"
+                    ],
+                    subdir: jdk_subdir,
+                },
             }
         };
     };
--- a/make/copy/Copy-jdk.crypto.cryptoki.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/copy/Copy-jdk.crypto.cryptoki.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, 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
--- a/make/copy/Copy-jdk.crypto.ucrypto.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/copy/Copy-jdk.crypto.ucrypto.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, 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
--- a/make/devkit/Tools.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/devkit/Tools.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2019, 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
--- a/make/gendata/GendataBlacklistedCerts.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gendata/GendataBlacklistedCerts.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, 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
--- a/make/gendata/GendataBreakIterator.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gendata/GendataBreakIterator.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 
-# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/gendata/GendataPublicSuffixList.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gendata/GendataPublicSuffixList.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 2019, 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
--- a/make/gensrc/Gensrc-java.desktop.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gensrc/Gensrc-java.desktop.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/gensrc/Gensrc-jdk.hotspot.agent.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gensrc/Gensrc-jdk.hotspot.agent.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2019, 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
--- a/make/gensrc/Gensrc-jdk.jlink.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gensrc/Gensrc-jdk.jlink.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, 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
--- a/make/gensrc/GensrcBuffer.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gensrc/GensrcBuffer.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/gensrc/GensrcCharacterData.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gensrc/GensrcCharacterData.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/gensrc/GensrcCharsetCoder.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gensrc/GensrcCharsetCoder.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/gensrc/GensrcIcons.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gensrc/GensrcIcons.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/gensrc/GensrcMisc.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gensrc/GensrcMisc.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
@@ -46,7 +46,8 @@
         @@VENDOR_VERSION_STRING@@ => $(VENDOR_VERSION_STRING) ; \
         @@VENDOR@@ => $(COMPANY_NAME) ; \
         @@VENDOR_URL@@ => $(VENDOR_URL) ; \
-        @@VENDOR_URL_BUG@@ => $(VENDOR_URL_BUG), \
+        @@VENDOR_URL_BUG@@ => $(VENDOR_URL_BUG) ; \
+        @@VENDOR_URL_VM_BUG@@ => $(VENDOR_URL_VM_BUG), \
 ))
 
 GENSRC_JAVA_BASE += $(BUILD_VERSION_JAVA)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/gensrc/GensrcModuleInfo.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,102 @@
+#
+# Copyright (c) 2015, 2018, 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# 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.
+#
+
+################################################################################
+# This file makes modifications to module-info.java files based on the build
+# configuration.
+#
+# Depending on build platform, imported modules and optional parts of the build
+# being active, some modules need to have extra exports, provides or uses
+# declarations added to them. These optional extras are defined in .extra files:
+#
+# src/<module>/<share,platform>/classes/module-info.java.extra
+#
+# The contents of the .extra files are simply extra lines that could fit into
+# the module-info file.
+#
+# This makefile is called once for each from-module with the variable
+# MODULE naming the from-module.
+#
+# The modified module-info.java files are put in the gensrc directory where
+# they will automatically override the static versions in the src tree.
+#
+################################################################################
+
+default: all
+
+include $(SPEC)
+include MakeBase.gmk
+include Modules.gmk
+
+################################################################################
+# Define this here since jdk/make/Tools.gmk cannot be included from the top
+# make directory. Should probably move some tools away from the jdk repo.
+TOOL_GENMODULEINFOSOURCE = $(JAVA_SMALL) \
+    $(INTERIM_LANGTOOLS_ARGS) \
+    -cp "$(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes" \
+    build.tools.module.GenModuleInfoSource
+
+################################################################################
+
+# Name of modification file.
+MOD_FILENAME := module-info.java.extra
+
+# Construct all possible src directories for the module.
+MODULE_CLASSES_DIRS := $(call FindModuleSrcDirs, $(MODULE))
+
+# Find all the .extra files in the src dirs.
+MOD_FILES := $(wildcard $(foreach f, $(MOD_FILENAME), $(addsuffix /$(f), \
+    $(MODULE_CLASSES_DIRS))))
+
+ifneq ($(MOD_FILES), )
+  # Only make this call if modification files are found for this module
+  ALL_MODULES := $(call FindAllModules)
+
+  $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/module-info.java: \
+      $(firstword $(call FindAllModuleInfos, $(MODULE))) \
+      $(BUILD_TOOLS_JDK) \
+      $(MOD_FILES) \
+      $(call DependOnVariable, ALL_MODULES)
+		$(call MakeTargetDir)
+		$(RM) $@ $@.tmp
+		$(TOOL_GENMODULEINFOSOURCE) -o $@.tmp \
+		    --source-file $< \
+		    --modules $(call CommaList, $(ALL_MODULES)) \
+		    $(MOD_FILES)
+		$(MV) $@.tmp $@
+
+  TARGETS += $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/module-info.java
+
+else
+  # If no modifications are found for this module, remove any module-info.java
+  # created by a previous build since that is no longer valid.
+  ifneq ($(wildcard $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/module-info.java), )
+    $(shell $(RM) $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/module-info.java)
+  endif
+endif
+
+################################################################################
+
+all: $(TARGETS)
--- a/make/gensrc/GensrcVarHandles.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/gensrc/GensrcVarHandles.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2019, 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
@@ -173,4 +173,3 @@
 
 # Include custom extension post hook
 $(eval $(call IncludeCustomExtension, gensrc/GensrcVarHandles-post.gmk))
-
--- a/make/hotspot/CopyToExplodedJdk.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/hotspot/CopyToExplodedJdk.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, 2019, 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
--- a/make/hotspot/lib/CompileDtraceLibraries.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/hotspot/lib/CompileDtraceLibraries.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2019, 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
--- a/make/hotspot/lib/JvmDtraceObjects.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/hotspot/lib/JvmDtraceObjects.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2019, 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
--- a/make/hotspot/lib/JvmFlags.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/hotspot/lib/JvmFlags.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2019, 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
--- a/make/hotspot/lib/JvmMapfile.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/hotspot/lib/JvmMapfile.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2019, 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
--- a/make/hotspot/symbols/symbols-unix	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/hotspot/symbols/symbols-unix	Mon Nov 04 11:25:55 2019 +0000
@@ -46,7 +46,6 @@
 JVM_ConstantPoolGetStringAt
 JVM_ConstantPoolGetTagAt
 JVM_ConstantPoolGetUTF8At
-JVM_CountStackFrames
 JVM_CurrentThread
 JVM_CurrentTimeMillis
 JVM_DefineClass
@@ -143,7 +142,6 @@
 JVM_IsArrayClass
 JVM_IsConstructorIx
 JVM_IsInterface
-JVM_IsInterrupted
 JVM_IsPrimitiveClass
 JVM_IsSameClassPackage
 JVM_IsSupportedJNIVersion
--- a/make/hotspot/test/GtestImage.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/hotspot/test/GtestImage.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, 2019, 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
--- a/make/launcher/Launcher-java.security.jgss.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/launcher/Launcher-java.security.jgss.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/launcher/Launcher-jdk.pack.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/launcher/Launcher-jdk.pack.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/lib/CoreLibraries.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/CoreLibraries.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/lib/Lib-java.base.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-java.base.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -148,26 +148,27 @@
 
     ############################################################################
     # Create symlinks to libjsig in each JVM variant sub dir
-    LIB_OUTPUTDIR := $(call FindLibDirForModule, java.base)
+    ifneq ($(STATIC_LIBS), true)
+      LIB_OUTPUTDIR := $(call FindLibDirForModule, java.base)
 
-    # $1 variant subdir
-    define CreateSymlinks
-      # Always symlink from libdir/variant/libjsig.so -> ../libjsig.so.
-      $(LIB_OUTPUTDIR)/$1/$(call SHARED_LIBRARY,jsig): \
-          $(LIB_OUTPUTDIR)/$(call SHARED_LIBRARY,jsig)
+      # $1 variant subdir
+      define CreateSymlinks
+        # Always symlink from libdir/variant/libjsig.so -> ../libjsig.so.
+        $(LIB_OUTPUTDIR)/$1/$(call SHARED_LIBRARY,jsig): \
+            $(LIB_OUTPUTDIR)/$(call SHARED_LIBRARY,jsig)
 		$$(call MakeDir, $$(@D))
 		$(RM) $$@
 		$(LN) -s ../$$(@F) $$@
 
-      TARGETS += $(LIB_OUTPUTDIR)/$1/$(call SHARED_LIBRARY,jsig)
-    endef
+        TARGETS += $(LIB_OUTPUTDIR)/$1/$(call SHARED_LIBRARY,jsig)
+      endef
 
-    # The subdir is the same as the variant for client and minimal, for all
-    # others it's server.
-    VARIANT_SUBDIRS := $(filter client minimal, $(JVM_VARIANTS)) \
-        $(if $(filter-out client minimal, $(JVM_VARIANTS)), server)
-    $(foreach v, $(VARIANT_SUBDIRS), $(eval $(call CreateSymlinks,$v)))
-
+      # The subdir is the same as the variant for client and minimal, for all
+      # others it's server.
+      VARIANT_SUBDIRS := $(filter client minimal, $(JVM_VARIANTS)) \
+          $(if $(filter-out client minimal, $(JVM_VARIANTS)), server)
+      $(foreach v, $(VARIANT_SUBDIRS), $(eval $(call CreateSymlinks,$v)))
+    endif
     ############################################################################
 
   endif
--- a/make/lib/Lib-java.instrument.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-java.instrument.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/lib/Lib-java.management.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-java.management.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/lib/Lib-java.prefs.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-java.prefs.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/lib/Lib-jdk.accessibility.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.accessibility.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, 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
--- a/make/lib/Lib-jdk.attach.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.attach.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/lib/Lib-jdk.crypto.mscapi.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.crypto.mscapi.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/lib/Lib-jdk.crypto.ucrypto.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.crypto.ucrypto.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2019, 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
--- a/make/lib/Lib-jdk.internal.le.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.internal.le.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2019, 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
--- a/make/lib/Lib-jdk.jdi.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.jdi.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/lib/Lib-jdk.jdwp.agent.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.jdwp.agent.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -38,7 +38,7 @@
         $(call SET_SHARED_LIBRARY_ORIGIN), \
     LIBS_linux := -lpthread, \
     LIBS_solaris := -lnsl -lsocket, \
-    LIBS_windows := $(JDKLIB_LIBS) ws2_32.lib, \
+    LIBS_windows := $(JDKLIB_LIBS) ws2_32.lib iphlpapi.lib, \
 ))
 
 $(BUILD_LIBDT_SOCKET): $(call FindLib, java.base, java)
--- a/make/lib/Lib-jdk.management.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.management.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2019, 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
--- a/make/lib/Lib-jdk.net.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.net.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, 2019, 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
--- a/make/lib/Lib-jdk.sctp.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/lib/Lib-jdk.sctp.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/rmic/Rmic-java.management.rmi.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/rmic/Rmic-java.management.rmi.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, 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
--- a/make/test/BuildFailureHandler.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/test/BuildFailureHandler.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, 2019, 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
--- a/make/test/JtregGraalUnit.gmk	Wed Oct 30 12:25:22 2019 +0000
+++ b/make/test/JtregGraalUnit.gmk	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2019, 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
--- a/src/hotspot/.mx.jvmci/suite.py	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/.mx.jvmci/suite.py	Mon Nov 04 11:25:55 2019 +0000
@@ -171,7 +171,9 @@
       "subDir" : "../../test/hotspot/jtreg/compiler/jvmci",
       "sourceDirs" : ["src"],
       "dependencies" : [
+        "mx:JUNIT",
         "TESTNG",
+        "jdk.vm.ci.code.test",
         "jdk.vm.ci.hotspot",
       ],
       "checkstyle" : "jdk.vm.ci.services",
--- a/src/hotspot/cpu/aarch64/aarch64.ad	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/cpu/aarch64/aarch64.ad	Mon Nov 04 11:25:55 2019 +0000
@@ -10413,6 +10413,56 @@
   ins_pipe(lmac_reg_reg);
 %}
 
+// Combine Integer Signed Multiply & Add/Sub/Neg Long
+
+instruct smaddL(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2, iRegLNoSp src3) %{
+  match(Set dst (AddL src3 (MulL (ConvI2L src1) (ConvI2L src2))));
+
+  ins_cost(INSN_COST * 3);
+  format %{ "smaddl  $dst, $src1, $src2, $src3" %}
+
+  ins_encode %{
+    __ smaddl(as_Register($dst$$reg),
+              as_Register($src1$$reg),
+              as_Register($src2$$reg),
+              as_Register($src3$$reg));
+  %}
+
+  ins_pipe(imac_reg_reg);
+%}
+
+instruct smsubL(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2, iRegLNoSp src3) %{
+  match(Set dst (SubL src3 (MulL (ConvI2L src1) (ConvI2L src2))));
+
+  ins_cost(INSN_COST * 3);
+  format %{ "smsubl  $dst, $src1, $src2, $src3" %}
+
+  ins_encode %{
+    __ smsubl(as_Register($dst$$reg),
+              as_Register($src1$$reg),
+              as_Register($src2$$reg),
+              as_Register($src3$$reg));
+  %}
+
+  ins_pipe(imac_reg_reg);
+%}
+
+instruct smnegL(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2, immL0 zero) %{
+  match(Set dst (MulL (SubL zero (ConvI2L src1)) (ConvI2L src2)));
+  match(Set dst (MulL (ConvI2L src1) (SubL zero (ConvI2L src2))));
+
+  ins_cost(INSN_COST * 3);
+  format %{ "smnegl  $dst, $src1, $src2" %}
+
+  ins_encode %{
+    __ smnegl(as_Register($dst$$reg),
+              as_Register($src1$$reg),
+              as_Register($src2$$reg));
+  %}
+
+  ins_pipe(imac_reg_reg);
+%}
+
 // Integer Divide
 
 instruct divI(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
--- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -544,7 +544,7 @@
 
   Register obj = stub->obj()->as_register();
   Register res = stub->result()->as_register();
-  Register addr = stub->addr()->as_register_lo();
+  Register addr = stub->addr()->as_pointer_register();
   Register tmp1 = stub->tmp1()->as_register();
   Register tmp2 = stub->tmp2()->as_register();
 
--- a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2018 SAP SE. All rights reserved.
+ * Copyright (c) 2012, 2019 SAP SE. 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
@@ -48,8 +48,12 @@
 define_pd_global(intx,     FreqInlineSize,               325 );
 define_pd_global(bool,     ResizeTLAB,                   true);
 define_pd_global(uintx,    ReservedCodeCacheSize,        32*M);
+define_pd_global(uintx,    NonProfiledCodeHeapSize,      13*M );
+define_pd_global(uintx,    ProfiledCodeHeapSize,         14*M );
+define_pd_global(uintx,    NonNMethodCodeHeapSize,       5*M );
 define_pd_global(uintx,    CodeCacheExpansionSize,       32*K);
 define_pd_global(uintx,    CodeCacheMinBlockLength,      1);
+define_pd_global(uintx,    CodeCacheMinimumUseSpace,     400*K);
 define_pd_global(size_t,   MetaspaceSize,                12*M);
 define_pd_global(bool,     NeverActAsServerClassMachine, true);
 define_pd_global(size_t,   NewSizeThreadIncrease,        16*K);
--- a/src/hotspot/cpu/ppc/disassembler_ppc.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/cpu/ppc/disassembler_ppc.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -27,8 +27,6 @@
 #include "code/codeCache.hpp"
 #include "compiler/disassembler.hpp"
 #include "depChecker_ppc.hpp"
-#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp"
-#include "gc/cms/parOopClosures.inline.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/cardTableBarrierSet.hpp"
 #include "gc/shared/genOopClosures.inline.hpp"
--- a/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -50,6 +50,8 @@
 #if defined(COMPILER2) && (defined(AIX) || defined(LINUX))
 // Include Transactional Memory lock eliding optimization
 #define INCLUDE_RTM_OPT 1
+#else
+#define INCLUDE_RTM_OPT 0
 #endif
 
 #define SUPPORT_RESERVED_STACK_AREA
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -571,7 +571,6 @@
   __ bctr();
 }
 
-#ifdef COMPILER2
 static int reg2slot(VMReg r) {
   return r->reg2stack() + SharedRuntime::out_preserve_stack_slots();
 }
@@ -579,7 +578,6 @@
 static int reg2offset(VMReg r) {
   return (r->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size;
 }
-#endif
 
 // ---------------------------------------------------------------------------
 // Read the array of BasicTypes from a signature, and compute where the
@@ -1305,7 +1303,6 @@
   return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
 }
 
-#ifdef COMPILER2
 // An oop arg. Must pass a handle not the oop itself.
 static void object_move(MacroAssembler* masm,
                         int frame_size_in_slots,
@@ -1813,8 +1810,6 @@
                                                  receiver_reg, member_reg, /*for_compiler_entry:*/ true);
 }
 
-#endif // COMPILER2
-
 // ---------------------------------------------------------------------------
 // Generate a native wrapper for a given method. The method takes arguments
 // in the Java compiled code convention, marshals them to the native
@@ -1851,7 +1846,6 @@
                                                 VMRegPair *in_regs,
                                                 BasicType ret_type,
                                                 address critical_entry) {
-#ifdef COMPILER2
   if (method->is_method_handle_intrinsic()) {
     vmIntrinsics::ID iid = method->intrinsic_id();
     intptr_t start = (intptr_t)__ pc();
@@ -2108,7 +2102,7 @@
 
   // Check ic: object class == cached class?
   if (!method_is_static) {
-  Register ic = as_Register(Matcher::inline_cache_reg_encode());
+  Register ic = R19_inline_cache_reg;
   Register receiver_klass = r_temp_1;
 
   __ cmpdi(CCR0, R3_ARG1, 0);
@@ -2638,12 +2632,10 @@
 
   // Handler for pending exceptions (out-of-line).
   // --------------------------------------------------------------------------
-
   // Since this is a native call, we know the proper exception handler
   // is the empty function. We just pop this frame and then jump to
   // forward_exception_entry.
   if (!is_critical_native) {
-  __ align(InteriorEntryAlignment);
   __ bind(handle_pending_exception);
 
   __ pop_frame();
@@ -2656,7 +2648,6 @@
   // --------------------------------------------------------------------------
 
   if (!method_is_static) {
-  __ align(InteriorEntryAlignment);
   __ bind(ic_miss);
 
   __ b64_patchable((address)SharedRuntime::get_ic_miss_stub(),
@@ -2683,10 +2674,6 @@
   }
 
   return nm;
-#else
-  ShouldNotReachHere();
-  return NULL;
-#endif // COMPILER2
 }
 
 // This function returns the adjust size (in number of words) to a c2i adapter
@@ -2863,7 +2850,7 @@
   // We can't grab a free register here, because all registers may
   // contain live values, so let the RegisterSaver do the adjustment
   // of the return pc.
-  const int return_pc_adjustment_no_exception = -HandlerImpl::size_deopt_handler();
+  const int return_pc_adjustment_no_exception = -MacroAssembler::bl64_patchable_size;
 
   // Push the "unpack frame"
   // Save everything in sight.
--- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -3103,6 +3103,7 @@
                                                              STUB_ENTRY(checkcast_arraycopy));
 
     // fill routines
+#ifdef COMPILER2
     if (OptimizeFill) {
       StubRoutines::_jbyte_fill          = generate_fill(T_BYTE,  false, "jbyte_fill");
       StubRoutines::_jshort_fill         = generate_fill(T_SHORT, false, "jshort_fill");
@@ -3111,6 +3112,7 @@
       StubRoutines::_arrayof_jshort_fill = generate_fill(T_SHORT, true, "arrayof_jshort_fill");
       StubRoutines::_arrayof_jint_fill   = generate_fill(T_INT,   true, "arrayof_jint_fill");
     }
+#endif
   }
 
   // Safefetch stubs.
@@ -3579,8 +3581,6 @@
     if (UseMultiplyToLenIntrinsic) {
       StubRoutines::_multiplyToLen = generate_multiplyToLen();
     }
-#endif
-
     if (UseSquareToLenIntrinsic) {
       StubRoutines::_squareToLen = generate_squareToLen();
     }
@@ -3595,6 +3595,7 @@
       StubRoutines::_montgomerySquare
         = CAST_FROM_FN_PTR(address, SharedRuntime::montgomery_square);
     }
+#endif
 
     if (UseAESIntrinsics) {
       StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock();
--- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -312,6 +312,7 @@
     FLAG_SET_DEFAULT(UseSHA, false);
   }
 
+#ifdef COMPILER2
   if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) {
     UseSquareToLenIntrinsic = true;
   }
@@ -327,6 +328,7 @@
   if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) {
     UseMontgomerySquareIntrinsic = true;
   }
+#endif
 
   if (UseVectorizedMismatchIntrinsic) {
     warning("UseVectorizedMismatchIntrinsic specified, but not available on this CPU.");
@@ -373,9 +375,11 @@
     if (UseRTMDeopt) {
       FLAG_SET_DEFAULT(UseRTMDeopt, false);
     }
+#ifdef COMPILER2
     if (PrintPreciseRTMLockingStatistics) {
       FLAG_SET_DEFAULT(PrintPreciseRTMLockingStatistics, false);
     }
+#endif
   }
 
   // This machine allows unaligned memory accesses
--- a/src/hotspot/os/linux/osContainer_linux.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/os/linux/osContainer_linux.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -54,12 +54,16 @@
 
 bool  OSContainer::_is_initialized   = false;
 bool  OSContainer::_is_containerized = false;
+int   OSContainer::_active_processor_count = 1;
 julong _unlimited_memory;
 
 class CgroupSubsystem: CHeapObj<mtInternal> {
  friend class OSContainer;
 
+
  private:
+    volatile jlong _next_check_counter;
+
     /* mountinfo contents */
     char *_root;
     char *_mount_point;
@@ -72,6 +76,7 @@
       _root = os::strdup(root);
       _mount_point = os::strdup(mountpoint);
       _path = NULL;
+      _next_check_counter = min_jlong;
     }
 
     /*
@@ -121,6 +126,14 @@
     }
 
     char *subsystem_path() { return _path; }
+
+    bool cache_has_expired() {
+      return os::elapsed_counter() > _next_check_counter;
+    }
+
+    void set_cache_expiry_time(jlong timeout) {
+      _next_check_counter = os::elapsed_counter() + timeout;
+    }
 };
 
 class CgroupMemorySubsystem: CgroupSubsystem {
@@ -132,31 +145,26 @@
      * file if everything else seems unlimited */
     bool _uses_mem_hierarchy;
     volatile jlong _memory_limit_in_bytes;
-    volatile jlong _next_check_counter;
 
  public:
     CgroupMemorySubsystem(char *root, char *mountpoint) : CgroupSubsystem::CgroupSubsystem(root, mountpoint) {
       _uses_mem_hierarchy = false;
       _memory_limit_in_bytes = -1;
-      _next_check_counter = min_jlong;
 
     }
 
     bool is_hierarchical() { return _uses_mem_hierarchy; }
     void set_hierarchical(bool value) { _uses_mem_hierarchy = value; }
 
-    bool should_check_memory_limit() {
-      return os::elapsed_counter() > _next_check_counter;
-    }
     jlong memory_limit_in_bytes() { return _memory_limit_in_bytes; }
     void set_memory_limit_in_bytes(jlong value) {
       _memory_limit_in_bytes = value;
       // max memory limit is unlikely to change, but we want to remain
-      // responsive to configuration changes. A very short (20ms) grace time
+      // responsive to configuration changes. A very short grace time
       // between re-read avoids excessive overhead during startup without
       // significantly reducing the VMs ability to promptly react to reduced
       // memory availability
-      _next_check_counter = os::elapsed_counter() + (NANOSECS_PER_SEC/50);
+      set_cache_expiry_time(OSCONTAINER_CACHE_TIMEOUT);
     }
 
 };
@@ -481,7 +489,7 @@
  *    OSCONTAINER_ERROR for not supported
  */
 jlong OSContainer::memory_limit_in_bytes() {
-  if (!memory->should_check_memory_limit()) {
+  if (!memory->cache_has_expired()) {
     return memory->memory_limit_in_bytes();
   }
   jlong memory_limit = read_memory_limit_in_bytes();
@@ -617,6 +625,14 @@
   int cpu_count, limit_count;
   int result;
 
+  // We use a cache with a timeout to avoid performing expensive
+  // computations in the event this function is called frequently.
+  // [See 8227006].
+  if (!cpu->cache_has_expired()) {
+    log_trace(os, container)("OSContainer::active_processor_count (cached): %d", OSContainer::_active_processor_count);
+    return OSContainer::_active_processor_count;
+  }
+
   cpu_count = limit_count = os::Linux::active_processor_count();
   int quota  = cpu_quota();
   int period = cpu_period();
@@ -649,6 +665,11 @@
 
   result = MIN2(cpu_count, limit_count);
   log_trace(os, container)("OSContainer::active_processor_count: %d", result);
+
+  // Update the value and reset the cache timeout
+  OSContainer::_active_processor_count = result;
+  cpu->set_cache_expiry_time(OSCONTAINER_CACHE_TIMEOUT);
+
   return result;
 }
 
--- a/src/hotspot/os/linux/osContainer_linux.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/os/linux/osContainer_linux.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -31,11 +31,16 @@
 
 #define OSCONTAINER_ERROR (-2)
 
+// 20ms timeout between re-reads of memory limit and _active_processor_count.
+#define OSCONTAINER_CACHE_TIMEOUT (NANOSECS_PER_SEC/50)
+
 class OSContainer: AllStatic {
 
  private:
   static bool   _is_initialized;
   static bool   _is_containerized;
+  static int    _active_processor_count;
+
   static jlong read_memory_limit_in_bytes();
 
  public:
--- a/src/hotspot/os/windows/osThread_windows.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/os/windows/osThread_windows.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -39,28 +39,15 @@
   }
 }
 
-// We need to specialize these to interact with the _interrupt_event.
-
-volatile bool OSThread::interrupted() {
-  return _interrupted != 0 &&
-    (WaitForSingleObject(_interrupt_event, 0) == WAIT_OBJECT_0);
-}
+// We need to specialize this to interact with the _interrupt_event.
 
 void OSThread::set_interrupted(bool z) {
   if (z) {
-    _interrupted = 1;
-    // More than one thread can get here with the same value of osthread,
-    // resulting in multiple notifications.  We do, however, want the store
-    // to interrupted() to be visible to other threads before we post
-    // the interrupt event.
-    OrderAccess::release();
     SetEvent(_interrupt_event);
   }
   else {
     // We should only ever clear the interrupt if we are in fact interrupted,
     // and this can only be done by the current thread on itself.
-    assert(_interrupted == 1, "invariant for clearing interrupt state");
-    _interrupted = 0;
     ResetEvent(_interrupt_event);
   }
 }
--- a/src/hotspot/os/windows/osThread_windows.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/os/windows/osThread_windows.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -43,10 +43,7 @@
   void set_thread_handle(HANDLE handle)            { _thread_handle = handle; }
   HANDLE interrupt_event() const                   { return _interrupt_event; }
   void set_interrupt_event(HANDLE interrupt_event) { _interrupt_event = interrupt_event; }
-  // These are specialized on Windows to interact with the _interrupt_event.
-  // Also note that Windows does not skip these calls if we are interrupted - see
-  // LibraryCallKit::inline_native_isInterrupted
-  volatile bool interrupted();
+  // This is specialized on Windows to interact with the _interrupt_event.
   void set_interrupted(bool z);
 
 #ifndef PRODUCT
--- a/src/hotspot/share/aot/aotCodeHeap.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/aot/aotCodeHeap.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -450,7 +450,6 @@
     SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_write_barrier_post", address, JVMCIRuntime::write_barrier_post);
 #endif
     SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_identity_hash_code", address, JVMCIRuntime::identity_hash_code);
-    SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_thread_is_interrupted", address, JVMCIRuntime::thread_is_interrupted);
     SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_exception_handler_for_pc", address, JVMCIRuntime::exception_handler_for_pc);
     SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_test_deoptimize_call_int", address, JVMCIRuntime::test_deoptimize_call_int);
     SET_AOT_GLOBAL_SYMBOL_VALUE("_aot_jvmci_runtime_throw_and_post_jvmti_exception", address, JVMCIRuntime::throw_and_post_jvmti_exception);
--- a/src/hotspot/share/ci/ciMethodData.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/ci/ciMethodData.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -266,6 +266,8 @@
         // With concurrent class unloading, the MDO could have stale metadata; override it
         clear_row(row);
       }
+    } else {
+      set_receiver(row, NULL);
     }
   }
 }
--- a/src/hotspot/share/classfile/classLoader.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/classfile/classLoader.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -73,14 +73,17 @@
 #include "utilities/hashtable.inline.hpp"
 #include "utilities/macros.hpp"
 
+// Entry point in java.dll for path canonicalization
+
+static canonicalize_fn_t CanonicalizeEntry  = NULL;
+
 // Entry points in zip.dll for loading zip/jar file entries
 
 typedef void * * (*ZipOpen_t)(const char *name, char **pmsg);
-typedef void (*ZipClose_t)(jzfile *zip);
+typedef void     (*ZipClose_t)(jzfile *zip);
 typedef jzentry* (*FindEntry_t)(jzfile *zip, const char *name, jint *sizeP, jint *nameLen);
 typedef jboolean (*ReadEntry_t)(jzfile *zip, jzentry *entry, unsigned char *buf, char *namebuf);
 typedef jzentry* (*GetNextEntry_t)(jzfile *zip, jint n);
-typedef jboolean (*ZipInflateFully_t)(void *inBuf, jlong inLen, void *outBuf, jlong outLen, char **pmsg);
 typedef jint     (*Crc32_t)(jint crc, const jbyte *buf, jint len);
 
 static ZipOpen_t         ZipOpen            = NULL;
@@ -88,8 +91,6 @@
 static FindEntry_t       FindEntry          = NULL;
 static ReadEntry_t       ReadEntry          = NULL;
 static GetNextEntry_t    GetNextEntry       = NULL;
-static canonicalize_fn_t CanonicalizeEntry  = NULL;
-static ZipInflateFully_t ZipInflateFully    = NULL;
 static Crc32_t           Crc32              = NULL;
 
 // Entry points for jimage.dll for loading jimage file entries
@@ -295,9 +296,7 @@
 }
 
 ClassPathZipEntry::~ClassPathZipEntry() {
-  if (ZipClose != NULL) {
-    (*ZipClose)(_zip);
-  }
+  (*ZipClose)(_zip);
   FREE_C_HEAP_ARRAY(char, _zip_name);
 }
 
@@ -964,11 +963,28 @@
   tty->print_cr("]");
 }
 
+void* ClassLoader::dll_lookup(void* lib, const char* name, const char* path) {
+  void* func = os::dll_lookup(lib, name);
+  if (func == NULL) {
+    char msg[256] = "";
+    jio_snprintf(msg, sizeof(msg), "Could not resolve \"%s\"", name);
+    vm_exit_during_initialization(msg, path);
+  }
+  return func;
+}
+
+void ClassLoader::load_java_library() {
+  assert(CanonicalizeEntry == NULL, "should not load java library twice");
+  void *javalib_handle = os::native_java_library();
+  if (javalib_handle == NULL) {
+    vm_exit_during_initialization("Unable to load java library", NULL);
+  }
+
+  CanonicalizeEntry = CAST_TO_FN_PTR(canonicalize_fn_t, dll_lookup(javalib_handle, "Canonicalize", NULL));
+}
+
 void ClassLoader::load_zip_library() {
   assert(ZipOpen == NULL, "should not load zip library twice");
-  // First make sure native library is loaded
-  os::native_java_library();
-  // Load zip library
   char path[JVM_MAXPATHLEN];
   char ebuf[1024];
   void* handle = NULL;
@@ -976,37 +992,19 @@
     handle = os::dll_load(path, ebuf, sizeof ebuf);
   }
   if (handle == NULL) {
-    vm_exit_during_initialization("Unable to load ZIP library", path);
-  }
-  // Lookup zip entry points
-  ZipOpen      = CAST_TO_FN_PTR(ZipOpen_t, os::dll_lookup(handle, "ZIP_Open"));
-  ZipClose     = CAST_TO_FN_PTR(ZipClose_t, os::dll_lookup(handle, "ZIP_Close"));
-  FindEntry    = CAST_TO_FN_PTR(FindEntry_t, os::dll_lookup(handle, "ZIP_FindEntry"));
-  ReadEntry    = CAST_TO_FN_PTR(ReadEntry_t, os::dll_lookup(handle, "ZIP_ReadEntry"));
-  GetNextEntry = CAST_TO_FN_PTR(GetNextEntry_t, os::dll_lookup(handle, "ZIP_GetNextEntry"));
-  ZipInflateFully = CAST_TO_FN_PTR(ZipInflateFully_t, os::dll_lookup(handle, "ZIP_InflateFully"));
-  Crc32        = CAST_TO_FN_PTR(Crc32_t, os::dll_lookup(handle, "ZIP_CRC32"));
-
-  // ZIP_Close is not exported on Windows in JDK5.0 so don't abort if ZIP_Close is NULL
-  if (ZipOpen == NULL || FindEntry == NULL || ReadEntry == NULL ||
-      GetNextEntry == NULL || Crc32 == NULL) {
-    vm_exit_during_initialization("Corrupted ZIP library", path);
+    vm_exit_during_initialization("Unable to load zip library", path);
   }
 
-  if (ZipInflateFully == NULL) {
-    vm_exit_during_initialization("Corrupted ZIP library ZIP_InflateFully missing", path);
-  }
-
-  // Lookup canonicalize entry in libjava.dll
-  void *javalib_handle = os::native_java_library();
-  CanonicalizeEntry = CAST_TO_FN_PTR(canonicalize_fn_t, os::dll_lookup(javalib_handle, "Canonicalize"));
-  // This lookup only works on 1.3. Do not check for non-null here
+  ZipOpen = CAST_TO_FN_PTR(ZipOpen_t, dll_lookup(handle, "ZIP_Open", path));
+  ZipClose = CAST_TO_FN_PTR(ZipClose_t, dll_lookup(handle, "ZIP_Close", path));
+  FindEntry = CAST_TO_FN_PTR(FindEntry_t, dll_lookup(handle, "ZIP_FindEntry", path));
+  ReadEntry = CAST_TO_FN_PTR(ReadEntry_t, dll_lookup(handle, "ZIP_ReadEntry", path));
+  GetNextEntry = CAST_TO_FN_PTR(GetNextEntry_t, dll_lookup(handle, "ZIP_GetNextEntry", path));
+  Crc32 = CAST_TO_FN_PTR(Crc32_t, dll_lookup(handle, "ZIP_CRC32", path));
 }
 
 void ClassLoader::load_jimage_library() {
-  // First make sure native library is loaded
-  os::native_java_library();
-  // Load jimage library
+  assert(JImageOpen == NULL, "should not load jimage library twice");
   char path[JVM_MAXPATHLEN];
   char ebuf[1024];
   void* handle = NULL;
@@ -1017,27 +1015,15 @@
     vm_exit_during_initialization("Unable to load jimage library", path);
   }
 
-  // Lookup jimage entry points
-  JImageOpen = CAST_TO_FN_PTR(JImageOpen_t, os::dll_lookup(handle, "JIMAGE_Open"));
-  guarantee(JImageOpen != NULL, "function JIMAGE_Open not found");
-  JImageClose = CAST_TO_FN_PTR(JImageClose_t, os::dll_lookup(handle, "JIMAGE_Close"));
-  guarantee(JImageClose != NULL, "function JIMAGE_Close not found");
-  JImagePackageToModule = CAST_TO_FN_PTR(JImagePackageToModule_t, os::dll_lookup(handle, "JIMAGE_PackageToModule"));
-  guarantee(JImagePackageToModule != NULL, "function JIMAGE_PackageToModule not found");
-  JImageFindResource = CAST_TO_FN_PTR(JImageFindResource_t, os::dll_lookup(handle, "JIMAGE_FindResource"));
-  guarantee(JImageFindResource != NULL, "function JIMAGE_FindResource not found");
-  JImageGetResource = CAST_TO_FN_PTR(JImageGetResource_t, os::dll_lookup(handle, "JIMAGE_GetResource"));
-  guarantee(JImageGetResource != NULL, "function JIMAGE_GetResource not found");
-  JImageResourceIterator = CAST_TO_FN_PTR(JImageResourceIterator_t, os::dll_lookup(handle, "JIMAGE_ResourceIterator"));
-  guarantee(JImageResourceIterator != NULL, "function JIMAGE_ResourceIterator not found");
-}
-
-jboolean ClassLoader::decompress(void *in, u8 inSize, void *out, u8 outSize, char **pmsg) {
-  return (*ZipInflateFully)(in, inSize, out, outSize, pmsg);
+  JImageOpen = CAST_TO_FN_PTR(JImageOpen_t, dll_lookup(handle, "JIMAGE_Open", path));
+  JImageClose = CAST_TO_FN_PTR(JImageClose_t, dll_lookup(handle, "JIMAGE_Close", path));
+  JImagePackageToModule = CAST_TO_FN_PTR(JImagePackageToModule_t, dll_lookup(handle, "JIMAGE_PackageToModule", path));
+  JImageFindResource = CAST_TO_FN_PTR(JImageFindResource_t, dll_lookup(handle, "JIMAGE_FindResource", path));
+  JImageGetResource = CAST_TO_FN_PTR(JImageGetResource_t, dll_lookup(handle, "JIMAGE_GetResource", path));
+  JImageResourceIterator = CAST_TO_FN_PTR(JImageResourceIterator_t, dll_lookup(handle, "JIMAGE_ResourceIterator", path));
 }
 
 int ClassLoader::crc32(int crc, const char* buf, int len) {
-  assert(Crc32 != NULL, "ZIP_CRC32 is not found");
   return (*Crc32)(crc, (const jbyte*)buf, len);
 }
 
@@ -1527,11 +1513,45 @@
                         "unsafeDefineClassCalls");
   }
 
+  // lookup java library entry points
+  load_java_library();
   // lookup zip library entry points
   load_zip_library();
-  // lookup jimage library entry points
+  // jimage library entry points are loaded below, in lookup_vm_options
+  setup_bootstrap_search_path();
+}
+
+char* lookup_vm_resource(JImageFile *jimage, const char *jimage_version, const char *path) {
+  jlong size;
+  JImageLocationRef location = (*JImageFindResource)(jimage, "java.base", jimage_version, path, &size);
+  if (location == 0)
+    return NULL;
+  char *val = NEW_C_HEAP_ARRAY(char, size+1, mtClass);
+  (*JImageGetResource)(jimage, location, val, size);
+  val[size] = '\0';
+  return val;
+}
+
+// Lookup VM options embedded in the modules jimage file
+char* ClassLoader::lookup_vm_options() {
+  jint error;
+  char modules_path[JVM_MAXPATHLEN];
+  const char* fileSep = os::file_separator();
+
+  // Initialize jimage library entry points
   load_jimage_library();
-  setup_bootstrap_search_path();
+
+  jio_snprintf(modules_path, JVM_MAXPATHLEN, "%s%slib%smodules", Arguments::get_java_home(), fileSep, fileSep);
+  JImageFile* jimage =(*JImageOpen)(modules_path, &error);
+  if (jimage == NULL) {
+    return NULL;
+  }
+
+  const char *jimage_version = get_jimage_version_string();
+  char *options = lookup_vm_resource(jimage, jimage_version, "jdk/internal/vm/options");
+
+  (*JImageClose)(jimage);
+  return options;
 }
 
 #if INCLUDE_CDS
@@ -1620,24 +1640,17 @@
   }
 }
 
-
 bool ClassLoader::get_canonical_path(const char* orig, char* out, int len) {
   assert(orig != NULL && out != NULL && len > 0, "bad arguments");
-  if (CanonicalizeEntry != NULL) {
-    JavaThread* THREAD = JavaThread::current();
-    JNIEnv* env = THREAD->jni_environment();
-    ResourceMark rm(THREAD);
+  JavaThread* THREAD = JavaThread::current();
+  JNIEnv* env = THREAD->jni_environment();
+  ResourceMark rm(THREAD);
 
-    // os::native_path writes into orig_copy
-    char* orig_copy = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, strlen(orig)+1);
-    strcpy(orig_copy, orig);
-    if ((CanonicalizeEntry)(env, os::native_path(orig_copy), out, len) < 0) {
-      return false;
-    }
-  } else {
-    // On JDK 1.2.2 the Canonicalize does not exist, so just do nothing
-    strncpy(out, orig, len);
-    out[len - 1] = '\0';
+  // os::native_path writes into orig_copy
+  char* orig_copy = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, strlen(orig)+1);
+  strcpy(orig_copy, orig);
+  if ((CanonicalizeEntry)(env, os::native_path(orig_copy), out, len) < 0) {
+    return false;
   }
   return true;
 }
--- a/src/hotspot/share/classfile/classLoader.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/classfile/classLoader.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -249,6 +249,8 @@
   static void setup_patch_mod_entries();
   static void create_javabase();
 
+  static void* dll_lookup(void* lib, const char* name, const char* path);
+  static void load_java_library();
   static void load_zip_library();
   static void load_jimage_library();
 
@@ -275,7 +277,6 @@
   static PackageEntry* get_package_entry(const char* class_name, ClassLoaderData* loader_data, TRAPS);
 
  public:
-  static jboolean decompress(void *in, u8 inSize, void *out, u8 outSize, char **pmsg);
   static int crc32(int crc, const char* buf, int len);
   static bool update_class_path_entry_list(const char *path,
                                            bool check_for_duplicates,
@@ -411,6 +412,9 @@
   static char* skip_uri_protocol(char* source);
   static void  record_result(InstanceKlass* ik, const ClassFileStream* stream, TRAPS);
 #endif
+
+  static char* lookup_vm_options();
+
   static JImageLocationRef jimage_find_resource(JImageFile* jf, const char* module_name,
                                                 const char* file_name, jlong &size);
 
--- a/src/hotspot/share/classfile/javaClasses.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/classfile/javaClasses.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -56,6 +56,7 @@
 #include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/handles.inline.hpp"
+#include "runtime/init.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
@@ -387,14 +388,20 @@
 Handle java_lang_String::create_from_platform_dependent_str(const char* str, TRAPS) {
   assert(str != NULL, "bad arguments");
 
-  typedef jstring (*to_java_string_fn_t)(JNIEnv*, const char *);
+  typedef jstring (JNICALL *to_java_string_fn_t)(JNIEnv*, const char *);
   static to_java_string_fn_t _to_java_string_fn = NULL;
 
   if (_to_java_string_fn == NULL) {
     void *lib_handle = os::native_java_library();
-    _to_java_string_fn = CAST_TO_FN_PTR(to_java_string_fn_t, os::dll_lookup(lib_handle, "NewStringPlatform"));
+    _to_java_string_fn = CAST_TO_FN_PTR(to_java_string_fn_t, os::dll_lookup(lib_handle, "JNU_NewStringPlatform"));
+#if defined(_WIN32) && !defined(_WIN64)
     if (_to_java_string_fn == NULL) {
-      fatal("NewStringPlatform missing");
+      // On 32 bit Windows, also try __stdcall decorated name
+      _to_java_string_fn = CAST_TO_FN_PTR(to_java_string_fn_t, os::dll_lookup(lib_handle, "_JNU_NewStringPlatform@8"));
+    }
+#endif
+    if (_to_java_string_fn == NULL) {
+      fatal("JNU_NewStringPlatform missing");
     }
   }
 
@@ -1628,6 +1635,7 @@
 int java_lang_Thread::_inheritedAccessControlContext_offset = 0;
 int java_lang_Thread::_priority_offset = 0;
 int java_lang_Thread::_eetop_offset = 0;
+int java_lang_Thread::_interrupted_offset = 0;
 int java_lang_Thread::_daemon_offset = 0;
 int java_lang_Thread::_stillborn_offset = 0;
 int java_lang_Thread::_stackSize_offset = 0;
@@ -1643,6 +1651,7 @@
   macro(_priority_offset,      k, vmSymbols::priority_name(), int_signature, false); \
   macro(_daemon_offset,        k, vmSymbols::daemon_name(), bool_signature, false); \
   macro(_eetop_offset,         k, "eetop", long_signature, false); \
+  macro(_interrupted_offset,   k, "interrupted", bool_signature, false); \
   macro(_stillborn_offset,     k, "stillborn", bool_signature, false); \
   macro(_stackSize_offset,     k, "stackSize", long_signature, false); \
   macro(_tid_offset,           k, "tid", long_signature, false); \
@@ -1671,6 +1680,21 @@
   java_thread->address_field_put(_eetop_offset, (address)thread);
 }
 
+bool java_lang_Thread::interrupted(oop java_thread) {
+#if INCLUDE_JFR
+  if (java_thread == NULL) {
+    // can happen from Jfr::on_vm_init leading to call of JavaThread::sleep
+    assert(!is_init_completed(), "should only happen during init");
+    return false;
+  }
+#endif
+  return java_thread->bool_field_volatile(_interrupted_offset);
+}
+
+void java_lang_Thread::set_interrupted(oop java_thread, bool val) {
+  java_thread->bool_field_put_volatile(_interrupted_offset, val);
+}
+
 
 oop java_lang_Thread::name(oop java_thread) {
   return java_thread->obj_field(_name_offset);
@@ -1956,6 +1980,8 @@
 // This class provides a simple wrapper over the internal structure of
 // exception backtrace to insulate users of the backtrace from needing
 // to know what it looks like.
+// The code of this class is not GC safe. Allocations can only happen
+// in expand().
 class BacktraceBuilder: public StackObj {
  friend class BacktraceIterator;
  private:
@@ -2104,10 +2130,14 @@
 
   void set_has_hidden_top_frame(TRAPS) {
     if (_has_hidden_top_frame == NULL) {
-      jvalue prim;
-      prim.z = 1;
-      PauseNoSafepointVerifier pnsv(&_nsv);
-      _has_hidden_top_frame = java_lang_boxing_object::create(T_BOOLEAN, &prim, CHECK);
+      // It would be nice to add java/lang/Boolean::TRUE here
+      // to indicate that this backtrace has a hidden top frame.
+      // But this code is used before TRUE is allocated.
+      // Therefor let's just use an arbitrary legal oop
+      // available right here. We only test for != null
+      // anyways. _methods is a short[].
+      assert(_methods != NULL, "we need a legal oop");
+      _has_hidden_top_frame = _methods;
       _head->obj_at_put(trace_hidden_offset, _has_hidden_top_frame);
     }
   }
--- a/src/hotspot/share/classfile/javaClasses.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/classfile/javaClasses.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -373,6 +373,7 @@
   static int _inheritedAccessControlContext_offset;
   static int _priority_offset;
   static int _eetop_offset;
+  static int _interrupted_offset;
   static int _daemon_offset;
   static int _stillborn_offset;
   static int _stackSize_offset;
@@ -391,6 +392,9 @@
   static JavaThread* thread(oop java_thread);
   // Set JavaThread for instance
   static void set_thread(oop java_thread, JavaThread* thread);
+  // Interrupted status
+  static bool interrupted(oop java_thread);
+  static void set_interrupted(oop java_thread, bool val);
   // Name
   static oop name(oop java_thread);
   static void set_name(oop java_thread, oop name);
--- a/src/hotspot/share/classfile/vmSymbols.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/classfile/vmSymbols.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -568,7 +568,6 @@
     if (!InlineClassNatives) return true;
     break;
   case vmIntrinsics::_currentThread:
-  case vmIntrinsics::_isInterrupted:
     if (!InlineThreadNatives) return true;
     break;
   case vmIntrinsics::_floatToRawIntBits:
--- a/src/hotspot/share/classfile/vmSymbols.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/classfile/vmSymbols.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -135,6 +135,8 @@
   template(java_lang_VersionProps,                    "java/lang/VersionProps")                   \
   template(java_runtime_name_name,                    "java_runtime_name")                        \
   template(java_runtime_version_name,                 "java_runtime_version")                     \
+  template(java_runtime_vendor_version_name,          "VENDOR_VERSION")                           \
+  template(java_runtime_vendor_vm_bug_url_name,       "VENDOR_URL_VM_BUG")                        \
                                                                                                   \
   /* system initialization */                                                                     \
   template(initPhase1_name,                           "initPhase1")                               \
@@ -860,9 +862,6 @@
   do_intrinsic(_arraycopy,                java_lang_System,       arraycopy_name, arraycopy_signature,           F_S)   \
    do_name(     arraycopy_name,                                  "arraycopy")                                           \
    do_signature(arraycopy_signature,                             "(Ljava/lang/Object;ILjava/lang/Object;II)V")          \
-  do_intrinsic(_isInterrupted,            java_lang_Thread,       isInterrupted_name, isInterrupted_signature,   F_R)   \
-   do_name(     isInterrupted_name,                              "isInterrupted")                                       \
-   do_signature(isInterrupted_signature,                         "(Z)Z")                                                \
   do_intrinsic(_currentThread,            java_lang_Thread,       currentThread_name, currentThread_signature,   F_S)   \
    do_name(     currentThread_name,                              "currentThread")                                       \
    do_signature(currentThread_signature,                         "()Ljava/lang/Thread;")                                \
--- a/src/hotspot/share/compiler/compilerDefinitions.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/compiler/compilerDefinitions.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -37,6 +37,32 @@
   "jvmci"
 };
 
+#ifdef TIERED
+bool CompilationModeFlag::_quick_only = false;
+bool CompilationModeFlag::_high_only = false;
+bool CompilationModeFlag::_high_only_quick_internal = false;
+
+
+bool CompilationModeFlag::initialize() {
+  if (CompilationMode != NULL) {
+    if (strcmp(CompilationMode, "default") == 0) {
+      // Do nothing, just support the "default" keyword.
+    } else if (strcmp(CompilationMode, "quick-only") == 0) {
+      _quick_only = true;
+    } else if (strcmp(CompilationMode, "high-only") == 0) {
+      _high_only = true;
+    } else if (strcmp(CompilationMode, "high-only-quick-internal") == 0) {
+      _high_only_quick_internal = true;
+    } else {
+        jio_fprintf(defaultStream::error_stream(), "Unsupported compilation mode '%s', supported modes are: quick-only, high-only, high-only-quick-internal\n", CompilationMode);
+        return false;
+      }
+    }
+  return true;
+}
+
+#endif
+
 #if defined(COMPILER2)
 CompLevel  CompLevel_highest_tier      = CompLevel_full_optimization;  // pure C2 and tiered or JVMCI and tiered
 #elif defined(COMPILER1)
@@ -208,6 +234,12 @@
     vm_exit_during_initialization("Negative value specified for CompileThresholdScaling", NULL);
   }
 
+  if (CompilationModeFlag::disable_intermediate()) {
+    if (FLAG_IS_DEFAULT(Tier0ProfilingStartPercentage)) {
+      FLAG_SET_DEFAULT(Tier0ProfilingStartPercentage, 33);
+    }
+  }
+
   // Scale tiered compilation thresholds.
   // CompileThresholdScaling == 0.0 is equivalent to -Xint and leaves compilation thresholds unchanged.
   if (!FLAG_IS_DEFAULT(CompileThresholdScaling) && CompileThresholdScaling > 0.0) {
@@ -234,6 +266,29 @@
     FLAG_SET_ERGO(Tier4MinInvocationThreshold, scaled_compile_threshold(Tier4MinInvocationThreshold));
     FLAG_SET_ERGO(Tier4CompileThreshold, scaled_compile_threshold(Tier4CompileThreshold));
     FLAG_SET_ERGO(Tier4BackEdgeThreshold, scaled_compile_threshold(Tier4BackEdgeThreshold));
+
+    if (CompilationModeFlag::disable_intermediate()) {
+      FLAG_SET_ERGO(Tier40InvocationThreshold, scaled_compile_threshold(Tier40InvocationThreshold));
+      FLAG_SET_ERGO(Tier40MinInvocationThreshold, scaled_compile_threshold(Tier40MinInvocationThreshold));
+      FLAG_SET_ERGO(Tier40CompileThreshold, scaled_compile_threshold(Tier40CompileThreshold));
+      FLAG_SET_ERGO(Tier40BackEdgeThreshold, scaled_compile_threshold(Tier40BackEdgeThreshold));
+    }
+
+#if INCLUDE_AOT
+    if (UseAOT) {
+      FLAG_SET_ERGO(Tier3AOTInvocationThreshold, scaled_compile_threshold(Tier3AOTInvocationThreshold));
+      FLAG_SET_ERGO(Tier3AOTMinInvocationThreshold, scaled_compile_threshold(Tier3AOTMinInvocationThreshold));
+      FLAG_SET_ERGO(Tier3AOTCompileThreshold, scaled_compile_threshold(Tier3AOTCompileThreshold));
+      FLAG_SET_ERGO(Tier3AOTBackEdgeThreshold, scaled_compile_threshold(Tier3AOTBackEdgeThreshold));
+
+      if (CompilationModeFlag::disable_intermediate()) {
+        FLAG_SET_ERGO(Tier0AOTInvocationThreshold, scaled_compile_threshold(Tier0AOTInvocationThreshold));
+        FLAG_SET_ERGO(Tier0AOTMinInvocationThreshold, scaled_compile_threshold(Tier0AOTMinInvocationThreshold));
+        FLAG_SET_ERGO(Tier0AOTCompileThreshold, scaled_compile_threshold(Tier0AOTCompileThreshold));
+        FLAG_SET_ERGO(Tier0AOTBackEdgeThreshold, scaled_compile_threshold(Tier0AOTBackEdgeThreshold));
+      }
+    }
+#endif // INCLUDE_AOT
   }
 }
 
@@ -245,11 +300,6 @@
     if (FLAG_IS_DEFAULT(TypeProfileWidth)) {
       FLAG_SET_DEFAULT(TypeProfileWidth, 8);
     }
-    if (TieredStopAtLevel != CompLevel_full_optimization) {
-      // Currently JVMCI compiler can only work at the full optimization level
-      warning("forcing TieredStopAtLevel to full optimization because JVMCI is enabled");
-      FLAG_SET_ERGO(TieredStopAtLevel, CompLevel_full_optimization);
-    }
     if (FLAG_IS_DEFAULT(TypeProfileLevel)) {
       FLAG_SET_DEFAULT(TypeProfileLevel, 0);
     }
@@ -270,11 +320,26 @@
         }
       }
     } else {
+#ifdef TIERED
+      if (!TieredCompilation) {
+         warning("Disabling tiered compilation with non-native JVMCI compiler is not recommended. "
+                 "Turning on tiered compilation and disabling intermediate compilation levels instead. ");
+         FLAG_SET_ERGO(TieredCompilation, true);
+         if (CompilationModeFlag::normal()) {
+           CompilationModeFlag::set_high_only_quick_internal(true);
+         }
+         if (CICompilerCount < 2 && CompilationModeFlag::quick_internal()) {
+            warning("Increasing number of compiler threads for JVMCI compiler.");
+            FLAG_SET_ERGO(CICompilerCount, 2);
+         }
+      }
+#else // TIERED
       // Adjust the on stack replacement percentage to avoid early
       // OSR compilations while JVMCI itself is warming up
       if (FLAG_IS_DEFAULT(OnStackReplacePercentage)) {
         FLAG_SET_DEFAULT(OnStackReplacePercentage, 933);
       }
+#endif // !TIERED
       // JVMCI needs values not less than defaults
       if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) {
         FLAG_SET_DEFAULT(ReservedCodeCacheSize, MAX2(64*M, ReservedCodeCacheSize));
--- a/src/hotspot/share/compiler/compilerDefinitions.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/compiler/compilerDefinitions.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -62,6 +62,24 @@
   CompLevel_full_optimization = 4          // C2 or JVMCI
 };
 
+class CompilationModeFlag : AllStatic {
+  static bool _quick_only;
+  static bool _high_only;
+  static bool _high_only_quick_internal;
+
+public:
+  static bool initialize();
+  static bool normal()                   { return !quick_only() && !high_only() && !high_only_quick_internal(); }
+  static bool quick_only()               { return _quick_only;               }
+  static bool high_only()                { return _high_only;                }
+  static bool high_only_quick_internal() { return _high_only_quick_internal; }
+
+  static bool disable_intermediate()     { return high_only() || high_only_quick_internal(); }
+  static bool quick_internal()           { return !high_only(); }
+
+  static void set_high_only_quick_internal(bool x) { _high_only_quick_internal = x; }
+};
+
 extern CompLevel CompLevel_highest_tier;
 extern CompLevel CompLevel_initial_compile;
 
--- a/src/hotspot/share/compiler/tieredThresholdPolicy.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/compiler/tieredThresholdPolicy.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -28,6 +28,7 @@
 #include "compiler/tieredThresholdPolicy.hpp"
 #include "memory/resourceArea.hpp"
 #include "runtime/arguments.hpp"
+#include "runtime/frame.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/safepointVerifiers.hpp"
@@ -42,43 +43,61 @@
 #include "c1/c1_Compiler.hpp"
 #include "opto/c2compiler.hpp"
 
-template<CompLevel level>
-bool TieredThresholdPolicy::call_predicate_helper(int i, int b, double scale, Method* method) {
+bool TieredThresholdPolicy::call_predicate_helper(Method* method, CompLevel cur_level, int i, int b, double scale) {
   double threshold_scaling;
   if (CompilerOracle::has_option_value(method, "CompileThresholdScaling", threshold_scaling)) {
     scale *= threshold_scaling;
   }
-  switch(level) {
+  switch(cur_level) {
   case CompLevel_aot:
-    return (i >= Tier3AOTInvocationThreshold * scale) ||
-           (i >= Tier3AOTMinInvocationThreshold * scale && i + b >= Tier3AOTCompileThreshold * scale);
+    if (CompilationModeFlag::disable_intermediate()) {
+      return (i >= Tier0AOTInvocationThreshold * scale) ||
+             (i >= Tier0AOTMinInvocationThreshold * scale && i + b >= Tier0AOTCompileThreshold * scale);
+    } else {
+      return (i >= Tier3AOTInvocationThreshold * scale) ||
+             (i >= Tier3AOTMinInvocationThreshold * scale && i + b >= Tier3AOTCompileThreshold * scale);
+    }
   case CompLevel_none:
+    if (CompilationModeFlag::disable_intermediate()) {
+      return (i >= Tier40InvocationThreshold * scale) ||
+             (i >= Tier40MinInvocationThreshold * scale && i + b >= Tier40CompileThreshold * scale);
+    }
+    // Fall through
   case CompLevel_limited_profile:
     return (i >= Tier3InvocationThreshold * scale) ||
            (i >= Tier3MinInvocationThreshold * scale && i + b >= Tier3CompileThreshold * scale);
   case CompLevel_full_profile:
    return (i >= Tier4InvocationThreshold * scale) ||
           (i >= Tier4MinInvocationThreshold * scale && i + b >= Tier4CompileThreshold * scale);
+  default:
+   return true;
   }
-  return true;
 }
 
-template<CompLevel level>
-bool TieredThresholdPolicy::loop_predicate_helper(int i, int b, double scale, Method* method) {
+bool TieredThresholdPolicy::loop_predicate_helper(Method* method, CompLevel cur_level, int i, int b, double scale) {
   double threshold_scaling;
   if (CompilerOracle::has_option_value(method, "CompileThresholdScaling", threshold_scaling)) {
     scale *= threshold_scaling;
   }
-  switch(level) {
+  switch(cur_level) {
   case CompLevel_aot:
-    return b >= Tier3AOTBackEdgeThreshold * scale;
+    if (CompilationModeFlag::disable_intermediate()) {
+      return b >= Tier0AOTBackEdgeThreshold * scale;
+    } else {
+      return b >= Tier3AOTBackEdgeThreshold * scale;
+    }
   case CompLevel_none:
+    if (CompilationModeFlag::disable_intermediate()) {
+      return b >= Tier40BackEdgeThreshold * scale;
+    }
+    // Fall through
   case CompLevel_limited_profile:
     return b >= Tier3BackEdgeThreshold * scale;
   case CompLevel_full_profile:
     return b >= Tier4BackEdgeThreshold * scale;
+  default:
+    return true;
   }
-  return true;
 }
 
 // Simple methods are as good being compiled with C1 as C2.
@@ -91,18 +110,17 @@
   return false;
 }
 
-bool TieredThresholdPolicy::should_compile_at_level_simple(Method* method) {
-  if (TieredThresholdPolicy::is_trivial(method)) {
-    return true;
-  }
+bool TieredThresholdPolicy::force_comp_at_level_simple(Method* method) {
+  if (CompilationModeFlag::quick_internal()) {
 #if INCLUDE_JVMCI
-  if (UseJVMCICompiler) {
-    AbstractCompiler* comp = CompileBroker::compiler(CompLevel_full_optimization);
-    if (comp != NULL && comp->is_jvmci() && ((JVMCICompiler*) comp)->force_comp_at_level_simple(method)) {
-      return true;
+    if (UseJVMCICompiler) {
+      AbstractCompiler* comp = CompileBroker::compiler(CompLevel_full_optimization);
+      if (comp != NULL && comp->is_jvmci() && ((JVMCICompiler*) comp)->force_comp_at_level_simple(method)) {
+        return true;
+      }
     }
+#endif
   }
-#endif
   return false;
 }
 
@@ -181,7 +199,12 @@
   tty->print("@%d queues=%d,%d", bci, CompileBroker::queue_size(CompLevel_full_profile),
                                       CompileBroker::queue_size(CompLevel_full_optimization));
 
-  print_specific(type, mh, imh, bci, level);
+  tty->print(" rate=");
+  if (mh->prev_time() == 0) tty->print("n/a");
+  else tty->print("%f", mh->rate());
+
+  tty->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback),
+                               threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback));
 
   if (type != COMPILE) {
     print_counters("", mh);
@@ -216,9 +239,11 @@
   tty->print_cr("]");
 }
 
+
 void TieredThresholdPolicy::initialize() {
   int count = CICompilerCount;
-  bool c1_only = TieredStopAtLevel < CompLevel_full_optimization;
+  bool c1_only = TieredStopAtLevel < CompLevel_full_optimization || CompilationModeFlag::quick_only();
+  bool c2_only = CompilationModeFlag::high_only();
 #ifdef _LP64
   // Turn on ergonomic compiler count selection
   if (FLAG_IS_DEFAULT(CICompilerCountPerCPU) && FLAG_IS_DEFAULT(CICompilerCount)) {
@@ -257,6 +282,8 @@
   if (c1_only) {
     // No C2 compiler thread required
     set_c1_count(count);
+  } else if (c2_only) {
+    set_c2_count(count);
   } else {
     set_c1_count(MAX2(count / 3, 1));
     set_c2_count(MAX2(count - c1_count(), 1));
@@ -413,7 +440,7 @@
     method_back_branch_event(method, inlinee, bci, comp_level, nm, thread);
     // Check if event led to a higher level OSR compilation
     CompLevel expected_comp_level = comp_level;
-    if (inlinee->is_not_osr_compilable(expected_comp_level)) {
+    if (!CompilationModeFlag::disable_intermediate() && inlinee->is_not_osr_compilable(expected_comp_level)) {
       // It's not possble to reach the expected level so fall back to simple.
       expected_comp_level = CompLevel_simple;
     }
@@ -430,7 +457,25 @@
 // Check if the method can be compiled, change level if necessary
 void TieredThresholdPolicy::compile(const methodHandle& mh, int bci, CompLevel level, JavaThread* thread) {
   assert(level <= TieredStopAtLevel, "Invalid compilation level");
+  if (CompilationModeFlag::quick_only()) {
+    assert(level <= CompLevel_simple, "Invalid compilation level");
+  } else if (CompilationModeFlag::disable_intermediate()) {
+    assert(level != CompLevel_full_profile && level != CompLevel_limited_profile, "C1 profiling levels shouldn't be used with intermediate levels disabled");
+  }
+
   if (level == CompLevel_none) {
+    if (mh->has_compiled_code()) {
+      // Happens when we switch from AOT to interpreter to profile.
+      MutexLocker ml(Compile_lock);
+      NoSafepointVerifier nsv;
+      if (mh->has_compiled_code()) {
+        mh->code()->make_not_used();
+      }
+      // Deoptimize immediately (we don't have to wait for a compile).
+      RegisterMap map(thread, false);
+      frame fr = thread->last_frame().sender(&map);
+      Deoptimization::deoptimize_frame(thread, fr.id());
+    }
     return;
   }
   if (level == CompLevel_aot) {
@@ -452,26 +497,28 @@
     return;
   }
 
-  // Check if the method can be compiled. If it cannot be compiled with C1, continue profiling
-  // in the interpreter and then compile with C2 (the transition function will request that,
-  // see common() ). If the method cannot be compiled with C2 but still can with C1, compile it with
-  // pure C1.
-  if ((bci == InvocationEntryBci && !can_be_compiled(mh, level))) {
-    if (level == CompLevel_full_optimization && can_be_compiled(mh, CompLevel_simple)) {
-      compile(mh, bci, CompLevel_simple, thread);
+  if (!CompilationModeFlag::disable_intermediate()) {
+    // Check if the method can be compiled. If it cannot be compiled with C1, continue profiling
+    // in the interpreter and then compile with C2 (the transition function will request that,
+    // see common() ). If the method cannot be compiled with C2 but still can with C1, compile it with
+    // pure C1.
+    if ((bci == InvocationEntryBci && !can_be_compiled(mh, level))) {
+      if (level == CompLevel_full_optimization && can_be_compiled(mh, CompLevel_simple)) {
+        compile(mh, bci, CompLevel_simple, thread);
+      }
+      return;
     }
-    return;
-  }
-  if ((bci != InvocationEntryBci && !can_be_osr_compiled(mh, level))) {
-    if (level == CompLevel_full_optimization && can_be_osr_compiled(mh, CompLevel_simple)) {
-      nmethod* osr_nm = mh->lookup_osr_nmethod_for(bci, CompLevel_simple, false);
-      if (osr_nm != NULL && osr_nm->comp_level() > CompLevel_simple) {
-        // Invalidate the existing OSR nmethod so that a compile at CompLevel_simple is permitted.
-        osr_nm->make_not_entrant();
+    if ((bci != InvocationEntryBci && !can_be_osr_compiled(mh, level))) {
+      if (level == CompLevel_full_optimization && can_be_osr_compiled(mh, CompLevel_simple)) {
+        nmethod* osr_nm = mh->lookup_osr_nmethod_for(bci, CompLevel_simple, false);
+        if (osr_nm != NULL && osr_nm->comp_level() > CompLevel_simple) {
+          // Invalidate the existing OSR nmethod so that a compile at CompLevel_simple is permitted.
+          osr_nm->make_not_entrant();
+        }
+        compile(mh, bci, CompLevel_simple, thread);
       }
-      compile(mh, bci, CompLevel_simple, thread);
+      return;
     }
-    return;
   }
   if (bci != InvocationEntryBci && mh->is_not_osr_compilable(level)) {
     return;
@@ -480,29 +527,12 @@
     if (PrintTieredEvents) {
       print_event(COMPILE, mh, mh, bci, level);
     }
-    submit_compile(mh, bci, level, thread);
+    int hot_count = (bci == InvocationEntryBci) ? mh->invocation_count() : mh->backedge_count();
+    update_rate(os::javaTimeMillis(), mh());
+    CompileBroker::compile_method(mh, bci, level, mh, hot_count, CompileTask::Reason_Tiered, thread);
   }
 }
 
-// Update the rate and submit compile
-void TieredThresholdPolicy::submit_compile(const methodHandle& mh, int bci, CompLevel level, JavaThread* thread) {
-  int hot_count = (bci == InvocationEntryBci) ? mh->invocation_count() : mh->backedge_count();
-  update_rate(os::javaTimeMillis(), mh());
-  CompileBroker::compile_method(mh, bci, level, mh, hot_count, CompileTask::Reason_Tiered, thread);
-}
-
-// Print an event.
-void TieredThresholdPolicy::print_specific(EventType type, const methodHandle& mh, const methodHandle& imh,
-                                             int bci, CompLevel level) {
-  tty->print(" rate=");
-  if (mh->prev_time() == 0) tty->print("n/a");
-  else tty->print("%f", mh->rate());
-
-  tty->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback),
-                               threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback));
-
-}
-
 // update_rate() is called from select_task() while holding a compile queue lock.
 void TieredThresholdPolicy::update_rate(jlong t, Method* m) {
   // Skip update if counters are absent.
@@ -585,27 +615,30 @@
   if (mdo != NULL) {
     int i = mdo->invocation_count_delta();
     int b = mdo->backedge_count_delta();
-    return call_predicate_helper<CompLevel_full_profile>(i, b, 1, method);
+    return call_predicate_helper(method, CompilationModeFlag::disable_intermediate() ? CompLevel_none : CompLevel_full_profile, i, b, 1);
   }
   return false;
 }
 
 double TieredThresholdPolicy::threshold_scale(CompLevel level, int feedback_k) {
-  double queue_size = CompileBroker::queue_size(level);
   int comp_count = compiler_count(level);
-  double k = queue_size / (feedback_k * comp_count) + 1;
+  if (comp_count > 0) {
+    double queue_size = CompileBroker::queue_size(level);
+    double k = queue_size / (feedback_k * comp_count) + 1;
 
-  // Increase C1 compile threshold when the code cache is filled more
-  // than specified by IncreaseFirstTierCompileThresholdAt percentage.
-  // The main intention is to keep enough free space for C2 compiled code
-  // to achieve peak performance if the code cache is under stress.
-  if ((TieredStopAtLevel == CompLevel_full_optimization) && (level != CompLevel_full_optimization))  {
-    double current_reverse_free_ratio = CodeCache::reverse_free_ratio(CodeCache::get_code_blob_type(level));
-    if (current_reverse_free_ratio > _increase_threshold_at_ratio) {
-      k *= exp(current_reverse_free_ratio - _increase_threshold_at_ratio);
+    // Increase C1 compile threshold when the code cache is filled more
+    // than specified by IncreaseFirstTierCompileThresholdAt percentage.
+    // The main intention is to keep enough free space for C2 compiled code
+    // to achieve peak performance if the code cache is under stress.
+    if (!CompilationModeFlag::disable_intermediate() && TieredStopAtLevel == CompLevel_full_optimization && level != CompLevel_full_optimization)  {
+      double current_reverse_free_ratio = CodeCache::reverse_free_ratio(CodeCache::get_code_blob_type(level));
+      if (current_reverse_free_ratio > _increase_threshold_at_ratio) {
+        k *= exp(current_reverse_free_ratio - _increase_threshold_at_ratio);
+      }
     }
+    return k;
   }
-  return k;
+  return 1;
 }
 
 // Call and loop predicates determine whether a transition to a higher
@@ -615,55 +648,71 @@
 // how many methods per compiler thread can be in the queue before
 // the threshold values double.
 bool TieredThresholdPolicy::loop_predicate(int i, int b, CompLevel cur_level, Method* method) {
+  double k = 1;
   switch(cur_level) {
   case CompLevel_aot: {
-    double k = threshold_scale(CompLevel_full_profile, Tier3LoadFeedback);
-    return loop_predicate_helper<CompLevel_aot>(i, b, k, method);
+    k = CompilationModeFlag::disable_intermediate() ? 1 : threshold_scale(CompLevel_full_profile, Tier3LoadFeedback);
+    break;
   }
-  case CompLevel_none:
+  case CompLevel_none: {
+    if (CompilationModeFlag::disable_intermediate()) {
+      k = threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback);
+      break;
+    }
+  }
+  // Fall through
   case CompLevel_limited_profile: {
-    double k = threshold_scale(CompLevel_full_profile, Tier3LoadFeedback);
-    return loop_predicate_helper<CompLevel_none>(i, b, k, method);
+    k = threshold_scale(CompLevel_full_profile, Tier3LoadFeedback);
+    break;
   }
   case CompLevel_full_profile: {
-    double k = threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback);
-    return loop_predicate_helper<CompLevel_full_profile>(i, b, k, method);
+    k = threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback);
+    break;
   }
   default:
     return true;
   }
+ return loop_predicate_helper(method, cur_level, i, b, k);
 }
 
 bool TieredThresholdPolicy::call_predicate(int i, int b, CompLevel cur_level, Method* method) {
+  double k = 1;
   switch(cur_level) {
   case CompLevel_aot: {
-    double k = threshold_scale(CompLevel_full_profile, Tier3LoadFeedback);
-    return call_predicate_helper<CompLevel_aot>(i, b, k, method);
+    k = CompilationModeFlag::disable_intermediate() ? 1 : threshold_scale(CompLevel_full_profile, Tier3LoadFeedback);
+    break;
   }
-  case CompLevel_none:
+  case CompLevel_none: {
+    if (CompilationModeFlag::disable_intermediate()) {
+      k = threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback);
+      break;
+    }
+  }
+  // Fall through
   case CompLevel_limited_profile: {
-    double k = threshold_scale(CompLevel_full_profile, Tier3LoadFeedback);
-    return call_predicate_helper<CompLevel_none>(i, b, k, method);
+    k = threshold_scale(CompLevel_full_profile, Tier3LoadFeedback);
+    break;
   }
   case CompLevel_full_profile: {
-    double k = threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback);
-    return call_predicate_helper<CompLevel_full_profile>(i, b, k, method);
+    k = threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback);
+    break;
   }
   default:
     return true;
   }
+  return call_predicate_helper(method, cur_level, i, b, k);
 }
 
 // Determine is a method is mature.
 bool TieredThresholdPolicy::is_mature(Method* method) {
-  if (should_compile_at_level_simple(method)) return true;
+  if (is_trivial(method) || force_comp_at_level_simple(method)) return true;
   MethodData* mdo = method->method_data();
   if (mdo != NULL) {
     int i = mdo->invocation_count();
     int b = mdo->backedge_count();
     double k = ProfileMaturityPercentage / 100.0;
-    return call_predicate_helper<CompLevel_full_profile>(i, b, k, method) ||
-           loop_predicate_helper<CompLevel_full_profile>(i, b, k, method);
+    CompLevel main_profile_level = CompilationModeFlag::disable_intermediate() ? CompLevel_none : CompLevel_full_profile;
+    return call_predicate_helper(method, main_profile_level, i, b, k) || loop_predicate_helper(method, main_profile_level, i, b, k);
   }
   return false;
 }
@@ -672,13 +721,16 @@
 // start profiling without waiting for the compiled method to arrive.
 // We also take the load on compilers into the account.
 bool TieredThresholdPolicy::should_create_mdo(Method* method, CompLevel cur_level) {
-  if (cur_level == CompLevel_none &&
-      CompileBroker::queue_size(CompLevel_full_optimization) <=
-      Tier3DelayOn * compiler_count(CompLevel_full_optimization)) {
-    int i = method->invocation_count();
-    int b = method->backedge_count();
-    double k = Tier0ProfilingStartPercentage / 100.0;
-    return call_predicate_helper<CompLevel_none>(i, b, k, method) || loop_predicate_helper<CompLevel_none>(i, b, k, method);
+  if (cur_level != CompLevel_none || force_comp_at_level_simple(method)) {
+    return false;
+  }
+  int i = method->invocation_count();
+  int b = method->backedge_count();
+  double k = Tier0ProfilingStartPercentage / 100.0;
+
+  // If the top level compiler is not keeping up, delay profiling.
+  if (CompileBroker::queue_size(CompLevel_full_optimization) <=  (CompilationModeFlag::disable_intermediate() ? Tier0Delay : Tier3DelayOn) * compiler_count(CompLevel_full_optimization)) {
+    return call_predicate_helper(method, CompLevel_none, i, b, k) || loop_predicate_helper(method, CompLevel_none, i, b, k);
   }
   return false;
 }
@@ -714,7 +766,7 @@
  *   1 - pure C1 (CompLevel_simple)
  *   2 - C1 with invocation and backedge counting (CompLevel_limited_profile)
  *   3 - C1 with full profiling (CompLevel_full_profile)
- *   4 - C2 (CompLevel_full_optimization)
+ *   4 - C2 or Graal (CompLevel_full_optimization)
  *
  * Common state transition patterns:
  * a. 0 -> 3 -> 4.
@@ -752,106 +804,129 @@
   int i = method->invocation_count();
   int b = method->backedge_count();
 
-  if (should_compile_at_level_simple(method)) {
+  if (force_comp_at_level_simple(method)) {
     next_level = CompLevel_simple;
   } else {
-    switch(cur_level) {
+    if (!CompilationModeFlag::disable_intermediate() && is_trivial(method)) {
+      next_level = CompLevel_simple;
+    } else {
+      switch(cur_level) {
       default: break;
-      case CompLevel_aot: {
-      // If we were at full profile level, would we switch to full opt?
-      if (common(p, method, CompLevel_full_profile, disable_feedback) == CompLevel_full_optimization) {
-        next_level = CompLevel_full_optimization;
-      } else if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
-                               Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
-                               (this->*p)(i, b, cur_level, method))) {
-        next_level = CompLevel_full_profile;
-      }
-    }
-    break;
-    case CompLevel_none:
-      // If we were at full profile level, would we switch to full opt?
-      if (common(p, method, CompLevel_full_profile, disable_feedback) == CompLevel_full_optimization) {
-        next_level = CompLevel_full_optimization;
-      } else if ((this->*p)(i, b, cur_level, method)) {
-#if INCLUDE_JVMCI
-        if (EnableJVMCI && UseJVMCICompiler) {
-          // Since JVMCI takes a while to warm up, its queue inevitably backs up during
-          // early VM execution. As of 2014-06-13, JVMCI's inliner assumes that the root
-          // compilation method and all potential inlinees have mature profiles (which
-          // includes type profiling). If it sees immature profiles, JVMCI's inliner
-          // can perform pathologically bad (e.g., causing OutOfMemoryErrors due to
-          // exploring/inlining too many graphs). Since a rewrite of the inliner is
-          // in progress, we simply disable the dialing back heuristic for now and will
-          // revisit this decision once the new inliner is completed.
-          next_level = CompLevel_full_profile;
-        } else
-#endif
-        {
-          // C1-generated fully profiled code is about 30% slower than the limited profile
-          // code that has only invocation and backedge counters. The observation is that
-          // if C2 queue is large enough we can spend too much time in the fully profiled code
-          // while waiting for C2 to pick the method from the queue. To alleviate this problem
-          // we introduce a feedback on the C2 queue size. If the C2 queue is sufficiently long
-          // we choose to compile a limited profiled version and then recompile with full profiling
-          // when the load on C2 goes down.
-          if (!disable_feedback && CompileBroker::queue_size(CompLevel_full_optimization) >
-              Tier3DelayOn * compiler_count(CompLevel_full_optimization)) {
-            next_level = CompLevel_limited_profile;
-          } else {
+      case CompLevel_aot:
+        if (CompilationModeFlag::disable_intermediate()) {
+          if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
+                                   Tier0Delay * compiler_count(CompLevel_full_optimization) &&
+                                  (this->*p)(i, b, cur_level, method))) {
+            next_level = CompLevel_none;
+          }
+        } else {
+          // If we were at full profile level, would we switch to full opt?
+          if (common(p, method, CompLevel_full_profile, disable_feedback) == CompLevel_full_optimization) {
+            next_level = CompLevel_full_optimization;
+          } else if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
+                                          Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
+                                         (this->*p)(i, b, cur_level, method))) {
             next_level = CompLevel_full_profile;
           }
         }
-      }
-      break;
-    case CompLevel_limited_profile:
-      if (is_method_profiled(method)) {
-        // Special case: we got here because this method was fully profiled in the interpreter.
-        next_level = CompLevel_full_optimization;
-      } else {
-        MethodData* mdo = method->method_data();
-        if (mdo != NULL) {
-          if (mdo->would_profile()) {
+        break;
+      case CompLevel_none:
+        if (CompilationModeFlag::disable_intermediate()) {
+          MethodData* mdo = method->method_data();
+          if (mdo != NULL) {
+            // If mdo exists that means we are in a normal profiling mode.
+            int mdo_i = mdo->invocation_count_delta();
+            int mdo_b = mdo->backedge_count_delta();
+            if ((this->*p)(mdo_i, mdo_b, cur_level, method)) {
+              next_level = CompLevel_full_optimization;
+            }
+          }
+        } else {
+          // If we were at full profile level, would we switch to full opt?
+          if (common(p, method, CompLevel_full_profile, disable_feedback) == CompLevel_full_optimization) {
+            next_level = CompLevel_full_optimization;
+          } else if ((this->*p)(i, b, cur_level, method)) {
+  #if INCLUDE_JVMCI
+            if (EnableJVMCI && UseJVMCICompiler) {
+              // Since JVMCI takes a while to warm up, its queue inevitably backs up during
+              // early VM execution. As of 2014-06-13, JVMCI's inliner assumes that the root
+              // compilation method and all potential inlinees have mature profiles (which
+              // includes type profiling). If it sees immature profiles, JVMCI's inliner
+              // can perform pathologically bad (e.g., causing OutOfMemoryErrors due to
+              // exploring/inlining too many graphs). Since a rewrite of the inliner is
+              // in progress, we simply disable the dialing back heuristic for now and will
+              // revisit this decision once the new inliner is completed.
+              next_level = CompLevel_full_profile;
+            } else
+  #endif
+            {
+              // C1-generated fully profiled code is about 30% slower than the limited profile
+              // code that has only invocation and backedge counters. The observation is that
+              // if C2 queue is large enough we can spend too much time in the fully profiled code
+              // while waiting for C2 to pick the method from the queue. To alleviate this problem
+              // we introduce a feedback on the C2 queue size. If the C2 queue is sufficiently long
+              // we choose to compile a limited profiled version and then recompile with full profiling
+              // when the load on C2 goes down.
+              if (!disable_feedback && CompileBroker::queue_size(CompLevel_full_optimization) >
+                  Tier3DelayOn * compiler_count(CompLevel_full_optimization)) {
+                next_level = CompLevel_limited_profile;
+              } else {
+                next_level = CompLevel_full_profile;
+              }
+            }
+          }
+        }
+        break;
+      case CompLevel_limited_profile:
+        if (is_method_profiled(method)) {
+          // Special case: we got here because this method was fully profiled in the interpreter.
+          next_level = CompLevel_full_optimization;
+        } else {
+          MethodData* mdo = method->method_data();
+          if (mdo != NULL) {
+            if (mdo->would_profile()) {
+              if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
+                                       Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
+                                       (this->*p)(i, b, cur_level, method))) {
+                next_level = CompLevel_full_profile;
+              }
+            } else {
+              next_level = CompLevel_full_optimization;
+            }
+          } else {
+            // If there is no MDO we need to profile
             if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
                                      Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
                                      (this->*p)(i, b, cur_level, method))) {
               next_level = CompLevel_full_profile;
             }
-          } else {
-            next_level = CompLevel_full_optimization;
-          }
-        } else {
-          // If there is no MDO we need to profile
-          if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
-                                   Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
-                                   (this->*p)(i, b, cur_level, method))) {
-            next_level = CompLevel_full_profile;
           }
         }
-      }
-      break;
-    case CompLevel_full_profile:
-      {
-        MethodData* mdo = method->method_data();
-        if (mdo != NULL) {
-          if (mdo->would_profile()) {
-            int mdo_i = mdo->invocation_count_delta();
-            int mdo_b = mdo->backedge_count_delta();
-            if ((this->*p)(mdo_i, mdo_b, cur_level, method)) {
+        break;
+      case CompLevel_full_profile:
+        {
+          MethodData* mdo = method->method_data();
+          if (mdo != NULL) {
+            if (mdo->would_profile()) {
+              int mdo_i = mdo->invocation_count_delta();
+              int mdo_b = mdo->backedge_count_delta();
+              if ((this->*p)(mdo_i, mdo_b, cur_level, method)) {
+                next_level = CompLevel_full_optimization;
+              }
+            } else {
               next_level = CompLevel_full_optimization;
             }
-          } else {
-            next_level = CompLevel_full_optimization;
           }
         }
+        break;
       }
-      break;
     }
   }
-  return MIN2(next_level, (CompLevel)TieredStopAtLevel);
+  return MIN2(next_level, CompilationModeFlag::quick_only() ? CompLevel_simple : (CompLevel)TieredStopAtLevel);
 }
 
 // Determine if a method should be compiled with a normal entry point at a different level.
-CompLevel TieredThresholdPolicy::call_event(Method* method, CompLevel cur_level, JavaThread * thread) {
+CompLevel TieredThresholdPolicy::call_event(Method* method, CompLevel cur_level, JavaThread* thread) {
   CompLevel osr_level = MIN2((CompLevel) method->highest_osr_comp_level(),
                              common(&TieredThresholdPolicy::loop_predicate, method, cur_level, true));
   CompLevel next_level = common(&TieredThresholdPolicy::call_predicate, method, cur_level);
@@ -950,7 +1025,8 @@
       if (level == CompLevel_aot) {
         // Recompile the enclosing method to prevent infinite OSRs. Stay at AOT level while it's compiling.
         if (max_osr_level != CompLevel_none && !CompileBroker::compilation_is_in_queue(mh)) {
-          compile(mh, InvocationEntryBci, MIN2((CompLevel)TieredStopAtLevel, CompLevel_full_profile), thread);
+          CompLevel enclosing_level = MIN2(CompilationModeFlag::quick_only() ? CompLevel_simple : (CompLevel)TieredStopAtLevel, CompLevel_full_profile);
+          compile(mh, InvocationEntryBci, enclosing_level, thread);
         }
       } else {
         // Current loop event level is not AOT
--- a/src/hotspot/share/compiler/tieredThresholdPolicy.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/compiler/tieredThresholdPolicy.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -222,22 +222,18 @@
 
   enum EventType { CALL, LOOP, COMPILE, REMOVE_FROM_QUEUE, UPDATE_IN_QUEUE, REPROFILE, MAKE_NOT_ENTRANT };
   void print_event(EventType type, const methodHandle& mh, const methodHandle& imh, int bci, CompLevel level);
-  // Print policy-specific information if necessary
-  void print_specific(EventType type, const methodHandle& mh, const methodHandle& imh, int bci, CompLevel level);
   // Check if the method can be compiled, change level if necessary
   void compile(const methodHandle& mh, int bci, CompLevel level, JavaThread* thread);
-  // Submit a given method for compilation
-  void submit_compile(const methodHandle& mh, int bci, CompLevel level, JavaThread* thread);
   // Simple methods are as good being compiled with C1 as C2.
   // This function tells if it's such a function.
   inline static bool is_trivial(Method* method);
   // Force method to be compiled at CompLevel_simple?
-  inline static bool should_compile_at_level_simple(Method* method);
+  inline bool force_comp_at_level_simple(Method* method);
 
   // Predicate helpers are used by .*_predicate() methods as well as others.
   // They check the given counter values, multiplied by the scale against the thresholds.
-  template<CompLevel level> static inline bool call_predicate_helper(int i, int b, double scale, Method* method);
-  template<CompLevel level> static inline bool loop_predicate_helper(int i, int b, double scale, Method* method);
+  inline bool call_predicate_helper(Method* method, CompLevel cur_level, int i, int b, double scale);
+  inline bool loop_predicate_helper(Method* method, CompLevel cur_level, int i, int b, double scale);
 
   // Get a compilation level for a given method.
   static CompLevel comp_level(Method* method);
--- a/src/hotspot/share/gc/g1/g1Trace.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/gc/g1/g1Trace.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -59,10 +59,10 @@
 };
 
 static void register_jfr_type_constants() {
-  JfrSerializer::register_serializer(TYPE_G1HEAPREGIONTYPE, false, true,
+  JfrSerializer::register_serializer(TYPE_G1HEAPREGIONTYPE, true,
                                      new G1HeapRegionTypeConstant());
 
-  JfrSerializer::register_serializer(TYPE_G1YCTYPE, false, true,
+  JfrSerializer::register_serializer(TYPE_G1YCTYPE, true,
                                      new G1YCTypeConstant());
 }
 
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -29,11 +29,6 @@
 #include "logging/log.hpp"
 #include "logging/logTag.hpp"
 
-ShenandoahPassiveHeuristics::ShenandoahPassiveHeuristics() : ShenandoahHeuristics() {
-  // Passive runs with max speed for allocation, because GC is always STW
-  SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahPacing);
-}
-
 bool ShenandoahPassiveHeuristics::should_start_gc() const {
   // Never do concurrent GCs.
   return false;
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -28,8 +28,6 @@
 
 class ShenandoahPassiveHeuristics : public ShenandoahHeuristics {
 public:
-  ShenandoahPassiveHeuristics();
-
   virtual bool should_start_gc() const;
 
   virtual bool should_process_references();
--- a/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -47,7 +47,6 @@
 
 void ShenandoahJFRSupport::register_jfr_type_serializers() {
   JfrSerializer::register_serializer(TYPE_SHENANDOAHHEAPREGIONSTATE,
-                                     false,
                                      true,
                                      new ShenandoahHeapRegionStateConstant());
 }
--- a/src/hotspot/share/gc/shenandoah/shenandoahPassiveMode.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/gc/shenandoah/shenandoahPassiveMode.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -32,8 +32,8 @@
   FLAG_SET_DEFAULT(ExplicitGCInvokesConcurrent, false);
   FLAG_SET_DEFAULT(ShenandoahImplicitGCInvokesConcurrent, false);
 
-  // Passive runs with max speed, reacts on allocation failure.
-  FLAG_SET_DEFAULT(ShenandoahPacing, false);
+  // Passive runs with max speed for allocation, because GC is always STW
+  SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahPacing);
 
   // No need for evacuation reserve with Full GC, only for Degenerated GC.
   if (!ShenandoahDegeneratedGC) {
--- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -695,8 +695,8 @@
     guarantee(cl.committed() == heap_committed,
               "%s: heap committed size must be consistent: heap-committed = " SIZE_FORMAT "%s, regions-committed = " SIZE_FORMAT "%s",
               label,
-              byte_size_in_exact_unit(heap_committed), proper_unit_for_byte_size(heap_committed),
-              byte_size_in_exact_unit(cl.committed()), proper_unit_for_byte_size(cl.committed()));
+              byte_size_in_proper_unit(heap_committed), proper_unit_for_byte_size(heap_committed),
+              byte_size_in_proper_unit(cl.committed()), proper_unit_for_byte_size(cl.committed()));
   }
 
   // Internal heap region checks
--- a/src/hotspot/share/gc/z/zTracer.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/gc/z/zTracer.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -59,11 +59,9 @@
 
 static void register_jfr_type_serializers() {
   JfrSerializer::register_serializer(TYPE_ZSTATISTICSCOUNTERTYPE,
-                                     false /* require_safepoint */,
                                      true /* permit_cache */,
                                      new ZStatisticsCounterTypeConstant());
   JfrSerializer::register_serializer(TYPE_ZSTATISTICSSAMPLERTYPE,
-                                     false /* require_safepoint */,
                                      true /* permit_cache */,
                                      new ZStatisticsSamplerTypeConstant());
 }
--- a/src/hotspot/share/include/jvm.h	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/include/jvm.h	Mon Nov 04 11:25:55 2019 +0000
@@ -248,16 +248,10 @@
 JNIEXPORT jobject JNICALL
 JVM_CurrentThread(JNIEnv *env, jclass threadClass);
 
-JNIEXPORT jint JNICALL
-JVM_CountStackFrames(JNIEnv *env, jobject thread);
-
 JNIEXPORT void JNICALL
 JVM_Interrupt(JNIEnv *env, jobject thread);
 
 JNIEXPORT jboolean JNICALL
-JVM_IsInterrupted(JNIEnv *env, jobject thread, jboolean clearInterrupted);
-
-JNIEXPORT jboolean JNICALL
 JVM_HoldsLock(JNIEnv *env, jclass threadClass, jobject obj);
 
 JNIEXPORT void JNICALL
@@ -1073,13 +1067,6 @@
     method_size_info main;     /* used everywhere else */
 } class_size_info;
 
-/*
- * Functions defined in libjava.so to perform string conversions.
- *
- */
-
-typedef jstring (*to_java_string_fn_t)(JNIEnv *env, char *str);
-
 #define JVM_RECOGNIZED_CLASS_MODIFIERS (JVM_ACC_PUBLIC | \
                                         JVM_ACC_FINAL | \
                                         JVM_ACC_SUPER | \
--- a/src/hotspot/share/interpreter/bytecode.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/interpreter/bytecode.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -147,7 +147,6 @@
 
 BasicType Bytecode_member_ref::result_type() const {
   ResultTypeFinder rts(signature());
-  rts.iterate();
   return rts.type();
 }
 
--- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -349,6 +349,7 @@
   _filename("filename", "Resulting recording filename, e.g. \\\"" JFR_FILENAME_EXAMPLE "\\\"", "STRING", false),
   _maxage("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, "0"),
   _maxsize("maxsize", "Maximum amount of bytes to keep (on disk) in (k)B, (M)B or (G)B, e.g. 500M, or 0 for no limit", "MEMORY SIZE", false, "0"),
+  _flush_interval("flush-interval", "Minimum time before flushing buffers, measured in (s)econds, e.g. 4 s, or 0 for flushing when a recording ends", "NANOTIME", false, "1s"),
   _dump_on_exit("dumponexit", "Dump running recording when JVM shuts down", "BOOLEAN", false),
   _path_to_gc_roots("path-to-gc-roots", "Collect path to GC roots", "BOOLEAN", false, "false") {
   _dcmdparser.add_dcmd_option(&_name);
@@ -359,6 +360,7 @@
   _dcmdparser.add_dcmd_option(&_filename);
   _dcmdparser.add_dcmd_option(&_maxage);
   _dcmdparser.add_dcmd_option(&_maxsize);
+  _dcmdparser.add_dcmd_option(&_flush_interval);
   _dcmdparser.add_dcmd_option(&_dump_on_exit);
   _dcmdparser.add_dcmd_option(&_path_to_gc_roots);
 };
@@ -411,6 +413,10 @@
     maxsize = JfrJavaSupport::new_java_lang_Long(_maxsize.value()._size, CHECK);
   }
 
+  jobject flush_interval = NULL;
+  if (_flush_interval.is_set()) {
+    flush_interval = JfrJavaSupport::new_java_lang_Long(_flush_interval.value()._nanotime, CHECK);
+  }
   jobject duration = NULL;
   if (_duration.is_set()) {
     duration = JfrJavaSupport::new_java_lang_Long(_duration.value()._nanotime, CHECK);
@@ -464,7 +470,7 @@
   static const char method[] = "execute";
   static const char signature[] = "(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Long;"
     "Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;"
-    "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;";
+    "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;";
 
   JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
   execute_args.set_receiver(h_dcmd_instance);
@@ -478,6 +484,7 @@
   execute_args.push_jobject(filename);
   execute_args.push_jobject(maxage);
   execute_args.push_jobject(maxsize);
+  execute_args.push_jobject(flush_interval);
   execute_args.push_jobject(dump_on_exit);
   execute_args.push_jobject(path_to_gc_roots);
 
--- a/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -90,6 +90,7 @@
   DCmdArgument<char*> _filename;
   DCmdArgument<NanoTimeArgument> _maxage;
   DCmdArgument<MemorySizeArgument> _maxsize;
+  DCmdArgument<NanoTimeArgument> _flush_interval;
   DCmdArgument<bool> _dump_on_exit;
   DCmdArgument<bool> _path_to_gc_roots;
 
--- a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1497,35 +1497,6 @@
   ik = new_ik;
 }
 
-// During retransform/redefine, copy the Method specific trace flags
-// from the previous ik ("the original klass") to the new ik ("the scratch_klass").
-// The open code for retransform/redefine does not know about these.
-// In doing this migration here, we ensure the new Methods (defined in scratch klass)
-// will carry over trace tags from the old Methods being replaced,
-// ensuring flag/tag continuity while being transparent to open code.
-static void copy_method_trace_flags(const InstanceKlass* the_original_klass, const InstanceKlass* the_scratch_klass) {
-  assert(the_original_klass != NULL, "invariant");
-  assert(the_scratch_klass != NULL, "invariant");
-  assert(the_original_klass->name() == the_scratch_klass->name(), "invariant");
-  const Array<Method*>* old_methods = the_original_klass->methods();
-  const Array<Method*>* new_methods = the_scratch_klass->methods();
-  const bool equal_array_length = old_methods->length() == new_methods->length();
-  // The Method array has the property of being sorted.
-  // If they are the same length, there is a one-to-one mapping.
-  // If they are unequal, there was a method added (currently only
-  // private static methods allowed to be added), use lookup.
-  for (int i = 0; i < old_methods->length(); ++i) {
-    const Method* const old_method = old_methods->at(i);
-    Method* const new_method = equal_array_length ? new_methods->at(i) :
-      the_scratch_klass->find_method(old_method->name(), old_method->signature());
-    assert(new_method != NULL, "invariant");
-    assert(new_method->name() == old_method->name(), "invariant");
-    assert(new_method->signature() == old_method->signature(), "invariant");
-    new_method->set_trace_flags(old_method->trace_flags());
-    assert(new_method->trace_flags() == old_method->trace_flags(), "invariant");
-  }
-}
-
 static bool is_retransforming(const InstanceKlass* ik, TRAPS) {
   assert(ik != NULL, "invariant");
   assert(JdkJfrEvent::is_a(ik), "invariant");
@@ -1533,16 +1504,7 @@
   assert(name != NULL, "invariant");
   Handle class_loader(THREAD, ik->class_loader());
   Handle protection_domain(THREAD, ik->protection_domain());
-  // nota bene: use lock-free dictionary lookup
-  const InstanceKlass* prev_ik = (const InstanceKlass*)SystemDictionary::find(name, class_loader, protection_domain, THREAD);
-  if (prev_ik == NULL) {
-    return false;
-  }
-  // an existing ik implies a retransform/redefine
-  assert(prev_ik != NULL, "invariant");
-  assert(JdkJfrEvent::is_a(prev_ik), "invariant");
-  copy_method_trace_flags(prev_ik, ik);
-  return true;
+  return SystemDictionary::find(name, class_loader, protection_domain, THREAD) != NULL;
 }
 
 // target for JFR_ON_KLASS_CREATION hook
@@ -1571,12 +1533,8 @@
     return;
   }
   assert(JdkJfrEvent::is_subklass(ik), "invariant");
-  if (is_retransforming(ik, THREAD)) {
-    // not the initial klass load
-    return;
-  }
-  if (ik->is_abstract()) {
-    // abstract classes are not instrumented
+  if (ik->is_abstract() || is_retransforming(ik, THREAD)) {
+    // abstract and scratch classes are not instrumented
     return;
   }
   ResourceMark rm(THREAD);
--- a/src/hotspot/share/jfr/jfr.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/jfr.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -71,6 +71,18 @@
   JfrThreadLocal::on_exit(t);
 }
 
+void Jfr::exclude_thread(Thread* t) {
+  JfrThreadLocal::exclude(t);
+}
+
+void Jfr::include_thread(Thread* t) {
+  JfrThreadLocal::include(t);
+}
+
+bool Jfr::is_excluded(Thread* t) {
+  return t != NULL && t->jfr_thread_local()->is_excluded();
+}
+
 void Jfr::on_java_thread_dismantle(JavaThread* jt) {
   if (JfrRecorder::is_recording()) {
     JfrCheckpointManager::write_thread_checkpoint(jt);
--- a/src/hotspot/share/jfr/jfr.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/jfr.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -53,6 +53,9 @@
   static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
   static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
   static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
+  static void exclude_thread(Thread* thread);
+  static bool is_excluded(Thread* thread);
+  static void include_thread(Thread* thread);
 };
 
 #endif // SHARE_JFR_JFR_HPP
--- a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -23,7 +23,6 @@
  */
 
 #include "precompiled.hpp"
-#include "jni.h"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/modules.hpp"
 #include "classfile/symbolTable.hpp"
@@ -42,9 +41,11 @@
 #include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/jniHandles.inline.hpp"
+#include "runtime/semaphore.inline.hpp"
 #include "runtime/synchronizer.hpp"
 #include "runtime/thread.inline.hpp"
 #include "runtime/threadSMR.hpp"
+#include "utilities/growableArray.hpp"
 
 #ifdef ASSERT
 void JfrJavaSupport::check_java_thread_in_vm(Thread* t) {
@@ -58,6 +59,12 @@
   assert(t->is_Java_thread(), "invariant");
   assert(((JavaThread*)t)->thread_state() == _thread_in_native, "invariant");
 }
+
+static void check_new_unstarted_java_thread(Thread* t) {
+  assert(t != NULL, "invariant");
+  assert(t->is_Java_thread(), "invariant");
+  assert(((JavaThread*)t)->thread_state() == _thread_new, "invariant");
+}
 #endif
 
 /*
@@ -93,6 +100,21 @@
   JNIHandles::destroy_global(handle);
 }
 
+jweak JfrJavaSupport::global_weak_jni_handle(const oop obj, Thread* t) {
+  DEBUG_ONLY(check_java_thread_in_vm(t));
+  HandleMark hm(t);
+  return JNIHandles::make_weak_global(Handle(t, obj));
+}
+
+jweak JfrJavaSupport::global_weak_jni_handle(const jobject handle, Thread* t) {
+  const oop obj = JNIHandles::resolve(handle);
+  return obj == NULL ? NULL : global_weak_jni_handle(obj, t);
+}
+
+void JfrJavaSupport::destroy_global_weak_jni_handle(jweak handle) {
+  JNIHandles::destroy_weak_global(handle);
+}
+
 oop JfrJavaSupport::resolve_non_null(jobject obj) {
   return JNIHandles::resolve_non_null(obj);
 }
@@ -603,9 +625,149 @@
   return true;
 }
 
-jlong JfrJavaSupport::jfr_thread_id(jobject target_thread) {
+class ThreadExclusionListAccess : public StackObj {
+ private:
+  static Semaphore _mutex_semaphore;
+ public:
+  ThreadExclusionListAccess() { _mutex_semaphore.wait(); }
+  ~ThreadExclusionListAccess() { _mutex_semaphore.signal(); }
+};
+
+Semaphore ThreadExclusionListAccess::_mutex_semaphore(1);
+static GrowableArray<jweak>* exclusion_list = NULL;
+
+static bool equals(const jweak excluded_thread, Handle target_thread) {
+  return JfrJavaSupport::resolve_non_null(excluded_thread) == target_thread();
+}
+
+static int find_exclusion_thread_idx(Handle thread) {
+  if (exclusion_list != NULL) {
+    for (int i = 0; i < exclusion_list->length(); ++i) {
+      if (equals(exclusion_list->at(i), thread)) {
+        return i;
+      }
+    }
+  }
+  return -1;
+}
+
+static Handle as_handle(jobject thread) {
+  return Handle(Thread::current(), JfrJavaSupport::resolve_non_null(thread));
+}
+
+static bool thread_is_not_excluded(Handle thread) {
+  return -1 == find_exclusion_thread_idx(thread);
+}
+
+static bool thread_is_not_excluded(jobject thread) {
+  return thread_is_not_excluded(as_handle(thread));
+}
+
+static bool is_thread_excluded(jobject thread) {
+  return !thread_is_not_excluded(thread);
+}
+
+#ifdef ASSERT
+static bool is_thread_excluded(Handle thread) {
+  return !thread_is_not_excluded(thread);
+}
+#endif // ASSERT
+
+static int add_thread_to_exclusion_list(jobject thread) {
+  ThreadExclusionListAccess lock;
+  if (exclusion_list == NULL) {
+    exclusion_list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<jweak>(10, true, mtTracing);
+  }
+  assert(exclusion_list != NULL, "invariant");
+  assert(thread_is_not_excluded(thread), "invariant");
+  jweak ref = JfrJavaSupport::global_weak_jni_handle(thread, Thread::current());
+  const int idx = exclusion_list->append(ref);
+  assert(is_thread_excluded(thread), "invariant");
+  return idx;
+}
+
+static void remove_thread_from_exclusion_list(Handle thread) {
+  assert(exclusion_list != NULL, "invariant");
+  assert(is_thread_excluded(thread), "invariant");
+  assert(exclusion_list != NULL, "invariant");
+  const int idx = find_exclusion_thread_idx(thread);
+  assert(idx >= 0, "invariant");
+  assert(idx < exclusion_list->length(), "invariant");
+  JfrJavaSupport::destroy_global_weak_jni_handle(exclusion_list->at(idx));
+  exclusion_list->delete_at(idx);
+  assert(thread_is_not_excluded(thread), "invariant");
+  if (0 == exclusion_list->length()) {
+    delete exclusion_list;
+    exclusion_list = NULL;
+  }
+}
+
+static void remove_thread_from_exclusion_list(jobject thread) {
+  ThreadExclusionListAccess lock;
+  remove_thread_from_exclusion_list(as_handle(thread));
+}
+
+// includes removal
+static bool check_exclusion_state_on_thread_start(JavaThread* jt) {
+  Handle h_obj(jt, jt->threadObj());
+  ThreadExclusionListAccess lock;
+  if (thread_is_not_excluded(h_obj)) {
+    return false;
+  }
+  remove_thread_from_exclusion_list(h_obj);
+  return true;
+}
+
+jlong JfrJavaSupport::jfr_thread_id(jobject thread) {
   ThreadsListHandle tlh;
   JavaThread* native_thread = NULL;
-  (void)tlh.cv_internal_thread_to_JavaThread(target_thread, &native_thread, NULL);
+  (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
   return native_thread != NULL ? JFR_THREAD_ID(native_thread) : 0;
 }
+
+void JfrJavaSupport::exclude(jobject thread) {
+  HandleMark hm;
+  ThreadsListHandle tlh;
+  JavaThread* native_thread = NULL;
+  (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
+  if (native_thread != NULL) {
+    JfrThreadLocal::exclude(native_thread);
+  } else {
+    // not started yet, track the thread oop
+    add_thread_to_exclusion_list(thread);
+  }
+}
+
+void JfrJavaSupport::include(jobject thread) {
+  HandleMark hm;
+  ThreadsListHandle tlh;
+  JavaThread* native_thread = NULL;
+  (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
+  if (native_thread != NULL) {
+    JfrThreadLocal::include(native_thread);
+  } else {
+    // not started yet, untrack the thread oop
+    remove_thread_from_exclusion_list(thread);
+  }
+}
+
+bool JfrJavaSupport::is_excluded(jobject thread) {
+  HandleMark hm;
+  ThreadsListHandle tlh;
+  JavaThread* native_thread = NULL;
+  (void)tlh.cv_internal_thread_to_JavaThread(thread, &native_thread, NULL);
+  return native_thread != NULL ? native_thread->jfr_thread_local()->is_excluded() : is_thread_excluded(thread);
+}
+
+void JfrJavaSupport::on_thread_start(Thread* t) {
+  assert(t != NULL, "invariant");
+  assert(Thread::current() == t, "invariant");
+  if (!t->is_Java_thread()) {
+    return;
+  }
+  DEBUG_ONLY(check_new_unstarted_java_thread(t);)
+  HandleMark hm;
+  if (check_exclusion_state_on_thread_start((JavaThread*)t)) {
+    JfrThreadLocal::exclude(t);
+  }
+}
--- a/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -29,18 +29,21 @@
 #include "utilities/exceptions.hpp"
 
 class Klass;
-class JavaThread;
 class outputStream;
 
 class JfrJavaSupport : public AllStatic {
  public:
   static jobject local_jni_handle(const oop obj, Thread* t);
   static jobject local_jni_handle(const jobject handle, Thread* t);
-  static void destroy_local_jni_handle(const jobject handle);
+  static void destroy_local_jni_handle(jobject handle);
 
   static jobject global_jni_handle(const oop obj, Thread* t);
   static jobject global_jni_handle(const jobject handle, Thread* t);
-  static void destroy_global_jni_handle(const jobject handle);
+  static void destroy_global_jni_handle(jobject handle);
+
+  static jweak global_weak_jni_handle(const oop obj, Thread* t);
+  static jweak global_weak_jni_handle(const jobject handle, Thread* t);
+  static void destroy_global_weak_jni_handle(jweak handle);
 
   static oop resolve_non_null(jobject obj);
   static void notify_all(jobject obj, TRAPS);
@@ -85,7 +88,11 @@
   static bool is_jdk_jfr_module_available();
   static bool is_jdk_jfr_module_available(outputStream* stream, TRAPS);
 
-  static jlong jfr_thread_id(jobject target_thread);
+  static jlong jfr_thread_id(jobject thread);
+  static void exclude(jobject thread);
+  static void include(jobject thread);
+  static bool is_excluded(jobject thread);
+  static void on_thread_start(Thread* t);
 
   // critical
   static void abort(jstring errorMsg, TRAPS);
--- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -284,6 +284,10 @@
   return JfrJavaEventWriter::flush(writer, used_size, requested_size, thread);
 JVM_END
 
+JVM_ENTRY_NO_ENV(void, jfr_flush(JNIEnv* env, jobject jvm))
+  JfrRepository::flush(thread);
+JVM_END
+
 JVM_ENTRY_NO_ENV(void, jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location))
   return JfrRepository::set_path(location, thread);
 JVM_END
@@ -311,3 +315,20 @@
 JVM_ENTRY_NO_ENV(void, jfr_emit_old_object_samples(JNIEnv* env, jobject jvm, jlong cutoff_ticks, jboolean emit_all))
   LeakProfiler::emit_events(cutoff_ticks, emit_all == JNI_TRUE);
 JVM_END
+
+JVM_ENTRY_NO_ENV(void, jfr_exclude_thread(JNIEnv* env, jobject jvm, jobject t))
+  JfrJavaSupport::exclude(t);
+JVM_END
+
+JVM_ENTRY_NO_ENV(void, jfr_include_thread(JNIEnv* env, jobject jvm, jobject t))
+  JfrJavaSupport::include(t);
+JVM_END
+
+JVM_ENTRY_NO_ENV(jboolean, jfr_is_thread_excluded(JNIEnv* env, jobject jvm, jobject t))
+  return JfrJavaSupport::is_excluded(t);
+JVM_END
+
+JVM_ENTRY_NO_ENV(jlong, jfr_chunk_start_nanos(JNIEnv* env, jobject jvm))
+  return JfrRepository::current_chunk_start_nanos();
+JVM_END
+
--- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -113,6 +113,7 @@
 
 jboolean JNICALL jfr_event_writer_flush(JNIEnv* env, jclass cls, jobject writer, jint used_size, jint requested_size);
 
+void JNICALL jfr_flush(JNIEnv* env, jobject jvm);
 void JNICALL jfr_abort(JNIEnv* env, jobject jvm, jstring errorMsg);
 
 jlong JNICALL jfr_get_epoch_address(JNIEnv* env, jobject jvm);
@@ -131,6 +132,13 @@
 
 jboolean JNICALL jfr_should_rotate_disk(JNIEnv* env, jobject jvm);
 
+void JNICALL jfr_exclude_thread(JNIEnv* env, jobject jvm, jobject t);
+
+void JNICALL jfr_include_thread(JNIEnv* env, jobject jvm, jobject t);
+
+jboolean JNICALL jfr_is_thread_excluded(JNIEnv* env, jobject jvm, jobject t);
+
+jlong JNICALL jfr_chunk_start_nanos(JNIEnv* env, jobject jvm);
 
 #ifdef __cplusplus
 }
--- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -70,6 +70,7 @@
       (char*)"getEventWriter", (char*)"()Ljava/lang/Object;", (void*)jfr_get_event_writer,
       (char*)"newEventWriter", (char*)"()Ljdk/jfr/internal/EventWriter;", (void*)jfr_new_event_writer,
       (char*)"flush", (char*)"(Ljdk/jfr/internal/EventWriter;II)Z", (void*)jfr_event_writer_flush,
+      (char*)"flush", (char*)"()V", (void*)jfr_flush,
       (char*)"setRepositoryLocation", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_repository_location,
       (char*)"abort", (char*)"(Ljava/lang/String;)V", (void*)jfr_abort,
       (char*)"getEpochAddress", (char*)"()J",(void*)jfr_get_epoch_address,
@@ -79,7 +80,11 @@
       (char*)"getUnloadedEventClassCount", (char*)"()J", (void*)jfr_get_unloaded_event_classes_count,
       (char*)"setCutoff", (char*)"(JJ)Z", (void*)jfr_set_cutoff,
       (char*)"emitOldObjectSamples", (char*)"(JZ)V", (void*)jfr_emit_old_object_samples,
-      (char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk
+      (char*)"shouldRotateDisk", (char*)"()Z", (void*)jfr_should_rotate_disk,
+      (char*)"exclude", (char*)"(Ljava/lang/Thread;)V", (void*)jfr_exclude_thread,
+      (char*)"include", (char*)"(Ljava/lang/Thread;)V", (void*)jfr_include_thread,
+      (char*)"isExcluded", (char*)"(Ljava/lang/Thread;)Z", (void*)jfr_is_thread_excluded,
+      (char*)"getChunkStartNanos", (char*)"()J", (void*)jfr_chunk_start_nanos
     };
 
     const size_t method_array_length = sizeof(method) / sizeof(JNINativeMethod);
--- a/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -259,6 +259,7 @@
   assert(leak_context_edge->parent() == NULL, "invariant");
 
   if (1 == length) {
+    store_gc_root_id_in_leak_context_edge(leak_context_edge, leak_context_edge);
     return;
   }
 
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/eventEmitter.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/eventEmitter.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -34,6 +34,7 @@
 #include "memory/resourceArea.hpp"
 #include "oops/markWord.hpp"
 #include "oops/oop.inline.hpp"
+#include "runtime/mutexLocker.hpp"
 #include "runtime/thread.inline.hpp"
 #include "runtime/vmThread.hpp"
 
@@ -51,8 +52,8 @@
 }
 
 void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_all) {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
   assert(sampler != NULL, "invariant");
-
   ResourceMark rm;
   EdgeStore edge_store;
   if (cutoff_ticks <= 0) {
@@ -68,6 +69,7 @@
 }
 
 size_t EventEmitter::write_events(ObjectSampler* object_sampler, EdgeStore* edge_store, bool emit_all) {
+  assert_locked_or_safepoint(JfrStream_lock);
   assert(_thread == Thread::current(), "invariant");
   assert(_thread->jfr_thread_local() == _jfr_thread_local, "invariant");
   assert(object_sampler != NULL, "invariant");
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -37,6 +37,7 @@
 #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
 #include "jfr/utilities/jfrHashtable.hpp"
 #include "jfr/utilities/jfrTypes.hpp"
+#include "runtime/mutexLocker.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/thread.hpp"
 #include "utilities/growableArray.hpp"
@@ -271,7 +272,7 @@
   }
   const JfrStackTrace* const stack_trace = resolve(sample);
   DEBUG_ONLY(validate_stack_trace(sample, stack_trace));
-  JfrCheckpointWriter writer(false, true, Thread::current());
+  JfrCheckpointWriter writer;
   writer.write_type(TYPE_STACKTRACE);
   writer.write_count(1);
   ObjectSampleCheckpoint::write_stacktrace(stack_trace, writer);
@@ -291,6 +292,7 @@
 
 // caller needs ResourceMark
 void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repo) {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
   assert(sampler != NULL, "invariant");
   assert(LeakProfiler::is_running(), "invariant");
   install_stack_traces(sampler, stack_trace_repo);
@@ -388,7 +390,7 @@
 static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) {
   // sample set is predicated on time of last sweep
   const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value();
-  JfrCheckpointWriter writer(false, false, thread);
+  JfrCheckpointWriter writer(thread, false);
   BlobWriter cbw(sampler, writer, last_sweep);
   iterate_samples(cbw, true);
   // reset blob write states
@@ -397,13 +399,14 @@
 }
 
 void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
+  assert_locked_or_safepoint(JfrStream_lock);
   assert(sampler != NULL, "invariant");
   assert(edge_store != NULL, "invariant");
   assert(thread != NULL, "invariant");
   write_sample_blobs(sampler, emit_all, thread);
   // write reference chains
   if (!edge_store->is_empty()) {
-    JfrCheckpointWriter writer(false, true, thread);
+    JfrCheckpointWriter writer(thread);
     ObjectSampleWriter osw(writer, edge_store);
     edge_store->iterate(osw);
   }
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -355,10 +355,6 @@
 
 static traceid get_gc_root_description_info_id(const Edge& edge, traceid id) {
   assert(edge.is_root(), "invariant");
-  if (EdgeUtils::is_leak_edge(edge)) {
-    return 0;
-  }
-
   if (root_infos == NULL) {
     root_infos = new RootDescriptionInfo();
   }
@@ -606,8 +602,8 @@
 static void register_serializers() {
   static bool is_registered = false;
   if (!is_registered) {
-    JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType());
-    JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType());
+    JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, true, new RootSystemType());
+    JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, true, new RootType());
     is_registered = true;
   }
 }
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/rootResolver.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -29,6 +29,7 @@
 #include "gc/shared/strongRootsScope.hpp"
 #include "jfr/leakprofiler/utilities/unifiedOop.hpp"
 #include "jfr/leakprofiler/checkpoint/rootResolver.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
 #include "memory/iterator.hpp"
 #include "memory/universe.hpp"
 #include "oops/klass.hpp"
@@ -36,7 +37,6 @@
 #include "prims/jvmtiThreadState.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/mutexLocker.hpp"
-#include "runtime/threadSMR.inline.hpp"
 #include "runtime/vframe_hp.hpp"
 #include "services/management.hpp"
 #include "utilities/growableArray.hpp"
@@ -256,8 +256,9 @@
  public:
   ReferenceToThreadRootClosure(RootCallback& callback) :_callback(callback), _complete(false) {
     assert_locked_or_safepoint(Threads_lock);
-    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
-      if (do_thread_roots(jt)) {
+    JfrJavaThreadIterator iter;
+    while (iter.has_next()) {
+      if (do_thread_roots(iter.next())) {
         return;
       }
     }
--- a/src/hotspot/share/jfr/leakprofiler/leakProfiler.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/leakprofiler/leakProfiler.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -31,6 +31,7 @@
 #include "jfr/recorder/service/jfrOptionSet.hpp"
 #include "logging/log.hpp"
 #include "memory/iterator.hpp"
+#include "runtime/mutexLocker.hpp"
 #include "runtime/thread.inline.hpp"
 #include "runtime/vmThread.hpp"
 
@@ -92,6 +93,7 @@
   if (!is_running()) {
     return;
   }
+  MutexLocker lock(JfrStream_lock);
   // exclusive access to object sampler instance
   ObjectSampler* const sampler = ObjectSampler::acquire();
   assert(sampler != NULL, "invariant");
--- a/src/hotspot/share/jfr/leakprofiler/sampling/objectSampler.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/leakprofiler/sampling/objectSampler.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -110,6 +110,9 @@
   }
   const JfrThreadLocal* const tl = thread->jfr_thread_local();
   assert(tl != NULL, "invariant");
+  if (tl->is_excluded()) {
+    return 0;
+  }
   if (!tl->has_thread_blob()) {
     JfrCheckpointManager::create_thread_blob(thread);
   }
--- a/src/hotspot/share/jfr/metadata/jfrSerializer.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/metadata/jfrSerializer.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -70,7 +70,8 @@
 class JfrSerializer : public CHeapObj<mtTracing> {
  public:
   virtual ~JfrSerializer() {}
-  static bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer);
+  virtual void on_rotation() {}
+  static bool register_serializer(JfrTypeId id, bool permit_cache, JfrSerializer* serializer);
   virtual void serialize(JfrCheckpointWriter& writer) = 0;
 };
 
--- a/src/hotspot/share/jfr/metadata/metadata.xml	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/metadata/metadata.xml	Mon Nov 04 11:25:55 2019 +0000
@@ -154,7 +154,7 @@
     <Field type="string" name="newValue" label="New Value" />
     <Field type="FlagValueOrigin" name="origin" label="Origin" />
   </Event>
- 
+
   <Type name="VirtualSpace">
     <Field type="ulong" contentType="address" name="start" label="Start Address" description="Start address of the virtual space" />
     <Field type="ulong" contentType="address" name="committedEnd" label="Committed End Address" description="End address of the committed memory for the virtual space" />
@@ -162,27 +162,27 @@
     <Field type="ulong" contentType="address" name="reservedEnd" label="Reserved End Address" description="End address of the reserved memory for the virtual space" />
     <Field type="ulong" contentType="bytes" name="reservedSize" label="Reserved Size" description="Size of the reserved memory for the virtual space" />
   </Type>
-  
+
   <Type name="ObjectSpace">
     <Field type="ulong" contentType="address" name="start" label="Start Address" description="Start address of the space" />
     <Field type="ulong" contentType="address" name="end" label="End Address" description="End address of the space" />
     <Field type="ulong" contentType="bytes" name="used" label="Used" description="Bytes allocated by objects in the space" />
     <Field type="ulong" contentType="bytes" name="size" label="Size" description="Size of the space" />
   </Type>
-  
+
   <Event name="GCHeapSummary" category="Java Virtual Machine, GC, Heap" label="Heap Summary" startTime="false">
     <Field type="uint" name="gcId" label="GC Identifier" relation="GcId" />
     <Field type="GCWhen" name="when" label="When" />
     <Field type="VirtualSpace" struct="true" name="heapSpace" label="Heap Space" />
     <Field type="ulong" contentType="bytes" name="heapUsed" label="Heap Used" description="Bytes allocated by objects in the heap" />
   </Event>
- 
+
   <Type name="MetaspaceSizes">
     <Field type="ulong" contentType="bytes" name="committed" label="Committed" description="Committed memory for this space" />
     <Field type="ulong" contentType="bytes" name="used" label="Used" description="Bytes allocated by objects in the space" />
     <Field type="ulong" contentType="bytes" name="reserved" label="Reserved" description="Reserved memory for this space" />
   </Type>
- 
+
   <Event name="MetaspaceSummary" category="Java Virtual Machine, GC, Heap" label="Metaspace Summary" startTime="false">
     <Field type="uint" name="gcId" label="GC Identifier" relation="GcId" />
     <Field type="GCWhen" name="when" label="When" />
@@ -442,7 +442,7 @@
     <Field type="uint" name="gcWorkerId" label="GC Worker Identifier" />
     <Field type="string" name="name" label="Name" />
   </Event>
-  
+
   <Event name="AllocationRequiringGC" category="Java Virtual Machine, GC, Detailed" label="Allocation Requiring GC" thread="true" stackTrace="true"
     startTime="false">
     <Field type="uint" name="gcId" label="Pending GC Identifier" relation="GcId" />
@@ -484,7 +484,7 @@
     <Field type="string" name="failureMessage" label="Failure Message" />
     <Field type="uint" name="compileId" label="Compilation Identifier" relation="CompileId" />
   </Event>
-  
+
   <Type name="CalleeMethod">
     <Field type="string" name="type" label="Class" />
     <Field type="string" name="name" label="Method Name" />
@@ -585,21 +585,21 @@
     <Field type="OldObjectGcRoot" name="root" label="GC Root" />
   </Event>
 
-  <Event name="DumpReason" category="Flight Recorder" label="Recording Reason" 
-         description="Who requested the recording and why" 
+  <Event name="DumpReason" category="Flight Recorder" label="Recording Reason"
+         description="Who requested the recording and why"
          startTime="false">
     <Field type="string" name="reason" label="Reason" description="Reason for writing recording data to disk" />
     <Field type="int" name="recordingId" label="Recording Id" description="Id of the recording that triggered the dump, or -1 if it was not related to a recording" />
   </Event>
 
-  <Event name="DataLoss" category="Flight Recorder" label="Data Loss" 
+  <Event name="DataLoss" category="Flight Recorder" label="Data Loss"
          description="Data could not be copied out from a buffer, typically because of contention"
          startTime="false">
     <Field type="ulong" contentType="bytes" name="amount" label="Amount" description="Amount lost data" />
     <Field type="ulong" contentType="bytes" name="total" label="Total" description="Total lost amount for thread" />
   </Event>
 
-  <Event name="JVMInformation" category="Java Virtual Machine" label="JVM Information" 
+  <Event name="JVMInformation" category="Java Virtual Machine" label="JVM Information"
          description="Description of JVM and the Java application"
          period="endChunk">
     <Field type="string" name="jvmName" label="JVM Name" />
@@ -810,8 +810,8 @@
     <Field type="int" name="standardCompileCount" label="Standard Compilations" />
     <Field type="ulong" contentType="bytes" name="osrBytesCompiled" label="OSR Bytes Compiled" />
     <Field type="ulong" contentType="bytes" name="standardBytesCompiled" label="Standard Bytes Compiled" />
-    <Field type="ulong" contentType="bytes" name="nmetodsSize" label="Compilation Resulting Size" />
-    <Field type="ulong" contentType="bytes" name="nmetodCodeSize" label="Compilation Resulting Code Size" />
+    <Field type="ulong" contentType="bytes" name="nmethodsSize" label="Compilation Resulting Size" />
+    <Field type="ulong" contentType="bytes" name="nmethodCodeSize" label="Compilation Resulting Code Size" />
     <Field type="long" contentType="millis" name="peakTimeSpent" label="Peak Time" />
     <Field type="long" contentType="millis" name="totalTimeSpent" label="Total time" />
   </Event>
@@ -1004,6 +1004,42 @@
     <Field type="string" name="state" label="State" />
   </Type>
 
+  <Event name="Flush" category="Flight Recorder" label="Flush" thread="false" experimental="true">
+    <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+    <Field type="ulong" name="elements" label="Elements Written" />
+    <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+  </Event>
+
+  <Event name="FlushStorage" category="Flight Recorder" label="Flush Storage" thread="false" experimental="true">
+    <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+    <Field type="ulong" name="elements" label="Elements Written" />
+    <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+  </Event>
+
+  <Event name="FlushStacktrace" category="Flight Recorder" label="Flush Stacktrace" thread="false" experimental="true">
+    <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+    <Field type="ulong" name="elements" label="Elements Written" />
+    <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+  </Event>
+
+  <Event name="FlushStringPool" category="Flight Recorder" label="Flush String Pool" thread="false" experimental="true">
+    <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+    <Field type="ulong" name="elements" label="Elements Written" />
+    <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+  </Event>
+
+  <Event name="FlushMetadata" category="Flight Recorder" label="Flush Metadata" thread="false" experimental="true">
+    <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+    <Field type="ulong" name="elements" label="Elements Written" />
+    <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+  </Event>
+
+  <Event name="FlushTypeSet" category="Flight Recorder" label="Flush Type Set" thread="false" experimental="true">
+    <Field type="ulong" name="flushId" label="Flush Identifier" relation="FlushId" />
+    <Field type="ulong" name="elements" label="Elements Written" />
+    <Field type="ulong" contentType="bytes" name="size" label="Size Written" />
+  </Event>
+
   <Type name="ZStatisticsCounterType" label="Z Statistics Counter">
     <Field type="string" name="counter" label="Counter" />
   </Type>
@@ -1183,35 +1219,40 @@
     <Field type="int" name="bytecodeIndex" label="Bytecode Index" />
     <Field type="FrameType" name="type" label="Frame Type" />
   </Type>
- 
+
+  <Type name="ChunkHeader" label="Chunk Header">
+    <Field type="byte" array="true" name="payload" label="Payload" />
+  </Type>
+
   <Relation name="JavaMonitorAddress"/>
   <Relation name="SafepointId"/>
   <Relation name="GcId"/>
   <Relation name="CompileId" />
   <Relation name="SweepId"/>
- 
-  <XmlType name="Package" parameterType="const PackageEntry*" fieldType="const PackageEntry*"/> 
-  <XmlType name="Class" javaType="java.lang.Class" parameterType="const Klass*" fieldType="const Klass*"/> 
-  <XmlType name="Module"  parameterType="const ModuleEntry*" fieldType="const ModuleEntry*"/> 
-  <XmlType name="ClassLoader" parameterType="const ClassLoaderData*" fieldType="const ClassLoaderData*"/> 
-  <XmlType name="Method" parameterType="const Method*" fieldType="const Method*"/> 
-  <XmlType name="Thread" javaType="java.lang.Thread" parameterType="u8" fieldType="u8"/> 
-  <XmlType name="Tickspan" contentType="tickspan" javaType="long" parameterType="const Tickspan&amp;" fieldType="Tickspan"/> 
-  <XmlType name="Ticks" contentType="tickstamp" javaType="long" parameterType="const Ticks&amp;" fieldType="Ticks"/> 
-  <XmlType name="ulong" javaType="long" unsigned="true" parameterType="u8" fieldType="u8"/> 
-  <XmlType name="uint" javaType="int" unsigned="true" parameterType="unsigned" fieldType="unsigned"/> 
-  <XmlType name="ushort" javaType="short" unsigned="true" parameterType="u2" fieldType="u2"/> 
-  <XmlType name="ubyte" javaType="byte" unsigned="true" parameterType="u1" fieldType="u1"/> 
-  <XmlType name="long" javaType="long" parameterType="s8" fieldType="s8"/> 
-  <XmlType name="int" javaType="int" parameterType="s4" fieldType="s4"/> 
-  <XmlType name="short" javaType="short" parameterType="s2" fieldType="s2"/> 
-  <XmlType name="byte" javaType="byte"  parameterType="s1" fieldType="s1"/> 
-  <XmlType name="double" javaType="double" parameterType="double" fieldType="double"/> 
-  <XmlType name="float" javaType="float"  parameterType="float" fieldType="float"/> 
-  <XmlType name="boolean" javaType="boolean" parameterType="bool" fieldType="bool"/> 
-  <XmlType name="char" javaType="char" parameterType="char" fieldType="char"/> 
-  <XmlType name="string" javaType="java.lang.String" parameterType="const char*" fieldType="const char*"/> 
- 
+  <Relation name="FlushId"/>
+
+  <XmlType name="Package" parameterType="const PackageEntry*" fieldType="const PackageEntry*"/>
+  <XmlType name="Class" javaType="java.lang.Class" parameterType="const Klass*" fieldType="const Klass*"/>
+  <XmlType name="Module"  parameterType="const ModuleEntry*" fieldType="const ModuleEntry*"/>
+  <XmlType name="ClassLoader" parameterType="const ClassLoaderData*" fieldType="const ClassLoaderData*"/>
+  <XmlType name="Method" parameterType="const Method*" fieldType="const Method*"/>
+  <XmlType name="Thread" javaType="java.lang.Thread" parameterType="u8" fieldType="u8"/>
+  <XmlType name="Tickspan" contentType="tickspan" javaType="long" parameterType="const Tickspan&amp;" fieldType="Tickspan"/>
+  <XmlType name="Ticks" contentType="tickstamp" javaType="long" parameterType="const Ticks&amp;" fieldType="Ticks"/>
+  <XmlType name="ulong" javaType="long" unsigned="true" parameterType="u8" fieldType="u8"/>
+  <XmlType name="uint" javaType="int" unsigned="true" parameterType="unsigned" fieldType="unsigned"/>
+  <XmlType name="ushort" javaType="short" unsigned="true" parameterType="u2" fieldType="u2"/>
+  <XmlType name="ubyte" javaType="byte" unsigned="true" parameterType="u1" fieldType="u1"/>
+  <XmlType name="long" javaType="long" parameterType="s8" fieldType="s8"/>
+  <XmlType name="int" javaType="int" parameterType="s4" fieldType="s4"/>
+  <XmlType name="short" javaType="short" parameterType="s2" fieldType="s2"/>
+  <XmlType name="byte" javaType="byte"  parameterType="s1" fieldType="s1"/>
+  <XmlType name="double" javaType="double" parameterType="double" fieldType="double"/>
+  <XmlType name="float" javaType="float"  parameterType="float" fieldType="float"/>
+  <XmlType name="boolean" javaType="boolean" parameterType="bool" fieldType="bool"/>
+  <XmlType name="char" javaType="char" parameterType="char" fieldType="char"/>
+  <XmlType name="string" javaType="java.lang.String" parameterType="const char*" fieldType="const char*"/>
+
   <XmlContentType name="bytes" annotation="jdk.jfr.DataAmount(BYTES)" />
   <XmlContentType name="tickstamp" annotation="jdk.jfr.Timestamp(TICKS)" />
   <XmlContentType name="epochmillis" annotation="jdk.jfr.Timestamp(MILLISECONDS_SINCE_EPOCH)" />
@@ -1223,5 +1264,5 @@
   <XmlContentType name="hertz" annotation="jdk.jfr.Frequency" />
   <XmlContentType name="bytes-per-second" annotation="jdk.jfr.DataAmount(BYTES), jdk.jfr.Frequency" />
   <XmlContentType name="bits-per-second" annotation="jdk.jfr.DataAmount(BITS), jdk.jfr.Frequency" />
- 
+
 </Metadata>
--- a/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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
@@ -39,7 +39,7 @@
   traceid id;
   uint64_t bytes_in;
   uint64_t bytes_out;
-  bool in_use;
+  mutable bool written;
 };
 
 static GrowableArray<InterfaceEntry>* _interfaces = NULL;
@@ -71,7 +71,7 @@
   entry.id = ++interface_id;
   entry.bytes_in = iface->get_bytes_in();
   entry.bytes_out = iface->get_bytes_out();
-  entry.in_use = false;
+  entry.written = false;
   return _interfaces->at(_interfaces->append(entry));
 }
 
@@ -108,6 +108,39 @@
   return ((current - old) * NANOSECS_PER_SEC) / interval.nanoseconds();
 }
 
+class JfrNetworkInterfaceName : public JfrSerializer {
+ public:
+   void serialize(JfrCheckpointWriter& writer) {} // we write each constant lazily
+
+   void on_rotation() {
+     for (int i = 0; i < _interfaces->length(); ++i) {
+       const InterfaceEntry& entry = _interfaces->at(i);
+       if (entry.written) {
+         entry.written = false;
+       }
+     }
+   }
+};
+
+static bool register_network_interface_name_serializer() {
+  assert(_interfaces != NULL, "invariant");
+  return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME,
+    false, // disallow caching; we want a callback every rotation
+    new JfrNetworkInterfaceName());
+}
+
+static void write_interface_constant(const InterfaceEntry& entry) {
+  if (entry.written) {
+    return;
+  }
+  JfrCheckpointWriter writer;
+  writer.write_type(TYPE_NETWORKINTERFACENAME);
+  writer.write_count(1);
+  writer.write_key(entry.id);
+  writer.write(entry.name);
+  entry.written = true;
+}
+
 static bool get_interfaces(NetworkInterface** network_interfaces) {
   const int ret_val = JfrOSInterface::network_utilization(network_interfaces);
   if (ret_val == OS_ERR) {
@@ -117,39 +150,6 @@
   return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED;
 }
 
-class JfrNetworkInterfaceName : public JfrSerializer {
- public:
-  void serialize(JfrCheckpointWriter& writer) {
-    assert(_interfaces != NULL, "invariant");
-    const JfrCheckpointContext ctx = writer.context();
-    const intptr_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet
-    int active_interfaces = 0;
-    for (int i = 0; i < _interfaces->length(); ++i) {
-      InterfaceEntry& entry = _interfaces->at(i);
-      if (entry.in_use) {
-        entry.in_use = false;
-        writer.write_key(entry.id);
-        writer.write(entry.name);
-        ++active_interfaces;
-      }
-    }
-    if (active_interfaces == 0) {
-      // nothing to write, restore context
-      writer.set_context(ctx);
-      return;
-    }
-    writer.write_count(active_interfaces, count_offset);
-  }
-};
-
-static bool register_network_interface_name_serializer() {
-  assert(_interfaces != NULL, "invariant");
-  return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME,
-                                            false, // require safepoint
-                                            false, // disallow caching; we want a callback every rotation
-                                            new JfrNetworkInterfaceName());
-}
-
 void JfrNetworkUtilization::send_events() {
   ResourceMark rm;
   NetworkInterface* network_interfaces;
@@ -169,7 +169,7 @@
       const uint64_t read_rate = rate_per_second(current_bytes_in, entry.bytes_in, interval);
       const uint64_t write_rate = rate_per_second(current_bytes_out, entry.bytes_out, interval);
       if (read_rate > 0 || write_rate > 0) {
-        entry.in_use = true;
+        write_interface_constant(entry);
         EventNetworkUtilization event(UNTIMED);
         event.set_starttime(cur_time);
         event.set_endtime(cur_time);
--- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -44,6 +44,7 @@
 #include "jfr/periodic/jfrNetworkUtilization.hpp"
 #include "jfr/recorder/jfrRecorder.hpp"
 #include "jfr/support/jfrThreadId.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
 #include "jfr/utilities/jfrTime.hpp"
 #include "jfrfiles/jfrPeriodic.hpp"
 #include "logging/log.hpp"
@@ -56,7 +57,6 @@
 #include "runtime/os.hpp"
 #include "runtime/os_perf.hpp"
 #include "runtime/thread.inline.hpp"
-#include "runtime/threadSMR.hpp"
 #include "runtime/sweeper.hpp"
 #include "runtime/vmThread.hpp"
 #include "services/classLoadingService.hpp"
@@ -410,13 +410,12 @@
   GrowableArray<jlong> allocated(initial_size);
   GrowableArray<traceid> thread_ids(initial_size);
   JfrTicks time_stamp = JfrTicks::now();
-  {
-    // Collect allocation statistics while holding threads lock
-    MutexLocker ml(Threads_lock);
-    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
-      allocated.append(jt->cooked_allocated_bytes());
-      thread_ids.append(JFR_THREAD_ID(jt));
-    }
+  JfrJavaThreadIterator iter;
+  while (iter.has_next()) {
+    JavaThread* const jt = iter.next();
+    assert(jt != NULL, "invariant");
+    allocated.append(jt->cooked_allocated_bytes());
+    thread_ids.append(JFR_THREAD_ID(jt));
   }
 
   // Write allocation statistics to buffer.
@@ -558,8 +557,8 @@
   event.set_standardCompileCount(CompileBroker::get_total_standard_compile_count());
   event.set_osrBytesCompiled(CompileBroker::get_sum_osr_bytes_compiled());
   event.set_standardBytesCompiled(CompileBroker::get_sum_standard_bytes_compiled());
-  event.set_nmetodsSize(CompileBroker::get_sum_nmethod_size());
-  event.set_nmetodCodeSize(CompileBroker::get_sum_nmethod_code_size());
+  event.set_nmethodsSize(CompileBroker::get_sum_nmethod_size());
+  event.set_nmethodCodeSize(CompileBroker::get_sum_nmethod_code_size());
   event.set_peakTimeSpent(CompileBroker::get_peak_compilation_time());
   event.set_totalTimeSpent(CompileBroker::get_total_compilation_time());
   event.commit();
--- a/src/hotspot/share/jfr/periodic/jfrThreadCPULoadEvent.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/periodic/jfrThreadCPULoadEvent.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -28,11 +28,10 @@
 #include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
 #include "jfr/support/jfrThreadId.hpp"
 #include "jfr/support/jfrThreadLocal.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
 #include "jfr/utilities/jfrTime.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "runtime/os.hpp"
-#include "runtime/thread.inline.hpp"
-#include "runtime/threadSMR.inline.hpp"
 
 jlong JfrThreadCPULoadEvent::get_wallclock_time() {
   return os::javaTimeNanos();
@@ -115,8 +114,12 @@
   JfrTicks event_time = JfrTicks::now();
   jlong cur_wallclock_time = JfrThreadCPULoadEvent::get_wallclock_time();
 
-  JavaThreadIteratorWithHandle jtiwh;
-  while (JavaThread* jt = jtiwh.next()) {
+  JfrJavaThreadIterator iter;
+  int number_of_threads = 0;
+  while (iter.has_next()) {
+    JavaThread* const jt = iter.next();
+    assert(jt != NULL, "invariant");
+    ++number_of_threads;
     EventThreadCPULoad event(UNTIMED);
     if (JfrThreadCPULoadEvent::update_event(event, jt, cur_wallclock_time, processor_count)) {
       event.set_starttime(event_time);
@@ -129,7 +132,7 @@
       event.commit();
     }
   }
-  log_trace(jfr)("Measured CPU usage for %d threads in %.3f milliseconds", jtiwh.length(),
+  log_trace(jfr)("Measured CPU usage for %d threads in %.3f milliseconds", number_of_threads,
     (double)(JfrTicks::now() - event_time).milliseconds());
   // Restore this thread's thread id
   periodic_thread_tl->set_thread_id(periodic_thread_id);
--- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -30,6 +30,7 @@
 #include "jfr/recorder/service/jfrOptionSet.hpp"
 #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
 #include "jfr/support/jfrThreadId.hpp"
+#include "jfr/support/jfrThreadLocal.hpp"
 #include "jfr/utilities/jfrTime.hpp"
 #include "logging/log.hpp"
 #include "runtime/frame.inline.hpp"
@@ -352,9 +353,14 @@
   }
 }
 
+static bool is_excluded(JavaThread* thread) {
+  assert(thread != NULL, "invariant");
+  return thread->is_hidden_from_external_view() || thread->in_deopt_handler() || thread->jfr_thread_local()->is_excluded();
+}
+
 bool JfrThreadSampleClosure::do_sample_thread(JavaThread* thread, JfrStackFrame* frames, u4 max_frames, JfrSampleType type) {
   assert(Threads_lock->owned_by_self(), "Holding the thread table lock.");
-  if (thread->is_hidden_from_external_view() || thread->in_deopt_handler()) {
+  if (is_excluded(thread)) {
     return false;
   }
 
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -37,11 +37,14 @@
 #include "jfr/recorder/storage/jfrMemorySpace.inline.hpp"
 #include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
 #include "jfr/utilities/jfrBigEndian.hpp"
+#include "jfr/utilities/jfrIterator.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
 #include "jfr/utilities/jfrTypes.hpp"
+#include "jfr/writers/jfrJavaEventWriter.hpp"
 #include "logging/log.hpp"
 #include "memory/resourceArea.hpp"
 #include "runtime/handles.inline.hpp"
-#include "runtime/mutexLocker.hpp"
+#include "runtime/mutex.hpp"
 #include "runtime/orderAccess.hpp"
 #include "runtime/os.inline.hpp"
 #include "runtime/safepoint.hpp"
@@ -168,7 +171,7 @@
 }
 
 bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const {
-  return _service_thread != thread && OrderAccess::load_acquire(&_checkpoint_epoch_state) != JfrTraceIdEpoch::epoch();
+  return _service_thread != thread && _checkpoint_epoch_state != JfrTraceIdEpoch::epoch();
 }
 
 static const size_t lease_retry = 10;
@@ -181,12 +184,24 @@
   return lease_free(size, manager._free_list_mspace, lease_retry, thread);
 }
 
+JfrCheckpointMspace* JfrCheckpointManager::lookup(BufferPtr old) const {
+  assert(old != NULL, "invariant");
+  return _free_list_mspace->in_free_list(old) ? _free_list_mspace : _epoch_transition_mspace;
+}
+
+BufferPtr JfrCheckpointManager::lease_buffer(BufferPtr old, Thread* thread, size_t size /* 0 */) {
+  assert(old != NULL, "invariant");
+  JfrCheckpointMspace* mspace = instance().lookup(old);
+  assert(mspace != NULL, "invariant");
+  return lease_free(size, mspace, lease_retry, thread);
+}
+
 /*
-* If the buffer was a "lease" from the free list, release back.
-*
-* The buffer is effectively invalidated for the thread post-return,
-* and the caller should take means to ensure that it is not referenced.
-*/
+ * If the buffer was a lease, release back.
+ *
+ * The buffer is effectively invalidated for the thread post-return,
+ * and the caller should take means to ensure that it is not referenced.
+ */
 static void release(BufferPtr const buffer, Thread* thread) {
   DEBUG_ONLY(assert_release(buffer);)
   buffer->clear_lease();
@@ -202,7 +217,7 @@
     return NULL;
   }
   // migration of in-flight information
-  BufferPtr const new_buffer = lease_buffer(thread, used + requested);
+  BufferPtr const new_buffer = lease_buffer(old, thread, used + requested);
   if (new_buffer != NULL) {
     migrate_outstanding_writes(old, new_buffer, used, requested);
   }
@@ -213,8 +228,8 @@
 // offsets into the JfrCheckpointEntry
 static const juint starttime_offset = sizeof(jlong);
 static const juint duration_offset = starttime_offset + sizeof(jlong);
-static const juint flushpoint_offset = duration_offset + sizeof(jlong);
-static const juint types_offset = flushpoint_offset + sizeof(juint);
+static const juint checkpoint_type_offset = duration_offset + sizeof(jlong);
+static const juint types_offset = checkpoint_type_offset + sizeof(juint);
 static const juint payload_offset = types_offset + sizeof(juint);
 
 template <typename Return>
@@ -234,21 +249,21 @@
   return read_data<jlong>(data + duration_offset);
 }
 
-static bool is_flushpoint(const u1* data) {
-  return read_data<juint>(data + flushpoint_offset) == (juint)1;
+static u1 checkpoint_type(const u1* data) {
+  return read_data<u1>(data + checkpoint_type_offset);
 }
 
 static juint number_of_types(const u1* data) {
   return read_data<juint>(data + types_offset);
 }
 
-static void write_checkpoint_header(JfrChunkWriter& cw, int64_t offset_prev_cp_event, const u1* data) {
+static void write_checkpoint_header(JfrChunkWriter& cw, int64_t delta_to_last_checkpoint, const u1* data) {
   cw.reserve(sizeof(u4));
   cw.write<u8>(EVENT_CHECKPOINT);
   cw.write(starttime(data));
   cw.write(duration(data));
-  cw.write(offset_prev_cp_event);
-  cw.write(is_flushpoint(data));
+  cw.write(delta_to_last_checkpoint);
+  cw.write(checkpoint_type(data));
   cw.write(number_of_types(data));
 }
 
@@ -261,9 +276,9 @@
   assert(data != NULL, "invariant");
   const int64_t event_begin = cw.current_offset();
   const int64_t last_checkpoint_event = cw.last_checkpoint_offset();
-  const int64_t delta = last_checkpoint_event == 0 ? 0 : last_checkpoint_event - event_begin;
+  const int64_t delta_to_last_checkpoint = last_checkpoint_event == 0 ? 0 : last_checkpoint_event - event_begin;
   const int64_t checkpoint_size = total_size(data);
-  write_checkpoint_header(cw, delta, data);
+  write_checkpoint_header(cw, delta_to_last_checkpoint, data);
   write_checkpoint_content(cw, data, checkpoint_size);
   const int64_t event_size = cw.current_offset() - event_begin;
   cw.write_padded_at_offset<u4>(event_size, event_begin);
@@ -305,13 +320,13 @@
 typedef CheckpointWriteOp<JfrCheckpointMspace::Type> WriteOperation;
 typedef ReleaseOp<JfrCheckpointMspace> CheckpointReleaseOperation;
 
-template <template <typename> class WriterHost, template <typename, typename> class CompositeOperation>
+template <template <typename> class WriterHost, template <typename, typename, typename> class CompositeOperation>
 static size_t write_mspace(JfrCheckpointMspace* mspace, JfrChunkWriter& chunkwriter) {
   assert(mspace != NULL, "invariant");
   WriteOperation wo(chunkwriter);
   WriterHost<WriteOperation> wh(wo);
   CheckpointReleaseOperation cro(mspace, Thread::current(), false);
-  CompositeOperation<WriterHost<WriteOperation>, CheckpointReleaseOperation> co(&wh, &cro);
+  CompositeOperation<WriterHost<WriteOperation>, CheckpointReleaseOperation, CompositeOperationAnd> co(&wh, &cro);
   assert(mspace->is_full_empty(), "invariant");
   process_free_list(co, mspace);
   return wo.processed();
@@ -333,6 +348,16 @@
   return write_mspace<ExclusiveOp, CompositeOperation>(_epoch_transition_mspace, _chunkwriter);
 }
 
+typedef MutexedWriteOp<WriteOperation> FlushOperation;
+
+size_t JfrCheckpointManager::flush() {
+  WriteOperation wo(_chunkwriter);
+  FlushOperation fo(wo);
+  assert(_free_list_mspace->is_full_empty(), "invariant");
+  process_free_list(fo, _free_list_mspace);
+  return wo.processed();
+}
+
 typedef DiscardOp<DefaultDiscarder<JfrBuffer> > DiscardOperation;
 size_t JfrCheckpointManager::clear() {
   JfrTypeSet::clear();
@@ -340,44 +365,88 @@
   process_free_list(discarder, _free_list_mspace);
   process_free_list(discarder, _epoch_transition_mspace);
   synchronize_epoch();
-  return discarder.processed();
+  return discarder.elements();
 }
 
-size_t JfrCheckpointManager::write_types() {
-  JfrCheckpointWriter writer(false, true, Thread::current());
-  JfrTypeManager::write_types(writer);
+// Optimization for write_static_type_set() and write_threads() is to write
+// directly into the epoch transition mspace because we will immediately
+// serialize and reset this mspace post-write.
+static JfrBuffer* get_epoch_transition_buffer(JfrCheckpointMspace* mspace, Thread* t) {
+  assert(mspace != NULL, "invariant");
+  JfrBuffer* const buffer = mspace->free_head();
+  assert(buffer != NULL, "invariant");
+  buffer->acquire(t);
+  buffer->set_lease();
+  DEBUG_ONLY(assert_free_lease(buffer);)
+  return buffer;
+}
+
+bool JfrCheckpointManager::is_static_type_set_required() {
+  return JfrTypeManager::has_new_static_type();
+}
+
+size_t JfrCheckpointManager::write_static_type_set() {
+  Thread* const t = Thread::current();
+  ResourceMark rm(t);
+  HandleMark hm(t);
+  JfrCheckpointWriter writer(t, get_epoch_transition_buffer(_epoch_transition_mspace, t), STATICS);
+  JfrTypeManager::write_static_types(writer);
   return writer.used_size();
 }
 
-size_t JfrCheckpointManager::write_safepoint_types() {
-  // this is also a "flushpoint"
-  JfrCheckpointWriter writer(true, true, Thread::current());
-  JfrTypeManager::write_safepoint_types(writer);
+size_t JfrCheckpointManager::write_threads() {
+  Thread* const t = Thread::current();
+  ResourceMark rm(t);
+  HandleMark hm(t);
+  JfrCheckpointWriter writer(t, get_epoch_transition_buffer(_epoch_transition_mspace, t), THREADS);
+  JfrTypeManager::write_threads(writer);
   return writer.used_size();
 }
 
+size_t JfrCheckpointManager::write_static_type_set_and_threads() {
+  write_static_type_set();
+  write_threads();
+  return write_epoch_transition_mspace();
+}
+
+void JfrCheckpointManager::shift_epoch() {
+  debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();)
+  JfrTraceIdEpoch::shift_epoch();
+  assert(current_epoch != JfrTraceIdEpoch::current(), "invariant");
+}
+
+void JfrCheckpointManager::on_rotation() {
+  assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+  JfrTypeManager::on_rotation();
+  notify_threads();
+}
+
 void JfrCheckpointManager::write_type_set() {
   assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
-  // can safepoint here
-  MutexLocker cld_lock(ClassLoaderDataGraph_lock);
-  MutexLocker module_lock(Module_lock);
-  if (!LeakProfiler::is_running()) {
-    JfrCheckpointWriter writer(true, true, Thread::current());
-    JfrTypeSet::serialize(&writer, NULL, false);
-    return;
+  if (LeakProfiler::is_running()) {
+    Thread* const t = Thread::current();
+    // can safepoint here
+    MutexLocker cld_lock(ClassLoaderDataGraph_lock);
+    MutexLocker module_lock(Module_lock);
+    JfrCheckpointWriter leakp_writer(t);
+    JfrCheckpointWriter writer(t);
+    JfrTypeSet::serialize(&writer, &leakp_writer, false, false);
+    ObjectSampleCheckpoint::on_type_set(leakp_writer);
+  } else {
+    // can safepoint here
+    MutexLocker cld_lock(ClassLoaderDataGraph_lock);
+    MutexLocker module_lock(Module_lock);
+    JfrCheckpointWriter writer(Thread::current());
+    JfrTypeSet::serialize(&writer, NULL, false, false);
   }
-  Thread* const t = Thread::current();
-  JfrCheckpointWriter leakp_writer(false, true, t);
-  JfrCheckpointWriter writer(false, true, t);
-  JfrTypeSet::serialize(&writer, &leakp_writer, false);
-  ObjectSampleCheckpoint::on_type_set(leakp_writer);
+  write();
 }
 
 void JfrCheckpointManager::write_type_set_for_unloaded_classes() {
   assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
-  JfrCheckpointWriter writer(false, true, Thread::current());
+  JfrCheckpointWriter writer(Thread::current());
   const JfrCheckpointContext ctx = writer.context();
-  JfrTypeSet::serialize(&writer, NULL, true);
+  JfrTypeSet::serialize(&writer, NULL, true, false);
   if (LeakProfiler::is_running()) {
     ObjectSampleCheckpoint::on_type_set_unload(writer);
   }
@@ -387,16 +456,51 @@
   }
 }
 
-void JfrCheckpointManager::create_thread_blob(JavaThread* jt) {
-  JfrTypeManager::create_thread_blob(jt);
+bool JfrCheckpointManager::is_type_set_required() {
+  return JfrTraceIdEpoch::has_changed_tag_state();
+}
+
+size_t JfrCheckpointManager::flush_type_set() {
+  assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
+  size_t elements = 0;
+  {
+    JfrCheckpointWriter writer(Thread::current());
+    // can safepoint here
+    MutexLocker cld_lock(ClassLoaderDataGraph_lock);
+    MutexLocker module_lock(Module_lock);
+    elements = JfrTypeSet::serialize(&writer, NULL, false, true);
+  }
+  flush();
+  return elements;
+}
+
+void JfrCheckpointManager::flush_static_type_set() {
+  flush();
 }
 
-void JfrCheckpointManager::write_thread_checkpoint(JavaThread* jt) {
-  JfrTypeManager::write_thread_checkpoint(jt);
+void JfrCheckpointManager::create_thread_blob(Thread* t) {
+  JfrTypeManager::create_thread_blob(t);
+}
+
+void JfrCheckpointManager::write_thread_checkpoint(Thread* t) {
+  JfrTypeManager::write_thread_checkpoint(t);
 }
 
-void JfrCheckpointManager::shift_epoch() {
-  debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();)
-  JfrTraceIdEpoch::shift_epoch();
-  assert(current_epoch != JfrTraceIdEpoch::current(), "invariant");
+class JfrNotifyClosure : public ThreadClosure {
+ public:
+  void do_thread(Thread* t) {
+    assert(t != NULL, "invariant");
+    assert(t->is_Java_thread(), "invariant");
+    assert_locked_or_safepoint(Threads_lock);
+    JfrJavaEventWriter::notify((JavaThread*)t);
+  }
+};
+
+void JfrCheckpointManager::notify_threads() {
+  assert(SafepointSynchronize::is_at_safepoint(), "invariant");
+  JfrNotifyClosure tc;
+  JfrJavaThreadIterator iter;
+  while (iter.has_next()) {
+    tc.do_thread(iter.next());
+  }
 }
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -68,18 +68,29 @@
   void unlock();
   DEBUG_ONLY(bool is_locked() const;)
 
+  JfrCheckpointMspace* lookup(Buffer* old) const;
+  bool use_epoch_transition_mspace(const Thread* t) const;
+  size_t write_epoch_transition_mspace();
+
   static Buffer* lease_buffer(Thread* t, size_t size = 0);
+  static Buffer* lease_buffer(Buffer* old, Thread* t, size_t size = 0);
   static Buffer* flush(Buffer* old, size_t used, size_t requested, Thread* t);
 
   size_t clear();
   size_t write();
-  size_t write_epoch_transition_mspace();
-  size_t write_types();
-  size_t write_safepoint_types();
+  size_t flush();
+
+  bool is_static_type_set_required();
+  size_t write_static_type_set();
+  size_t write_threads();
+  size_t write_static_type_set_and_threads();
+  bool is_type_set_required();
   void write_type_set();
+  static void write_type_set_for_unloaded_classes();
+
   void shift_epoch();
   void synchronize_epoch();
-  bool use_epoch_transition_mspace(const Thread* t) const;
+  void notify_threads();
 
   JfrCheckpointManager(JfrChunkWriter& cw);
   ~JfrCheckpointManager();
@@ -87,14 +98,17 @@
   static JfrCheckpointManager& instance();
   static JfrCheckpointManager* create(JfrChunkWriter& cw);
   bool initialize();
+  void on_rotation();
   static void destroy();
 
  public:
+  size_t flush_type_set();
+  void flush_static_type_set();
+  static void create_thread_blob(Thread* t);
+  static void write_thread_checkpoint(Thread* t);
   void register_service_thread(const Thread* t);
-  static void write_type_set_for_unloaded_classes();
-  static void create_thread_blob(JavaThread* jt);
-  static void write_thread_checkpoint(JavaThread* jt);
 
+  friend class Jfr;
   friend class JfrRecorder;
   friend class JfrRecorderService;
   friend class JfrCheckpointFlush;
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -31,12 +31,26 @@
 JfrCheckpointFlush::JfrCheckpointFlush(Type* old, size_t used, size_t requested, Thread* t) :
   _result(JfrCheckpointManager::flush(old, used, requested, t)) {}
 
-JfrCheckpointWriter::JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread) :
-  JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(thread), thread),
+JfrCheckpointWriter::JfrCheckpointWriter(JfrCheckpointType type /* GENERIC */) :
+  JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(Thread::current()), Thread::current()),
   _time(JfrTicks::now()),
   _offset(0),
   _count(0),
-  _flushpoint(flushpoint),
+  _type(type),
+  _header(true) {
+  assert(this->is_acquired(), "invariant");
+  assert(0 == this->current_offset(), "invariant");
+  if (_header) {
+    reserve(sizeof(JfrCheckpointEntry));
+  }
+}
+
+JfrCheckpointWriter::JfrCheckpointWriter(Thread* t, bool header /* true */, JfrCheckpointType type /* GENERIC */) :
+  JfrCheckpointWriterBase(JfrCheckpointManager::lease_buffer(t), t),
+  _time(JfrTicks::now()),
+  _offset(0),
+  _count(0),
+  _type(type),
   _header(header) {
   assert(this->is_acquired(), "invariant");
   assert(0 == this->current_offset(), "invariant");
@@ -45,13 +59,27 @@
   }
 }
 
-static void write_checkpoint_header(u1* pos, int64_t size, jlong time, bool flushpoint, u4 type_count) {
+JfrCheckpointWriter::JfrCheckpointWriter(Thread* t, JfrBuffer* buffer, JfrCheckpointType type /* GENERIC */) :
+  JfrCheckpointWriterBase(buffer, t),
+  _time(JfrTicks::now()),
+  _offset(0),
+  _count(0),
+  _type(type),
+  _header(true) {
+  assert(this->is_acquired(), "invariant");
+  assert(0 == this->current_offset(), "invariant");
+  if (_header) {
+    reserve(sizeof(JfrCheckpointEntry));
+  }
+}
+
+static void write_checkpoint_header(u1* pos, int64_t size, jlong time, u4 checkpoint_type, u4 type_count) {
   assert(pos != NULL, "invariant");
   JfrBigEndianWriter be_writer(pos, sizeof(JfrCheckpointEntry));
   be_writer.write(size);
   be_writer.write(time);
   be_writer.write(JfrTicks::now().value() - time);
-  be_writer.write(flushpoint ? (u4)1 : (u4)0);
+  be_writer.write(checkpoint_type);
   be_writer.write(type_count);
   assert(be_writer.is_valid(), "invariant");
 }
@@ -74,18 +102,10 @@
   assert(this->used_size() > sizeof(JfrCheckpointEntry), "invariant");
   const int64_t size = this->current_offset();
   assert(size + this->start_pos() == this->current_pos(), "invariant");
-  write_checkpoint_header(const_cast<u1*>(this->start_pos()), size, _time, is_flushpoint(), count());
+  write_checkpoint_header(const_cast<u1*>(this->start_pos()), size, _time, (u4)_type, count());
   release();
 }
 
-void JfrCheckpointWriter::set_flushpoint(bool flushpoint) {
-  _flushpoint = flushpoint;
-}
-
-bool JfrCheckpointWriter::is_flushpoint() const {
-  return _flushpoint;
-}
-
 u4 JfrCheckpointWriter::count() const {
   return _count;
 }
@@ -140,7 +160,7 @@
   }
   *size = this->used_size();
   assert(this->start_pos() + *size == this->current_pos(), "invariant");
-  write_checkpoint_header(const_cast<u1*>(this->start_pos()), this->used_offset(), _time, is_flushpoint(), count());
+  write_checkpoint_header(const_cast<u1*>(this->start_pos()), this->used_offset(), _time, (u4)_type, count());
   _header = false; // the header was just written
   if (move) {
     this->seek(_offset);
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -54,23 +54,24 @@
 };
 
 class JfrCheckpointWriter : public JfrCheckpointWriterBase {
+  friend class JfrCheckpointManager;
   friend class JfrSerializerRegistration;
  private:
   JfrTicks _time;
   int64_t _offset;
   u4 _count;
-  bool _flushpoint;
+  JfrCheckpointType _type;
   bool _header;
 
   u4 count() const;
   void set_count(u4 count);
   void increment();
-  void set_flushpoint(bool flushpoint);
-  bool is_flushpoint() const;
   const u1* session_data(size_t* size, bool move = false, const JfrCheckpointContext* ctx = NULL);
   void release();
+  JfrCheckpointWriter(Thread* t, JfrBuffer* buffer, JfrCheckpointType type = GENERIC);
  public:
-  JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread);
+  JfrCheckpointWriter(JfrCheckpointType type = GENERIC);
+  JfrCheckpointWriter(Thread* t, bool header = true, JfrCheckpointType mode = GENERIC);
   ~JfrCheckpointWriter();
   void write_type(JfrTypeId type_id);
   void write_count(u4 nof_entries);
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,61 +29,53 @@
 #include "oops/klass.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/typeArrayOop.inline.hpp"
-#include "runtime/semaphore.hpp"
 #include "runtime/thread.inline.hpp"
 
-static jbyteArray _metadata_blob = NULL;
-static Semaphore metadata_mutex_semaphore(1);
+static jbyteArray metadata_blob = NULL;
+static u8 metadata_id = 0;
+static u8 last_metadata_id = 0;
 
-void JfrMetadataEvent::lock() {
-  metadata_mutex_semaphore.wait();
-}
-
-void JfrMetadataEvent::unlock() {
-  metadata_mutex_semaphore.signal();
+static void write_metadata_blob(JfrChunkWriter& chunkwriter) {
+  assert(metadata_blob != NULL, "invariant");
+  const typeArrayOop arr = (typeArrayOop)JfrJavaSupport::resolve_non_null(metadata_blob);
+  assert(arr != NULL, "invariant");
+  const int length = arr->length();
+  const Klass* const k = arr->klass();
+  assert(k != NULL && k->is_array_klass(), "invariant");
+  const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast(k);
+  const jbyte* const data_address = arr->byte_at_addr(0);
+  chunkwriter.write_unbuffered(data_address, length);
 }
 
-static void write_metadata_blob(JfrChunkWriter& chunkwriter, jbyteArray metadata_blob) {
-  if (metadata_blob != NULL) {
-    const typeArrayOop arr = (typeArrayOop)JfrJavaSupport::resolve_non_null(metadata_blob);
-    assert(arr != NULL, "invariant");
-    const int length = arr->length();
-    const Klass* const k = arr->klass();
-    assert(k != NULL && k->is_array_klass(), "invariant");
-    const TypeArrayKlass* const byte_arr_klass = TypeArrayKlass::cast(k);
-    const jbyte* const data_address = arr->byte_at_addr(0);
-    chunkwriter.write_unbuffered(data_address, length);
+void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) {
+  assert(chunkwriter.is_valid(), "invariant");
+  if (last_metadata_id == metadata_id && chunkwriter.has_metadata()) {
+    return;
   }
-}
-
-// the semaphore is assumed to be locked  (was locked previous safepoint)
-size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter, jlong metadata_offset) {
-  assert(chunkwriter.is_valid(), "invariant");
-  assert(chunkwriter.current_offset() == metadata_offset, "invariant");
   // header
-  chunkwriter.reserve(sizeof(u4));
+  const int64_t metadata_offset = chunkwriter.reserve(sizeof(u4));
   chunkwriter.write<u8>(EVENT_METADATA); // ID 0
   // time data
   chunkwriter.write(JfrTicks::now());
   chunkwriter.write((u8)0); // duration
-  chunkwriter.write((u8)0); // metadata id
-  write_metadata_blob(chunkwriter, _metadata_blob); // payload
-  unlock(); // open up for java to provide updated metadata
+  chunkwriter.write(metadata_id); // metadata id
+  write_metadata_blob(chunkwriter); // payload
   // fill in size of metadata descriptor event
-  const jlong size_written = chunkwriter.current_offset() - metadata_offset;
+  const int64_t size_written = chunkwriter.current_offset() - metadata_offset;
   chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset);
-  return size_written;
+  chunkwriter.set_last_metadata_offset(metadata_offset);
+  last_metadata_id = metadata_id;
 }
 
 void JfrMetadataEvent::update(jbyteArray metadata) {
   JavaThread* thread = (JavaThread*)Thread::current();
   assert(thread->is_Java_thread(), "invariant");
   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));
-  lock();
-  if (_metadata_blob != NULL) {
-    JfrJavaSupport::destroy_global_jni_handle(_metadata_blob);
+  if (metadata_blob != NULL) {
+    JfrJavaSupport::destroy_global_jni_handle(metadata_blob);
   }
   const oop new_desc_oop = JfrJavaSupport::resolve_non_null(metadata);
-  _metadata_blob = new_desc_oop != NULL ? (jbyteArray)JfrJavaSupport::global_jni_handle(new_desc_oop, thread) : NULL;
-  unlock();
+  assert(new_desc_oop != NULL, "invariant");
+  metadata_blob = (jbyteArray)JfrJavaSupport::global_jni_handle(new_desc_oop, thread);
+  ++metadata_id;
 }
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -33,13 +33,10 @@
 //
 // Metadata is continuously updated in Java as event classes are loaded / unloaded.
 // Using update(), Java stores a binary representation back to native.
-// This is for easy access on chunk finalization as well as having it readily available in the case of fatal error.
 //
 class JfrMetadataEvent : AllStatic {
  public:
-  static void lock();
-  static void unlock();
-  static size_t write(JfrChunkWriter& writer, jlong metadata_offset);
+  static void write(JfrChunkWriter& writer);
   static void update(jbyteArray metadata);
 };
 
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -25,7 +25,6 @@
 #include "precompiled.hpp"
 #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
 #include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
-#include "jfr/utilities/jfrResourceManager.hpp"
 #include "jfr/utilities/jfrTypes.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/jniHandles.inline.hpp"
@@ -33,6 +32,8 @@
 #include "runtime/semaphore.hpp"
 #include "utilities/growableArray.hpp"
 
+static const int initial_array_size = 30;
+
 class ThreadGroupExclusiveAccess : public StackObj {
  private:
   static Semaphore _mutex_semaphore;
@@ -257,12 +258,10 @@
   }
 }
 
-JfrThreadGroup::JfrThreadGroup() : _list(NULL) {
-  _list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(30, true);
-}
+JfrThreadGroup::JfrThreadGroup() :
+  _list(new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(initial_array_size, true, mtTracing)) {}
 
 JfrThreadGroup::~JfrThreadGroup() {
-  assert(SafepointSynchronize::is_at_safepoint(), "invariant");
   if (_list != NULL) {
     for (int i = 0; i < _list->length(); i++) {
       JfrThreadGroupEntry* e = _list->at(i);
@@ -281,14 +280,11 @@
 }
 
 traceid JfrThreadGroup::thread_group_id(const JavaThread* jt, Thread* current) {
-  ResourceMark rm(current);
-  HandleMark hm(current);
   JfrThreadGroupsHelper helper(jt, current);
   return helper.is_valid() ? thread_group_id_internal(helper) : 0;
 }
 
 traceid JfrThreadGroup::thread_group_id(JavaThread* const jt) {
-  assert(!JfrStream_lock->owned_by_self(), "holding stream lock but should not hold it here");
   return thread_group_id(jt, jt);
 }
 
@@ -396,9 +392,7 @@
   ThreadGroupExclusiveAccess lock;
   JfrThreadGroup* tg_instance = instance();
   assert(tg_instance != NULL, "invariant");
-  ResourceManager<JfrThreadGroup> tg_handle(tg_instance);
-  set_instance(NULL);
-  tg_handle->write_thread_group_entries(writer);
+  tg_instance->write_thread_group_entries(writer);
 }
 
 // for writing a particular thread group
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+* Copyright (c) 2016, 2019, 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
@@ -23,9 +23,13 @@
 */
 
 #include "precompiled.hpp"
+#include "classfile/javaClasses.inline.hpp"
 #include "jfr/recorder/checkpoint/types/jfrThreadState.hpp"
 #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
+#include "jfr/support/jfrThreadLocal.hpp"
 #include "jvmtifiles/jvmti.h"
+#include "runtime/osThread.hpp"
+#include "runtime/thread.hpp"
 
 struct jvmti_thread_state {
   u8 id;
@@ -80,3 +84,47 @@
   }
 }
 
+traceid JfrThreadId::id(const Thread* t) {
+  assert(t != NULL, "invariant");
+  if (!t->is_Java_thread()) {
+    return os_id(t);
+  }
+  const JavaThread* const jt = (JavaThread*)t;
+  const oop thread_obj = jt->threadObj();
+  return thread_obj != NULL ? java_lang_Thread::thread_id(thread_obj) : 0;
+}
+
+traceid JfrThreadId::os_id(const Thread* t) {
+  assert(t != NULL, "invariant");
+  const OSThread* const os_thread = t->osthread();
+  return os_thread != NULL ? os_thread->thread_id() : 0;
+}
+
+traceid JfrThreadId::jfr_id(const Thread* t) {
+  assert(t != NULL, "invariant");
+  return t->jfr_thread_local()->thread_id();
+}
+
+// caller needs ResourceMark
+const char* get_java_thread_name(const Thread* t) {
+  assert(t != NULL, "invariant");
+  assert(t->is_Java_thread(), "invariant");
+  const JavaThread* const jt = ((JavaThread*)t);
+  const char* name_str = "<no-name - thread name unresolved>";
+  const oop thread_obj = jt->threadObj();
+  if (thread_obj != NULL) {
+    const oop name = java_lang_Thread::name(thread_obj);
+    if (name != NULL) {
+      name_str = java_lang_String::as_utf8_string(name);
+    }
+  } else if (jt->is_attaching_via_jni()) {
+    name_str = "<no-name - thread is attaching>";
+  }
+  assert(name_str != NULL, "unexpected NULL thread name");
+  return name_str;
+}
+
+const char* JfrThreadName::name(const Thread* t) {
+  assert(t != NULL, "invariant");
+  return t->is_Java_thread() ? get_java_thread_name(t) : t->name();
+}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadState.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -28,10 +28,24 @@
 #include "memory/allocation.hpp"
 
 class JfrCheckpointWriter;
+class Thread;
 
 class JfrThreadState : public AllStatic {
  public:
   static void serialize(JfrCheckpointWriter& writer);
 };
 
+class JfrThreadId : public AllStatic {
+public:
+  static traceid id(const Thread* t);
+  static traceid os_id(const Thread* t);
+  static traceid jfr_id(const Thread* t);
+};
+
+class JfrThreadName : public AllStatic {
+ public:
+  // Requires a ResourceMark for get_thread_name/as_utf8
+  static const char* name(const Thread* t);
+};
+
 #endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADSTATE_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -31,13 +31,14 @@
 #include "gc/shared/gcTrace.hpp"
 #include "gc/shared/gcWhen.hpp"
 #include "jfr/leakprofiler/leakProfiler.hpp"
-#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
+#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
 #include "jfr/recorder/checkpoint/types/jfrType.hpp"
 #include "jfr/recorder/jfrRecorder.hpp"
 #include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp"
 #include "jfr/recorder/checkpoint/types/jfrThreadState.hpp"
 #include "jfr/support/jfrThreadLocal.hpp"
 #include "jfr/writers/jfrJavaEventWriter.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
 #include "memory/metaspaceGCThresholdUpdater.hpp"
 #include "memory/referenceType.hpp"
 #include "memory/universe.hpp"
@@ -84,27 +85,18 @@
   void do_thread(Thread* t);
 };
 
-// Requires a ResourceMark for get_thread_name/as_utf8
 void JfrCheckpointThreadClosure::do_thread(Thread* t) {
   assert(t != NULL, "invariant");
-  assert_locked_or_safepoint(Threads_lock);
-  const JfrThreadLocal* const tl = t->jfr_thread_local();
-  assert(tl != NULL, "invariant");
-  if (tl->is_dead()) {
-    return;
-  }
   ++_count;
-  _writer.write_key(tl->thread_id());
-  _writer.write(t->name());
-  const OSThread* const os_thread = t->osthread();
-  _writer.write<traceid>(os_thread != NULL ? os_thread->thread_id() : 0);
+  _writer.write_key(JfrThreadId::jfr_id(t));
+  const char* const name = JfrThreadName::name(t);
+  assert(name != NULL, "invariant");
+  _writer.write(name);
+  _writer.write<traceid>(JfrThreadId::os_id(t));
   if (t->is_Java_thread()) {
-    JavaThread* const jt = (JavaThread*)t;
-    _writer.write(jt->name());
-    _writer.write(java_lang_Thread::thread_id(jt->threadObj()));
-    _writer.write(JfrThreadGroup::thread_group_id(jt, _curthread));
-    // since we are iterating threads during a safepoint, also issue notification
-    JfrJavaEventWriter::notify(jt);
+    _writer.write(name);
+    _writer.write(JfrThreadId::id(t));
+    _writer.write(JfrThreadGroup::thread_group_id((JavaThread*)t, _curthread));
     return;
   }
   _writer.write((const char*)NULL); // java name
@@ -113,13 +105,18 @@
 }
 
 void JfrThreadConstantSet::serialize(JfrCheckpointWriter& writer) {
-  assert(SafepointSynchronize::is_at_safepoint(), "invariant");
   JfrCheckpointThreadClosure tc(writer);
-  Threads::threads_do(&tc);
+  JfrJavaThreadIterator javathreads;
+  while (javathreads.has_next()) {
+    tc.do_thread(javathreads.next());
+  }
+  JfrNonJavaThreadIterator nonjavathreads;
+  while (nonjavathreads.has_next()) {
+    tc.do_thread(nonjavathreads.next());
+  }
 }
 
 void JfrThreadGroupConstant::serialize(JfrCheckpointWriter& writer) {
-  assert(SafepointSynchronize::is_at_safepoint(), "invariant");
   JfrThreadGroup::serialize(writer);
 }
 
@@ -133,6 +130,7 @@
     case JVMFlag::ERGONOMIC: return "Ergonomic";
     case JVMFlag::ATTACH_ON_DEMAND: return "Attach on demand";
     case JVMFlag::INTERNAL: return "Internal";
+    case JVMFlag::JIMAGE_RESOURCE: return "JImage resource";
     default: ShouldNotReachHere(); return "";
   }
 }
@@ -277,19 +275,21 @@
 void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) {
   assert(_thread != NULL, "invariant");
   assert(_thread == Thread::current(), "invariant");
-  assert(_thread->is_Java_thread(), "invariant");
-  ResourceMark rm(_thread);
-  const oop threadObj = _thread->threadObj();
-  assert(threadObj != NULL, "invariant");
-  const u8 java_lang_thread_id = java_lang_Thread::thread_id(threadObj);
-  const char* const thread_name = _thread->name();
-  const traceid thread_group_id = JfrThreadGroup::thread_group_id(_thread);
   writer.write_count(1);
-  writer.write_key(_thread->jfr_thread_local()->thread_id());
-  writer.write(thread_name);
-  writer.write((traceid)_thread->osthread()->thread_id());
-  writer.write(thread_name);
-  writer.write(java_lang_thread_id);
-  writer.write(thread_group_id);
-  JfrThreadGroup::serialize(&writer, thread_group_id);
+  writer.write_key(JfrThreadId::jfr_id(_thread));
+  const char* const name = JfrThreadName::name(_thread);
+  writer.write(name);
+  writer.write(JfrThreadId::os_id(_thread));
+  if (_thread->is_Java_thread()) {
+    writer.write(name);
+    writer.write(JfrThreadId::id(_thread));
+    JavaThread* const jt = (JavaThread*)_thread;
+    const traceid thread_group_id = JfrThreadGroup::thread_group_id(jt, jt);
+    writer.write(thread_group_id);
+    JfrThreadGroup::serialize(&writer, thread_group_id);
+    return;
+  }
+  writer.write((const char*)NULL); // java name
+  writer.write((traceid)0); // java thread id
+  writer.write((traceid)0); // java thread group
 }
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -27,16 +27,6 @@
 
 #include "jfr/metadata/jfrSerializer.hpp"
 
-class JfrThreadConstantSet : public JfrSerializer {
- public:
-  void serialize(JfrCheckpointWriter& writer);
-};
-
-class JfrThreadGroupConstant : public JfrSerializer {
- public:
-  void serialize(JfrCheckpointWriter& writer);
-};
-
 class FlagValueOriginConstant : public JfrSerializer {
  public:
   void serialize(JfrCheckpointWriter& writer);
@@ -102,6 +92,16 @@
   void serialize(JfrCheckpointWriter& writer);
 };
 
+class JfrThreadConstantSet : public JfrSerializer {
+ public:
+  void serialize(JfrCheckpointWriter& writer);
+};
+
+class JfrThreadGroupConstant : public JfrSerializer {
+ public:
+  void serialize(JfrCheckpointWriter& writer);
+};
+
 class ThreadStateConstant : public JfrSerializer {
  public:
   void serialize(JfrCheckpointWriter& writer);
@@ -109,9 +109,9 @@
 
 class JfrThreadConstant : public JfrSerializer {
  private:
-  JavaThread* _thread;
+  Thread* _thread;
  public:
-  JfrThreadConstant(JavaThread* jt) : _thread(jt) {}
+  JfrThreadConstant(Thread* t) : _thread(t) {}
   void serialize(JfrCheckpointWriter& writer);
 };
 
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -27,6 +27,7 @@
 #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
 #include "jfr/recorder/checkpoint/types/jfrType.hpp"
 #include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp"
+#include "jfr/recorder/jfrRecorder.hpp"
 #include "jfr/utilities/jfrDoublyLinkedList.hpp"
 #include "jfr/utilities/jfrIterator.hpp"
 #include "memory/resourceArea.hpp"
@@ -73,29 +74,71 @@
     return _id;
   }
 
-  void invoke(JfrCheckpointWriter& writer) const;
+  void on_rotation() const {
+    _serializer->on_rotation();
+  }
+
+  void invoke(JfrCheckpointWriter& writer) const {
+    if (_cache.valid()) {
+      writer.increment();
+      _cache->write(writer);
+      return;
+    }
+    const JfrCheckpointContext ctx = writer.context();
+    // serialize the type id before invoking callback
+    writer.write_type(_id);
+    const intptr_t start = writer.current_offset();
+    // invoke the serializer routine
+    _serializer->serialize(writer);
+    if (start == writer.current_offset()) {
+      // the serializer implementation did nothing, rewind to restore
+      writer.set_context(ctx);
+      return;
+    }
+    if (_permit_cache) {
+      _cache = writer.copy(&ctx);
+    }
+  }
 };
 
-void JfrSerializerRegistration::invoke(JfrCheckpointWriter& writer) const {
-  if (_cache.valid()) {
-    writer.increment();
-    _cache->write(writer);
-    return;
-  }
-  const JfrCheckpointContext ctx = writer.context();
-  // serialize the type id before invoking callback
-  writer.write_type(_id);
-  const intptr_t start = writer.current_offset();
-  // invoke the serializer routine
-  _serializer->serialize(writer);
-  if (start == writer.current_offset() ) {
-    // the serializer implementation did nothing, rewind to restore
-    writer.set_context(ctx);
-    return;
-  }
-  if (_permit_cache) {
-    _cache = writer.copy(&ctx);
-  }
+static void serialize_threads(JfrCheckpointWriter& writer) {
+  JfrThreadConstantSet thread_set;
+  writer.write_type(TYPE_THREAD);
+  thread_set.serialize(writer);
+}
+
+static void serialize_thread_groups(JfrCheckpointWriter& writer) {
+  JfrThreadGroupConstant thread_group_set;
+  writer.write_type(TYPE_THREADGROUP);
+  thread_group_set.serialize(writer);
+}
+
+void JfrTypeManager::write_threads(JfrCheckpointWriter& writer) {
+  serialize_threads(writer);
+  serialize_thread_groups(writer);
+}
+
+void JfrTypeManager::create_thread_blob(Thread* t) {
+  assert(t != NULL, "invariant");
+  ResourceMark rm(t);
+  HandleMark hm(t);
+  JfrThreadConstant type_thread(t);
+  JfrCheckpointWriter writer(t, true, THREADS);
+  writer.write_type(TYPE_THREAD);
+  type_thread.serialize(writer);
+  // create and install a checkpoint blob
+  t->jfr_thread_local()->set_thread_blob(writer.move());
+  assert(t->jfr_thread_local()->has_thread_blob(), "invariant");
+}
+
+void JfrTypeManager::write_thread_checkpoint(Thread* t) {
+  assert(t != NULL, "invariant");
+  ResourceMark rm(t);
+  HandleMark hm(t);
+  JfrThreadConstant type_thread(t);
+  JfrCheckpointWriter writer(t, true, THREADS);
+  writer.write_type(TYPE_THREAD);
+  type_thread.serialize(writer);
 }
 
 class SerializerRegistrationGuard : public StackObj {
@@ -115,7 +158,6 @@
 typedef JfrDoublyLinkedList<JfrSerializerRegistration> List;
 typedef StopOnNullIterator<const List> Iterator;
 static List types;
-static List safepoint_types;
 
 void JfrTypeManager::destroy() {
   SerializerRegistrationGuard guard;
@@ -126,52 +168,15 @@
     assert(registration != NULL, "invariant");
     delete registration;
   }
-  Iterator sp_type_iter(safepoint_types);
-  while (sp_type_iter.has_next()) {
-    registration = safepoint_types.remove(sp_type_iter.next());
-    assert(registration != NULL, "invariant");
-    delete registration;
-  }
-}
-
-void JfrTypeManager::write_types(JfrCheckpointWriter& writer) {
-  const Iterator iter(types);
-  while (iter.has_next()) {
-    iter.next()->invoke(writer);
-  }
 }
 
-void JfrTypeManager::write_safepoint_types(JfrCheckpointWriter& writer) {
-  assert(SafepointSynchronize::is_at_safepoint(), "invariant");
-  const Iterator iter(safepoint_types);
+void JfrTypeManager::on_rotation() {
+  const Iterator iter(types);
   while (iter.has_next()) {
-    iter.next()->invoke(writer);
+    iter.next()->on_rotation();
   }
 }
 
-void JfrTypeManager::create_thread_blob(JavaThread* jt) {
-  assert(jt != NULL, "invariant");
-  ResourceMark rm(jt);
-  HandleMark hm(jt);
-  JfrThreadConstant type_thread(jt);
-  JfrCheckpointWriter writer(false, true, jt);
-  writer.write_type(TYPE_THREAD);
-  type_thread.serialize(writer);
-  // create and install a checkpoint blob
-  jt->jfr_thread_local()->set_thread_blob(writer.move());
-  assert(jt->jfr_thread_local()->has_thread_blob(), "invariant");
-}
-
-void JfrTypeManager::write_thread_checkpoint(JavaThread* jt) {
-  assert(jt != NULL, "JavaThread is NULL!");
-  ResourceMark rm(jt);
-  HandleMark hm(jt);
-  JfrThreadConstant type_thread(jt);
-  JfrCheckpointWriter writer(false, true, jt);
-  writer.write_type(TYPE_THREAD);
-  type_thread.serialize(writer);
-}
-
 #ifdef ASSERT
 static void assert_not_registered_twice(JfrTypeId id, List& list) {
   const Iterator iter(list);
@@ -181,52 +186,65 @@
 }
 #endif
 
-static bool register_type(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer) {
+static bool new_registration = false;
+
+static bool register_static_type(JfrTypeId id, bool permit_cache, JfrSerializer* serializer) {
   assert(serializer != NULL, "invariant");
   JfrSerializerRegistration* const registration = new JfrSerializerRegistration(id, permit_cache, serializer);
   if (registration == NULL) {
     delete serializer;
     return false;
   }
-  if (require_safepoint) {
-    assert(!safepoint_types.in_list(registration), "invariant");
-    DEBUG_ONLY(assert_not_registered_twice(id, safepoint_types);)
-    safepoint_types.prepend(registration);
-  } else {
-    assert(!types.in_list(registration), "invariant");
-    DEBUG_ONLY(assert_not_registered_twice(id, types);)
-    types.prepend(registration);
+  assert(!types.in_list(registration), "invariant");
+  DEBUG_ONLY(assert_not_registered_twice(id, types);)
+  if (JfrRecorder::is_recording()) {
+    JfrCheckpointWriter writer(STATICS);
+    registration->invoke(writer);
+    new_registration = true;
   }
+  types.prepend(registration);
   return true;
 }
 
 bool JfrTypeManager::initialize() {
   SerializerRegistrationGuard guard;
-
-  // register non-safepointing type serialization
-  register_type(TYPE_FLAGVALUEORIGIN, false, true, new FlagValueOriginConstant());
-  register_type(TYPE_INFLATECAUSE, false, true, new MonitorInflateCauseConstant());
-  register_type(TYPE_GCCAUSE, false, true, new GCCauseConstant());
-  register_type(TYPE_GCNAME, false, true, new GCNameConstant());
-  register_type(TYPE_GCWHEN, false, true, new GCWhenConstant());
-  register_type(TYPE_GCTHRESHOLDUPDATER, false, true, new GCThresholdUpdaterConstant());
-  register_type(TYPE_METADATATYPE, false, true, new MetadataTypeConstant());
-  register_type(TYPE_METASPACEOBJECTTYPE, false, true, new MetaspaceObjectTypeConstant());
-  register_type(TYPE_REFERENCETYPE, false, true, new ReferenceTypeConstant());
-  register_type(TYPE_NARROWOOPMODE, false, true, new NarrowOopModeConstant());
-  register_type(TYPE_COMPILERPHASETYPE, false, true, new CompilerPhaseTypeConstant());
-  register_type(TYPE_CODEBLOBTYPE, false, true, new CodeBlobTypeConstant());
-  register_type(TYPE_VMOPERATIONTYPE, false, true, new VMOperationTypeConstant());
-  register_type(TYPE_THREADSTATE, false, true, new ThreadStateConstant());
-
-  // register safepointing type serialization
-  register_type(TYPE_THREADGROUP, true, false, new JfrThreadGroupConstant());
-  register_type(TYPE_THREAD, true, false, new JfrThreadConstantSet());
+  register_static_type(TYPE_FLAGVALUEORIGIN, true, new FlagValueOriginConstant());
+  register_static_type(TYPE_INFLATECAUSE, true, new MonitorInflateCauseConstant());
+  register_static_type(TYPE_GCCAUSE, true, new GCCauseConstant());
+  register_static_type(TYPE_GCNAME, true, new GCNameConstant());
+  register_static_type(TYPE_GCWHEN, true, new GCWhenConstant());
+  register_static_type(TYPE_GCTHRESHOLDUPDATER, true, new GCThresholdUpdaterConstant());
+  register_static_type(TYPE_METADATATYPE, true, new MetadataTypeConstant());
+  register_static_type(TYPE_METASPACEOBJECTTYPE, true, new MetaspaceObjectTypeConstant());
+  register_static_type(TYPE_REFERENCETYPE, true, new ReferenceTypeConstant());
+  register_static_type(TYPE_NARROWOOPMODE, true, new NarrowOopModeConstant());
+  register_static_type(TYPE_COMPILERPHASETYPE, true, new CompilerPhaseTypeConstant());
+  register_static_type(TYPE_CODEBLOBTYPE, true, new CodeBlobTypeConstant());
+  register_static_type(TYPE_VMOPERATIONTYPE, true, new VMOperationTypeConstant());
+  register_static_type(TYPE_THREADSTATE, true, new ThreadStateConstant());
   return true;
 }
 
 // implementation for the static registration function exposed in the JfrSerializer api
-bool JfrSerializer::register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, JfrSerializer* serializer) {
+bool JfrSerializer::register_serializer(JfrTypeId id, bool permit_cache, JfrSerializer* serializer) {
   SerializerRegistrationGuard guard;
-  return register_type(id, require_safepoint, permit_cache, serializer);
+  return register_static_type(id, permit_cache, serializer);
 }
+
+bool JfrTypeManager::has_new_static_type() {
+  if (new_registration) {
+    SerializerRegistrationGuard guard;
+    new_registration = false;
+    return true;
+  }
+  return false;
+}
+
+void JfrTypeManager::write_static_types(JfrCheckpointWriter& writer) {
+  SerializerRegistrationGuard guard;
+  const Iterator iter(types);
+  while (iter.has_next()) {
+    iter.next()->invoke(writer);
+  }
+  new_registration = false;
+}
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -33,10 +33,12 @@
  public:
   static bool initialize();
   static void destroy();
-  static void write_types(JfrCheckpointWriter& writer);
-  static void write_safepoint_types(JfrCheckpointWriter& writer);
-  static void create_thread_blob(JavaThread* jt);
-  static void write_thread_checkpoint(JavaThread* jt);
+  static void on_rotation();
+  static void write_threads(JfrCheckpointWriter& writer);
+  static void create_thread_blob(Thread* t);
+  static void write_thread_checkpoint(Thread* t);
+  static bool has_new_static_type();
+  static void write_static_types(JfrCheckpointWriter& writer);
 };
 
 #endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPEMANAGER_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -43,6 +43,7 @@
 #include "oops/objArrayKlass.hpp"
 #include "oops/oop.inline.hpp"
 #include "utilities/accessFlags.hpp"
+#include "utilities/bitMap.inline.hpp"
 
 typedef const Klass* KlassPtr;
 typedef const PackageEntry* PkgPtr;
@@ -72,7 +73,7 @@
 }
 
 static bool current_epoch() {
-  return _class_unload;
+  return _class_unload || _flushpoint;
 }
 
 static bool previous_epoch() {
@@ -117,7 +118,7 @@
 static traceid module_id(PkgPtr pkg, bool leakp) {
   assert(pkg != NULL, "invariant");
   ModPtr module_entry = pkg->module();
-  if (module_entry == NULL || !module_entry->is_named()) {
+  if (module_entry == NULL) {
     return 0;
   }
   if (leakp) {
@@ -136,9 +137,7 @@
 
 static traceid cld_id(CldPtr cld, bool leakp) {
   assert(cld != NULL, "invariant");
-  if (cld->is_unsafe_anonymous()) {
-    return 0;
-  }
+  assert(!cld->is_unsafe_anonymous(), "invariant");
   if (leakp) {
     SET_LEAKP(cld);
   } else {
@@ -153,6 +152,17 @@
   return ptr->access_flags().get_flags();
 }
 
+static bool is_unsafe_anonymous(const Klass* klass) {
+  assert(klass != NULL, "invariant");
+  return klass->is_instance_klass() && ((const InstanceKlass*)klass)->is_unsafe_anonymous();
+}
+
+static ClassLoaderData* get_cld(const Klass* klass) {
+  assert(klass != NULL, "invariant");
+  return is_unsafe_anonymous(klass) ?
+    InstanceKlass::cast(klass)->unsafe_anonymous_host()->class_loader_data() : klass->class_loader_data();
+}
+
 template <typename T>
 static void set_serialized(const T* ptr) {
   assert(ptr != NULL, "invariant");
@@ -184,7 +194,7 @@
     assert(theklass->is_typeArray_klass(), "invariant");
   }
   writer->write(artifact_id(klass));
-  writer->write(cld_id(klass->class_loader_data(), leakp));
+  writer->write(cld_id(get_cld(klass), leakp));
   writer->write(mark_symbol(klass, leakp));
   writer->write(pkg_id);
   writer->write(get_flags(klass));
@@ -236,7 +246,7 @@
 static void do_klass(Klass* klass) {
   assert(klass != NULL, "invariant");
   assert(_subsystem_callback != NULL, "invariant");
-  if (current_epoch()) {
+  if (_flushpoint) {
     if (USED_THIS_EPOCH(klass)) {
       _subsystem_callback->do_artifact(klass);
       return;
@@ -538,13 +548,22 @@
   do_previous_epoch_artifact(_subsystem_callback, cld);
 }
 
-class CldFieldSelector {
+class KlassCldFieldSelector {
  public:
   typedef CldPtr TypePtr;
   static TypePtr select(KlassPtr klass) {
     assert(klass != NULL, "invariant");
-    CldPtr cld = klass->class_loader_data();
-    return cld->is_unsafe_anonymous() ? NULL : cld;
+    return get_cld(klass);
+  }
+};
+
+class ModuleCldFieldSelector {
+public:
+  typedef CldPtr TypePtr;
+  static TypePtr select(KlassPtr klass) {
+    assert(klass != NULL, "invariant");
+    ModPtr mod = ModuleFieldSelector::select(klass);
+    return mod != NULL ? mod->loader_data() : NULL;
   }
 };
 
@@ -570,14 +589,18 @@
 typedef JfrTypeWriterHost<CldWriterImpl, TYPE_CLASSLOADER> CldWriter;
 typedef CompositeFunctor<CldPtr, CldWriter, ClearArtifact<CldPtr> > CldWriterWithClear;
 typedef JfrArtifactCallbackHost<CldPtr, CldWriterWithClear> CldCallback;
-typedef KlassToFieldEnvelope<CldFieldSelector, CldWriter> KlassCldWriter;
+typedef KlassToFieldEnvelope<KlassCldFieldSelector, CldWriter> KlassCldWriter;
+typedef KlassToFieldEnvelope<ModuleCldFieldSelector, CldWriter> ModuleCldWriter;
+typedef CompositeFunctor<KlassPtr, KlassCldWriter, ModuleCldWriter> KlassAndModuleCldWriter;
 
 typedef LeakPredicate<CldPtr> LeakCldPredicate;
 typedef JfrPredicatedTypeWriterImplHost<CldPtr, LeakCldPredicate, write__classloader__leakp> LeakCldWriterImpl;
 typedef JfrTypeWriterHost<LeakCldWriterImpl, TYPE_CLASSLOADER> LeakCldWriter;
 
 typedef CompositeFunctor<CldPtr, LeakCldWriter, CldWriter> CompositeCldWriter;
-typedef KlassToFieldEnvelope<CldFieldSelector, CompositeCldWriter> KlassCompositeCldWriter;
+typedef KlassToFieldEnvelope<KlassCldFieldSelector, CompositeCldWriter> KlassCompositeCldWriter;
+typedef KlassToFieldEnvelope<ModuleCldFieldSelector, CompositeCldWriter> ModuleCompositeCldWriter;
+typedef CompositeFunctor<KlassPtr, KlassCompositeCldWriter, ModuleCompositeCldWriter> KlassAndModuleCompositeCldWriter;
 typedef CompositeFunctor<CldPtr, CompositeCldWriter, ClearArtifact<CldPtr> > CompositeCldWriterWithClear;
 typedef JfrArtifactCallbackHost<CldPtr, CompositeCldWriterWithClear> CompositeCldCallback;
 
@@ -585,14 +608,16 @@
   assert(_writer != NULL, "invariant");
   CldWriter cldw(_writer, _class_unload);
   KlassCldWriter kcw(&cldw);
+  ModuleCldWriter mcw(&cldw);
+  KlassAndModuleCldWriter kmcw(&kcw, &mcw);
   if (current_epoch()) {
-    _artifacts->iterate_klasses(kcw);
+    _artifacts->iterate_klasses(kmcw);
     _artifacts->tally(cldw);
     return;
   }
   assert(previous_epoch(), "invariant");
   if (_leakp_writer == NULL) {
-    _artifacts->iterate_klasses(kcw);
+    _artifacts->iterate_klasses(kmcw);
     ClearArtifact<CldPtr> clear;
     CldWriterWithClear cldwwc(&cldw, &clear);
     CldCallback callback(&cldwwc);
@@ -602,7 +627,9 @@
     LeakCldWriter lcldw(_leakp_writer, _class_unload);
     CompositeCldWriter ccldw(&lcldw, &cldw);
     KlassCompositeCldWriter kccldw(&ccldw);
-    _artifacts->iterate_klasses(kccldw);
+    ModuleCompositeCldWriter mccldw(&ccldw);
+    KlassAndModuleCompositeCldWriter kmccldw(&kccldw, &mccldw);
+    _artifacts->iterate_klasses(kmccldw);
     ClearArtifact<CldPtr> clear;
     CompositeCldWriterWithClear ccldwwc(&ccldw, &clear);
     CompositeCldCallback callback(&ccldwwc);
@@ -652,7 +679,31 @@
   return write_method(writer, method, true);
 }
 
-template <typename MethodCallback, typename KlassCallback, bool leakp>
+class BitMapFilter {
+  ResourceBitMap _bitmap;
+ public:
+  explicit BitMapFilter(int length = 0) : _bitmap((size_t)length) {}
+  bool operator()(size_t idx) {
+    if (_bitmap.size() == 0) {
+      return true;
+    }
+    if (_bitmap.at(idx)) {
+      return false;
+    }
+    _bitmap.set_bit(idx);
+    return true;
+  }
+};
+
+class AlwaysTrue {
+ public:
+  explicit AlwaysTrue(int length = 0) {}
+  bool operator()(size_t idx) {
+    return true;
+  }
+};
+
+template <typename MethodCallback, typename KlassCallback, class Filter, bool leakp>
 class MethodIteratorHost {
  private:
   MethodCallback _method_cb;
@@ -671,13 +722,19 @@
 
   bool operator()(KlassPtr klass) {
     if (_method_used_predicate(klass)) {
-      const InstanceKlass* const ik = InstanceKlass::cast(klass);
+      const InstanceKlass* ik = InstanceKlass::cast(klass);
       const int len = ik->methods()->length();
-      for (int i = 0; i < len; ++i) {
-        MethodPtr method = ik->methods()->at(i);
-        if (_method_flag_predicate(method)) {
-          _method_cb(method);
+      Filter filter(ik->previous_versions() != NULL ? len : 0);
+      while (ik != NULL) {
+        for (int i = 0; i < len; ++i) {
+          MethodPtr method = ik->methods()->at(i);
+          if (_method_flag_predicate(method) && filter(i)) {
+            _method_cb(method);
+          }
         }
+        // There can be multiple versions of the same method running
+        // due to redefinition. Need to inspect the complete set of methods.
+        ik = ik->previous_versions();
       }
     }
     return _klass_cb(klass);
@@ -697,16 +754,23 @@
   }
 };
 
+template <typename T>
+class EmptyStub {
+ public:
+  bool operator()(T const& value) { return true; }
+};
+
 typedef SerializePredicate<MethodPtr> MethodPredicate;
 typedef JfrPredicatedTypeWriterImplHost<MethodPtr, MethodPredicate, write__method> MethodWriterImplTarget;
-typedef Wrapper<KlassPtr, Stub> KlassCallbackStub;
+typedef Wrapper<KlassPtr, EmptyStub> KlassCallbackStub;
 typedef JfrTypeWriterHost<MethodWriterImplTarget, TYPE_METHOD> MethodWriterImpl;
-typedef MethodIteratorHost<MethodWriterImpl, KlassCallbackStub, false> MethodWriter;
+typedef MethodIteratorHost<MethodWriterImpl, KlassCallbackStub, BitMapFilter, false> MethodWriter;
 
 typedef LeakPredicate<MethodPtr> LeakMethodPredicate;
 typedef JfrPredicatedTypeWriterImplHost<MethodPtr, LeakMethodPredicate, write__method__leakp> LeakMethodWriterImplTarget;
 typedef JfrTypeWriterHost<LeakMethodWriterImplTarget, TYPE_METHOD> LeakMethodWriterImpl;
-typedef MethodIteratorHost<LeakMethodWriterImpl, KlassCallbackStub, true> LeakMethodWriter;
+typedef MethodIteratorHost<LeakMethodWriterImpl, KlassCallbackStub, BitMapFilter, true> LeakMethodWriter;
+typedef MethodIteratorHost<LeakMethodWriterImpl, KlassCallbackStub, BitMapFilter, true> LeakMethodWriter;
 typedef CompositeFunctor<KlassPtr, LeakMethodWriter, MethodWriter> CompositeMethodWriter;
 
 static void write_methods() {
@@ -832,7 +896,7 @@
 
 typedef Wrapper<KlassPtr, ClearArtifact> ClearKlassBits;
 typedef Wrapper<MethodPtr, ClearArtifact> ClearMethodFlag;
-typedef MethodIteratorHost<ClearMethodFlag, ClearKlassBits, false> ClearKlassAndMethods;
+typedef MethodIteratorHost<ClearMethodFlag, ClearKlassBits, AlwaysTrue, false> ClearKlassAndMethods;
 
 static size_t teardown() {
   assert(_artifacts != NULL, "invariant");
@@ -847,10 +911,11 @@
   return total_count;
 }
 
-static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload) {
+static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint) {
   _writer = writer;
   _leakp_writer = leakp_writer;
   _class_unload = class_unload;
+  _flushpoint = flushpoint;
   if (_artifacts == NULL) {
     _artifacts = new JfrArtifactSet(class_unload);
   } else {
@@ -864,10 +929,10 @@
 /**
  * Write all "tagged" (in-use) constant artifacts and their dependencies.
  */
-size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload) {
+size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint) {
   assert(writer != NULL, "invariant");
   ResourceMark rm;
-  setup(writer, leakp_writer, class_unload);
+  setup(writer, leakp_writer, class_unload, flushpoint);
   // write order is important because an individual write step
   // might tag an artifact to be written in a subsequent step
   if (!write_klasses()) {
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -32,7 +32,7 @@
 class JfrTypeSet : AllStatic {
  public:
   static void clear();
-  static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload);
+  static size_t serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer, bool class_unload, bool flushpoint);
 };
 
 #endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESET_HPP
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -99,12 +99,6 @@
 };
 
 template <typename T>
-class Stub {
- public:
-  bool operator()(T const& value) { return true; }
-};
-
-template <typename T>
 class SerializePredicate {
   bool _class_unload;
  public:
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -33,7 +33,6 @@
 #include "oops/method.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/atomic.hpp"
-#include "runtime/orderAccess.hpp"
 #include "runtime/vm_version.hpp"
 #include "runtime/jniHandles.inline.hpp"
 #include "runtime/thread.inline.hpp"
@@ -45,7 +44,7 @@
   traceid compare_value;
   traceid exchange_value;
   do {
-    compare_value = OrderAccess::load_acquire(dest);
+    compare_value = *dest;
     exchange_value = compare_value + 1;
   } while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
   return exchange_value;
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -31,7 +31,6 @@
 #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp"
 #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp"
 #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
-#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp"
 #include "jfr/support/jfrKlassExtension.hpp"
 #include "oops/arrayKlass.hpp"
 #include "oops/klass.hpp"
@@ -60,7 +59,14 @@
 
 inline traceid JfrTraceId::use(const Klass* klass) {
   assert(klass != NULL, "invariant");
-  return set_used_and_get(klass);
+  if (SHOULD_TAG(klass)) {
+    SET_USED_THIS_EPOCH(klass);
+    assert(USED_THIS_EPOCH(klass), "invariant");
+    JfrTraceIdEpoch::set_changed_tag_state();
+    return get(klass);
+  }
+  assert(USED_THIS_EPOCH(klass), "invariant");
+  return TRACE_ID(klass);
 }
 
 inline traceid JfrTraceId::use(const Method* method) {
@@ -71,10 +77,16 @@
 inline traceid JfrTraceId::use(const Klass* klass, const Method* method) {
   assert(klass != NULL, "invariant");
   assert(method != NULL, "invariant");
-  SET_METHOD_FLAG_USED_THIS_EPOCH(method);
-
-  SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass);
+  if (SHOULD_TAG_KLASS_METHOD(klass)) {
+    SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass);
+  }
   assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant");
+  if (METHOD_FLAG_NOT_USED_THIS_EPOCH(method)) {
+    assert(USED_THIS_EPOCH(klass), "invariant");
+    SET_METHOD_FLAG_USED_THIS_EPOCH(method);
+    JfrTraceIdEpoch::set_changed_tag_state();
+  }
+  assert(METHOD_FLAG_USED_THIS_EPOCH(method), "invariant");
   return (METHOD_ID(klass, method));
 }
 
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -38,7 +38,7 @@
 static const int meta_offset = low_offset - 1;
 #endif
 
-inline void set_bits(jbyte bits, jbyte* const dest) {
+inline void set_bits(jbyte bits, jbyte volatile* const dest) {
   assert(dest != NULL, "invariant");
   if (bits != (*dest & bits)) {
     *dest |= bits;
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -25,15 +25,13 @@
 #include "precompiled.hpp"
 #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
 #include "runtime/safepoint.hpp"
-#include "runtime/orderAccess.hpp"
 
 // Alternating epochs on each rotation allow for concurrent tagging.
-// The regular epoch shift happens only during a safepoint.
-// The fence is there only for the emergency dump case which happens outside of safepoint.
+// The epoch shift happens only during a safepoint.
 bool JfrTraceIdEpoch::_epoch_state = false;
+bool volatile JfrTraceIdEpoch::_tag_state = false;
+
 void JfrTraceIdEpoch::shift_epoch() {
+  assert(SafepointSynchronize::is_at_safepoint(), "invariant");
   _epoch_state = !_epoch_state;
-  if (!SafepointSynchronize::is_at_safepoint()) {
-    OrderAccess::fence();
-  }
 }
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -25,18 +25,19 @@
 #ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
 #define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
 
+#include "jfr/utilities/jfrTypes.hpp"
 #include "memory/allocation.hpp"
-#include "jfr/utilities/jfrTypes.hpp"
+#include "runtime/orderAccess.hpp"
 
-#define USED_BIT 1
-#define METHOD_USED_BIT (USED_BIT << 2)
-#define EPOCH_1_SHIFT 0
-#define EPOCH_2_SHIFT 1
-#define USED_EPOCH_1_BIT (USED_BIT << EPOCH_1_SHIFT)
-#define USED_EPOCH_2_BIT (USED_BIT << EPOCH_2_SHIFT)
-#define METHOD_USED_EPOCH_1_BIT (METHOD_USED_BIT << EPOCH_1_SHIFT)
-#define METHOD_USED_EPOCH_2_BIT (METHOD_USED_BIT << EPOCH_2_SHIFT)
-#define METHOD_AND_CLASS_IN_USE_BITS (METHOD_USED_BIT | USED_BIT)
+#define USED_BIT                             1
+#define METHOD_USED_BIT                      (USED_BIT << 2)
+#define EPOCH_1_SHIFT                        0
+#define EPOCH_2_SHIFT                        1
+#define USED_EPOCH_1_BIT                     (USED_BIT << EPOCH_1_SHIFT)
+#define USED_EPOCH_2_BIT                     (USED_BIT << EPOCH_2_SHIFT)
+#define METHOD_USED_EPOCH_1_BIT              (METHOD_USED_BIT << EPOCH_1_SHIFT)
+#define METHOD_USED_EPOCH_2_BIT              (METHOD_USED_BIT << EPOCH_2_SHIFT)
+#define METHOD_AND_CLASS_IN_USE_BITS         (METHOD_USED_BIT | USED_BIT)
 #define METHOD_AND_CLASS_IN_USE_EPOCH_1_BITS (METHOD_AND_CLASS_IN_USE_BITS << EPOCH_1_SHIFT)
 #define METHOD_AND_CLASS_IN_USE_EPOCH_2_BITS (METHOD_AND_CLASS_IN_USE_BITS << EPOCH_2_SHIFT)
 
@@ -44,6 +45,8 @@
   friend class JfrCheckpointManager;
  private:
   static bool _epoch_state;
+  static bool volatile _tag_state;
+
   static void shift_epoch();
 
  public:
@@ -86,6 +89,20 @@
   static traceid method_and_class_in_use_prev_epoch_bits() {
     return _epoch_state ? METHOD_AND_CLASS_IN_USE_EPOCH_1_BITS :  METHOD_AND_CLASS_IN_USE_EPOCH_2_BITS;
   }
+
+  static bool has_changed_tag_state() {
+    if (OrderAccess::load_acquire(&_tag_state)) {
+      OrderAccess::release_store(&_tag_state, false);
+      return true;
+    }
+    return false;
+  }
+
+  static void set_changed_tag_state() {
+    if (!OrderAccess::load_acquire(&_tag_state)) {
+      OrderAccess::release_store(&_tag_state, true);
+    }
+  }
 };
 
 #endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDEPOCH_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunk.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2012, 2019, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jfr/recorder/repository/jfrChunk.hpp"
+#include "jfr/recorder/service/jfrOptionSet.hpp"
+#include "jfr/utilities/jfrTime.hpp"
+#include "jfr/utilities/jfrTimeConverter.hpp"
+#include "jfr/utilities/jfrTypes.hpp"
+#include "runtime/os.inline.hpp"
+
+static const char* const MAGIC = "FLR";
+static const u2 JFR_VERSION_MAJOR = 2;
+static const u2 JFR_VERSION_MINOR = 1;
+
+// strictly monotone
+static jlong nanos_now() {
+  static jlong last = 0;
+  const jlong now = os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC;
+  if (now > last) {
+    last = now;
+  } else {
+    ++last;
+  }
+  return last;
+}
+
+static jlong ticks_now() {
+  return JfrTicks::now();
+}
+
+JfrChunk::JfrChunk() :
+  _path(NULL),
+  _start_ticks(0),
+  _previous_start_ticks(invalid_time),
+  _start_nanos(0),
+  _previous_start_nanos(invalid_time),
+  _last_update_nanos(0),
+  _last_checkpoint_offset(0),
+  _last_metadata_offset(0),
+  _generation(1) {}
+
+JfrChunk::~JfrChunk() {
+  reset();
+}
+
+void JfrChunk::reset() {
+  if (_path != NULL) {
+    JfrCHeapObj::free(_path, strlen(_path) + 1);
+    _path = NULL;
+  }
+  _last_checkpoint_offset = _last_metadata_offset = 0;
+  _generation = 1;
+}
+
+const char* JfrChunk::magic() const {
+  return MAGIC;
+}
+
+u2 JfrChunk::major_version() const {
+  return JFR_VERSION_MAJOR;
+}
+
+u2 JfrChunk::minor_version() const {
+  return JFR_VERSION_MINOR;
+}
+
+u2 JfrChunk::capabilities() const {
+  // chunk capabilities, CompressedIntegers etc
+  static bool compressed_integers = JfrOptionSet::compressed_integers();
+  return compressed_integers;
+}
+
+int64_t JfrChunk::cpu_frequency() const {
+  static const jlong frequency = JfrTime::frequency();
+  return frequency;
+}
+
+void JfrChunk::set_last_checkpoint_offset(int64_t offset) {
+  _last_checkpoint_offset = offset;
+}
+
+int64_t JfrChunk::last_checkpoint_offset() const {
+  return _last_checkpoint_offset;
+}
+
+int64_t JfrChunk::start_ticks() const {
+  assert(_start_ticks != 0, "invariant");
+  return _start_ticks;
+}
+
+int64_t JfrChunk::start_nanos() const {
+  assert(_start_nanos != 0, "invariant");
+  return _start_nanos;
+}
+
+int64_t JfrChunk::previous_start_ticks() const {
+  assert(_previous_start_ticks != invalid_time, "invariant");
+  return _previous_start_ticks;
+}
+
+int64_t JfrChunk::previous_start_nanos() const {
+  assert(_previous_start_nanos != invalid_time, "invariant");
+  return _previous_start_nanos;
+}
+
+void JfrChunk::update_start_ticks() {
+  _start_ticks = ticks_now();
+}
+
+void JfrChunk::update_start_nanos() {
+  const jlong now = nanos_now();
+  assert(now > _start_nanos, "invariant");
+  assert(now > _last_update_nanos, "invariant");
+  _start_nanos = _last_update_nanos = now;
+}
+
+void JfrChunk::update_current_nanos() {
+  const jlong now = nanos_now();
+  assert(now > _last_update_nanos, "invariant");
+  _last_update_nanos = now;
+}
+
+void JfrChunk::save_current_and_update_start_ticks() {
+  _previous_start_ticks = _start_ticks;
+  update_start_ticks();
+}
+
+void JfrChunk::save_current_and_update_start_nanos() {
+  _previous_start_nanos = _start_nanos;
+  update_start_nanos();
+}
+
+void JfrChunk::set_time_stamp() {
+  save_current_and_update_start_nanos();
+  save_current_and_update_start_ticks();
+}
+
+int64_t JfrChunk::last_chunk_duration() const {
+  assert(_previous_start_nanos != invalid_time, "invariant");
+  return _start_nanos - _previous_start_nanos;
+}
+
+static char* copy_path(const char* path) {
+  assert(path != NULL, "invariant");
+  const size_t path_len = strlen(path);
+  char* new_path = JfrCHeapObj::new_array<char>(path_len + 1);
+  strncpy(new_path, path, path_len + 1);
+  return new_path;
+}
+
+void JfrChunk::set_path(const char* path) {
+  if (_path != NULL) {
+    JfrCHeapObj::free(_path, strlen(_path) + 1);
+    _path = NULL;
+  }
+  if (path != NULL) {
+    _path = copy_path(path);
+  }
+}
+
+const char* JfrChunk::path() const {
+  return _path;
+}
+
+bool JfrChunk::is_started() const {
+  return _start_nanos != 0;
+}
+
+bool JfrChunk::is_finished() const {
+  return 0 == _generation;
+}
+
+int64_t JfrChunk::duration() const {
+  assert(_last_update_nanos >= _start_nanos, "invariant");
+  return _last_update_nanos - _start_nanos;
+}
+
+int64_t JfrChunk::last_metadata_offset() const {
+  return _last_metadata_offset;
+}
+
+void JfrChunk::set_last_metadata_offset(int64_t offset) {
+  assert(offset > _last_metadata_offset, "invariant");
+  _last_metadata_offset = offset;
+}
+
+bool JfrChunk::has_metadata() const {
+  return 0 != _last_metadata_offset;
+}
+
+u1 JfrChunk::generation() const {
+  assert(_generation > 0, "invariant");
+  const u1 this_generation = _generation++;
+  if (GUARD == _generation) {
+    _generation = 1;
+  }
+  return this_generation;
+}
+
+u1 JfrChunk::next_generation() const {
+  assert(_generation > 0, "invariant");
+  const u1 next_gen = _generation;
+  return GUARD == next_gen ? 1 : next_gen;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunk.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012, 2019, 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.
+ *
+ */
+
+#ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
+#define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
+
+#include "jfr/utilities/jfrAllocation.hpp"
+
+const u1 COMPLETE = 0;
+const u1 GUARD = 0xff;
+const u1 PAD = 0;
+
+class JfrChunk : public JfrCHeapObj {
+  friend class JfrChunkWriter;
+  friend class JfrChunkHeadWriter;
+ private:
+  char* _path;
+  int64_t _start_ticks;
+  int64_t _previous_start_ticks;
+  int64_t _start_nanos;
+  int64_t _previous_start_nanos;
+  int64_t _last_update_nanos;
+  int64_t _last_checkpoint_offset;
+  int64_t _last_metadata_offset;
+  mutable u1 _generation;
+
+  JfrChunk();
+  ~JfrChunk();
+  void reset();
+
+  const char* magic() const;
+  u2 major_version() const;
+  u2 minor_version() const;
+  int64_t cpu_frequency() const;
+  u2 capabilities() const;
+
+  void update_start_ticks();
+  void update_start_nanos();
+  void save_current_and_update_start_ticks();
+  void save_current_and_update_start_nanos();
+
+  int64_t last_checkpoint_offset() const;
+  void set_last_checkpoint_offset(int64_t offset);
+
+  int64_t last_metadata_offset() const;
+  void set_last_metadata_offset(int64_t offset);
+  bool has_metadata() const;
+
+  int64_t start_ticks() const;
+  int64_t start_nanos() const;
+
+  int64_t previous_start_ticks() const;
+  int64_t previous_start_nanos() const;
+  int64_t last_chunk_duration() const;
+
+  void set_time_stamp();
+  void update_current_nanos();
+
+  void set_path(const char* path);
+  const char* path() const;
+
+  bool is_started() const;
+  bool is_finished() const;
+
+  int64_t duration() const;
+  u1 generation() const;
+  u1 next_generation() const;
+};
+
+#endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkState.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,120 +0,0 @@
-/*
- * Copyright (c) 2012, 2019, 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.
- *
- */
-
-#include "precompiled.hpp"
-#include "jfr/dcmd/jfrDcmds.hpp"
-#include "jfr/recorder/jfrRecorder.hpp"
-#include "jfr/recorder/repository/jfrChunkState.hpp"
-#include "jfr/recorder/repository/jfrChunkWriter.hpp"
-#include "jfr/utilities/jfrTime.hpp"
-#include "jfr/utilities/jfrTimeConverter.hpp"
-#include "logging/log.hpp"
-#include "runtime/os.inline.hpp"
-#include "runtime/thread.inline.hpp"
-
-JfrChunkState::JfrChunkState() :
-  _path(NULL),
-  _start_ticks(0),
-  _start_nanos(0),
-  _previous_start_ticks(0),
-  _previous_start_nanos(0),
-  _last_checkpoint_offset(0) {}
-
-JfrChunkState::~JfrChunkState() {
-  reset();
-}
-
-void JfrChunkState::reset() {
-  if (_path != NULL) {
-    JfrCHeapObj::free(_path, strlen(_path) + 1);
-    _path = NULL;
-  }
-  set_last_checkpoint_offset(0);
-}
-
-void JfrChunkState::set_last_checkpoint_offset(int64_t offset) {
-  _last_checkpoint_offset = offset;
-}
-
-int64_t JfrChunkState::last_checkpoint_offset() const {
-  return _last_checkpoint_offset;
-}
-
-int64_t JfrChunkState::previous_start_ticks() const {
-  return _previous_start_ticks;
-}
-
-int64_t JfrChunkState::previous_start_nanos() const {
-  return _previous_start_nanos;
-}
-
-void JfrChunkState::update_start_ticks() {
-  _start_ticks = JfrTicks::now();
-}
-
-void JfrChunkState::update_start_nanos() {
-  _start_nanos = os::javaTimeMillis() * JfrTimeConverter::NANOS_PER_MILLISEC;
-}
-
-void JfrChunkState::save_current_and_update_start_ticks() {
-  _previous_start_ticks = _start_ticks;
-  update_start_ticks();
-}
-
-void JfrChunkState::save_current_and_update_start_nanos() {
-  _previous_start_nanos = _start_nanos;
-  update_start_nanos();
-}
-
-void JfrChunkState::update_time_to_now() {
-  save_current_and_update_start_nanos();
-  save_current_and_update_start_ticks();
-}
-
-int64_t JfrChunkState::last_chunk_duration() const {
-  return _start_nanos - _previous_start_nanos;
-}
-
-static char* copy_path(const char* path) {
-  assert(path != NULL, "invariant");
-  const size_t path_len = strlen(path);
-  char* new_path = JfrCHeapObj::new_array<char>(path_len + 1);
-  strncpy(new_path, path, path_len + 1);
-  return new_path;
-}
-
-void JfrChunkState::set_path(const char* path) {
-  assert(JfrStream_lock->owned_by_self(), "invariant");
-  if (_path != NULL) {
-    JfrCHeapObj::free(_path, strlen(_path) + 1);
-    _path = NULL;
-  }
-  if (path != NULL) {
-    _path = copy_path(path);
-  }
-}
-
-const char* JfrChunkState::path() const {
-  return _path;
-}
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkState.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2012, 2019, 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.
- *
- */
-
-#ifndef SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKSTATE_HPP
-#define SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKSTATE_HPP
-
-#include "jfr/utilities/jfrAllocation.hpp"
-#include "jfr/utilities/jfrTypes.hpp"
-
-class JfrChunkState : public JfrCHeapObj {
-  friend class JfrChunkWriter;
- private:
-  char* _path;
-  int64_t _start_ticks;
-  int64_t _start_nanos;
-  int64_t _previous_start_ticks;
-  int64_t _previous_start_nanos;
-  int64_t _last_checkpoint_offset;
-
-  void update_start_ticks();
-  void update_start_nanos();
-  void save_current_and_update_start_ticks();
-  void save_current_and_update_start_nanos();
-
-  JfrChunkState();
-  ~JfrChunkState();
-  void reset();
-  int64_t last_checkpoint_offset() const;
-  void set_last_checkpoint_offset(int64_t offset);
-  int64_t previous_start_ticks() const;
-  int64_t previous_start_nanos() const;
-  int64_t last_chunk_duration() const;
-  void update_time_to_now();
-  void set_path(const char* path);
-  const char* path() const;
-};
-
-#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKSTATE_HPP
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -23,82 +23,218 @@
  */
 
 #include "precompiled.hpp"
-#include "jfr/recorder/repository/jfrChunkState.hpp"
+#include "jfr/recorder/repository/jfrChunk.hpp"
 #include "jfr/recorder/repository/jfrChunkWriter.hpp"
-#include "jfr/recorder/service/jfrOptionSet.hpp"
 #include "jfr/utilities/jfrTime.hpp"
-#include "jfr/utilities/jfrTypes.hpp"
 #include "runtime/mutexLocker.hpp"
-#include "runtime/os.hpp"
 #include "runtime/os.inline.hpp"
 
-static const u2 JFR_VERSION_MAJOR = 2;
-static const u2 JFR_VERSION_MINOR = 0;
-static const size_t MAGIC_LEN = 4;
-static const size_t FILEHEADER_SLOT_SIZE = 8;
-static const size_t CHUNK_SIZE_OFFSET = 8;
-
-JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunkstate(NULL) {}
-
-bool JfrChunkWriter::initialize() {
-  assert(_chunkstate == NULL, "invariant");
-  _chunkstate = new JfrChunkState();
-  return _chunkstate != NULL;
-}
+static const int64_t MAGIC_OFFSET = 0;
+static const int64_t MAGIC_LEN = 4;
+static const int64_t VERSION_OFFSET = MAGIC_LEN;
+static const int64_t SIZE_OFFSET = 8;
+static const int64_t SLOT_SIZE = 8;
+static const int64_t CHECKPOINT_OFFSET = SIZE_OFFSET + SLOT_SIZE;
+static const int64_t METADATA_OFFSET = CHECKPOINT_OFFSET + SLOT_SIZE;
+static const int64_t START_NANOS_OFFSET = METADATA_OFFSET + SLOT_SIZE;
+static const int64_t DURATION_NANOS_OFFSET = START_NANOS_OFFSET + SLOT_SIZE;
+static const int64_t START_TICKS_OFFSET = DURATION_NANOS_OFFSET + SLOT_SIZE;
+static const int64_t CPU_FREQUENCY_OFFSET = START_TICKS_OFFSET + SLOT_SIZE;
+static const int64_t GENERATION_OFFSET = CPU_FREQUENCY_OFFSET + SLOT_SIZE;
+static const int64_t CAPABILITY_OFFSET = GENERATION_OFFSET + 2;
+static const int64_t HEADER_SIZE = CAPABILITY_OFFSET + 2;
 
 static fio_fd open_chunk(const char* path) {
-  assert(JfrStream_lock->owned_by_self(), "invariant");
   return path != NULL ? os::open(path, O_CREAT | O_RDWR, S_IREAD | S_IWRITE) : invalid_fd;
 }
 
-bool JfrChunkWriter::open() {
-  assert(_chunkstate != NULL, "invariant");
-  JfrChunkWriterBase::reset(open_chunk(_chunkstate->path()));
-  const bool is_open = this->has_valid_fd();
-  if (is_open) {
-    this->bytes("FLR", MAGIC_LEN);
-    this->be_write((u2)JFR_VERSION_MAJOR);
-    this->be_write((u2)JFR_VERSION_MINOR);
-    this->reserve(6 * FILEHEADER_SLOT_SIZE);
-    // u8 chunk_size
-    // u8 initial checkpoint offset
-    // u8 metadata section offset
-    // u8 chunk start nanos
-    // u8 chunk duration nanos
-    // u8 chunk start ticks
-    this->be_write(JfrTime::frequency());
-    // chunk capabilities, CompressedIntegers etc
-    this->be_write((u4)JfrOptionSet::compressed_integers() ? 1 : 0);
-    _chunkstate->reset();
+#ifdef ASSERT
+static void assert_writer_position(JfrChunkWriter* writer, int64_t offset) {
+  assert(writer != NULL, "invariant");
+  assert(offset == writer->current_offset(), "invariant");
+}
+#endif
+
+class JfrChunkHeadWriter : public StackObj {
+ private:
+  JfrChunkWriter* _writer;
+  JfrChunk* _chunk;
+ public:
+  void write_magic() {
+    _writer->bytes(_chunk->magic(), MAGIC_LEN);
+  }
+
+  void write_version() {
+    _writer->be_write(_chunk->major_version());
+    _writer->be_write(_chunk->minor_version());
+  }
+
+  void write_size(int64_t size) {
+    _writer->be_write(size);
+  }
+
+  void write_checkpoint() {
+    _writer->be_write(_chunk->last_checkpoint_offset());
+  }
+
+  void write_metadata() {
+    _writer->be_write(_chunk->last_metadata_offset());
+  }
+
+  void write_time(bool finalize) {
+    if (finalize) {
+      _writer->be_write(_chunk->previous_start_nanos());
+      _writer->be_write(_chunk->last_chunk_duration());
+      _writer->be_write(_chunk->previous_start_ticks());
+      return;
+    }
+    _writer->be_write(_chunk->start_nanos());
+    _writer->be_write(_chunk->duration());
+    _writer->be_write(_chunk->start_ticks());
+  }
+
+  void write_cpu_frequency() {
+    _writer->be_write(_chunk->cpu_frequency());
+  }
+
+  void write_generation(bool finalize) {
+    _writer->be_write(finalize ? COMPLETE : _chunk->generation());
+    _writer->be_write(PAD);
+  }
+
+  void write_next_generation() {
+    _writer->be_write(_chunk->next_generation());
+    _writer->be_write(PAD);
+  }
+
+  void write_guard() {
+    _writer->be_write(GUARD);
+    _writer->be_write(PAD);
+  }
+
+  void write_guard_flush() {
+    write_guard();
+    _writer->flush();
   }
-  return is_open;
+
+  void write_capabilities() {
+    _writer->be_write(_chunk->capabilities());
+  }
+
+  void write_size_to_generation(int64_t size, bool finalize) {
+    write_size(size);
+    write_checkpoint();
+    write_metadata();
+    write_time(finalize);
+    write_cpu_frequency();
+    write_generation(finalize);
+  }
+
+  void flush(int64_t size, bool finalize) {
+    assert(_writer->is_valid(), "invariant");
+    assert(_chunk != NULL, "invariant");
+    DEBUG_ONLY(assert_writer_position(_writer, SIZE_OFFSET);)
+    write_size_to_generation(size, finalize);
+    // no need to write capabilities
+    _writer->seek(size); // implicit flush
+  }
+
+  void initialize() {
+    assert(_writer->is_valid(), "invariant");
+    assert(_chunk != NULL, "invariant");
+    DEBUG_ONLY(assert_writer_position(_writer, 0);)
+    write_magic();
+    write_version();
+    write_size_to_generation(HEADER_SIZE, false);
+    write_capabilities();
+    DEBUG_ONLY(assert_writer_position(_writer, HEADER_SIZE);)
+    _writer->flush();
+  }
+
+  JfrChunkHeadWriter(JfrChunkWriter* writer, int64_t offset, bool guard = true) : _writer(writer), _chunk(writer->_chunk) {
+    assert(_writer != NULL, "invariant");
+    assert(_writer->is_valid(), "invariant");
+    assert(_chunk != NULL, "invariant");
+    if (0 == _writer->current_offset()) {
+      assert(HEADER_SIZE == offset, "invariant");
+      initialize();
+    } else {
+      if (guard) {
+        _writer->seek(GENERATION_OFFSET);
+        write_guard();
+        _writer->seek(offset);
+      } else {
+        _chunk->update_current_nanos();
+      }
+    }
+    DEBUG_ONLY(assert_writer_position(_writer, offset);)
+  }
+};
+
+static int64_t prepare_chunk_header_constant_pool(JfrChunkWriter& cw, int64_t event_offset, bool flushpoint) {
+  const int64_t delta = cw.last_checkpoint_offset() == 0 ? 0 : cw.last_checkpoint_offset() - event_offset;
+  const u4 checkpoint_type = flushpoint ? (u4)(FLUSH | HEADER) : (u4)HEADER;
+  cw.reserve(sizeof(u4));
+  cw.write<u8>(EVENT_CHECKPOINT);
+  cw.write<u8>(JfrTicks::now().value());
+  cw.write<u8>(0); // duration
+  cw.write<u8>(delta); // to previous checkpoint
+  cw.write<u4>(checkpoint_type);
+  cw.write<u4>(1); // pool count
+  cw.write<u8>(TYPE_CHUNKHEADER);
+  cw.write<u4>(1); // count
+  cw.write<u8>(1); // key
+  cw.write<u4>(HEADER_SIZE); // length of byte array
+  return cw.current_offset();
 }
 
-size_t JfrChunkWriter::close(int64_t metadata_offset) {
-  write_header(metadata_offset);
-  this->flush();
-  this->close_fd();
-  return (size_t)size_written();
+int64_t JfrChunkWriter::write_chunk_header_checkpoint(bool flushpoint) {
+  assert(this->has_valid_fd(), "invariant");
+  const int64_t event_size_offset = current_offset();
+  const int64_t header_content_pos = prepare_chunk_header_constant_pool(*this, event_size_offset, flushpoint);
+  JfrChunkHeadWriter head(this, header_content_pos, false);
+  head.write_magic();
+  head.write_version();
+  const int64_t chunk_size_offset = reserve(sizeof(int64_t)); // size to be decided when we are done
+  be_write(event_size_offset); // last checkpoint offset will be this checkpoint
+  head.write_metadata();
+  head.write_time(false);
+  head.write_cpu_frequency();
+  head.write_next_generation();
+  head.write_capabilities();
+  assert(current_offset() - header_content_pos == HEADER_SIZE, "invariant");
+  const u4 checkpoint_size = current_offset() - event_size_offset;
+  write_padded_at_offset<u4>(checkpoint_size, event_size_offset);
+  set_last_checkpoint_offset(event_size_offset);
+  const size_t sz_written = size_written();
+  write_be_at_offset(sz_written, chunk_size_offset);
+  return sz_written;
 }
 
-void JfrChunkWriter::write_header(int64_t metadata_offset) {
-  assert(this->is_valid(), "invariant");
-  // Chunk size
-  this->write_be_at_offset(size_written(), CHUNK_SIZE_OFFSET);
-  // initial checkpoint event offset
-  this->write_be_at_offset(_chunkstate->last_checkpoint_offset(), CHUNK_SIZE_OFFSET + (1 * FILEHEADER_SLOT_SIZE));
-  // metadata event offset
-  this->write_be_at_offset(metadata_offset, CHUNK_SIZE_OFFSET + (2 * FILEHEADER_SLOT_SIZE));
-  // start of chunk in nanos since epoch
-  this->write_be_at_offset(_chunkstate->previous_start_nanos(), CHUNK_SIZE_OFFSET + (3 * FILEHEADER_SLOT_SIZE));
-  // duration of chunk in nanos
-  this->write_be_at_offset(_chunkstate->last_chunk_duration(), CHUNK_SIZE_OFFSET + (4 * FILEHEADER_SLOT_SIZE));
-  // start of chunk in ticks
-  this->write_be_at_offset(_chunkstate->previous_start_ticks(), CHUNK_SIZE_OFFSET + (5 * FILEHEADER_SLOT_SIZE));
+int64_t JfrChunkWriter::flush_chunk(bool flushpoint) {
+  assert(_chunk != NULL, "invariant");
+  const int64_t sz_written = write_chunk_header_checkpoint(flushpoint);
+  assert(size_written() == sz_written, "invariant");
+  JfrChunkHeadWriter head(this, SIZE_OFFSET);
+  head.flush(sz_written, !flushpoint);
+  return sz_written;
 }
 
-void JfrChunkWriter::set_chunk_path(const char* chunk_path) {
-  _chunkstate->set_path(chunk_path);
+JfrChunkWriter::JfrChunkWriter() : JfrChunkWriterBase(NULL), _chunk(new JfrChunk()) {}
+
+JfrChunkWriter::~JfrChunkWriter() {
+  assert(_chunk != NULL, "invariant");
+  delete _chunk;
+}
+
+void JfrChunkWriter::set_path(const char* path) {
+  assert(_chunk != NULL, "invariant");
+  _chunk->set_path(path);
+}
+
+void JfrChunkWriter::set_time_stamp() {
+  assert(_chunk != NULL, "invariant");
+  _chunk->set_time_stamp();
 }
 
 int64_t JfrChunkWriter::size_written() const {
@@ -106,13 +242,46 @@
 }
 
 int64_t JfrChunkWriter::last_checkpoint_offset() const {
-  return _chunkstate->last_checkpoint_offset();
+  assert(_chunk != NULL, "invariant");
+  return _chunk->last_checkpoint_offset();
+}
+
+int64_t JfrChunkWriter::current_chunk_start_nanos() const {
+  assert(_chunk != NULL, "invariant");
+  return this->is_valid() ? _chunk->start_nanos() : invalid_time;
 }
 
 void JfrChunkWriter::set_last_checkpoint_offset(int64_t offset) {
-  _chunkstate->set_last_checkpoint_offset(offset);
+  assert(_chunk != NULL, "invariant");
+  _chunk->set_last_checkpoint_offset(offset);
+}
+
+void JfrChunkWriter::set_last_metadata_offset(int64_t offset) {
+  assert(_chunk != NULL, "invariant");
+  _chunk->set_last_metadata_offset(offset);
+}
+
+bool JfrChunkWriter::has_metadata() const {
+  assert(_chunk != NULL, "invariant");
+  return _chunk->has_metadata();
 }
 
-void JfrChunkWriter::time_stamp_chunk_now() {
-  _chunkstate->update_time_to_now();
+bool JfrChunkWriter::open() {
+  assert(_chunk != NULL, "invariant");
+  JfrChunkWriterBase::reset(open_chunk(_chunk->path()));
+  const bool is_open = this->has_valid_fd();
+  if (is_open) {
+    assert(0 == this->current_offset(), "invariant");
+    _chunk->reset();
+    JfrChunkHeadWriter head(this, HEADER_SIZE);
+  }
+  return is_open;
 }
+
+int64_t JfrChunkWriter::close() {
+  assert(this->has_valid_fd(), "invariant");
+  const int64_t size_written = flush_chunk(false);
+  this->close_fd();
+  assert(!this->is_valid(), "invariant");
+  return size_written;
+}
--- a/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -29,29 +29,36 @@
 #include "jfr/writers/jfrStreamWriterHost.inline.hpp"
 #include "jfr/writers/jfrWriterHost.inline.hpp"
 
-typedef MallocAdapter<M> JfrStreamBuffer; // 1 mb buffered writes
-typedef StreamWriterHost<JfrStreamBuffer, JfrCHeapObj> JfrBufferedStreamWriter;
-typedef WriterHost<BigEndianEncoder, CompressedIntegerEncoder, JfrBufferedStreamWriter> JfrChunkWriterBase;
+typedef MallocAdapter<M> JfrChunkBuffer; // 1 mb buffered writes
+typedef StreamWriterHost<JfrChunkBuffer, JfrCHeapObj> JfrBufferedChunkWriter;
+typedef WriterHost<BigEndianEncoder, CompressedIntegerEncoder, JfrBufferedChunkWriter> JfrChunkWriterBase;
 
-class JfrChunkState;
+class JfrChunk;
+class JfrChunkHeadWriter;
 
 class JfrChunkWriter : public JfrChunkWriterBase {
+  friend class JfrChunkHeadWriter;
   friend class JfrRepository;
  private:
-  JfrChunkState* _chunkstate;
-
+  JfrChunk* _chunk;
+  void set_path(const char* path);
+  int64_t flush_chunk(bool flushpoint);
   bool open();
-  size_t close(int64_t metadata_offset);
-  void write_header(int64_t metadata_offset);
-  void set_chunk_path(const char* chunk_path);
+  int64_t close();
+  int64_t current_chunk_start_nanos() const;
+  int64_t write_chunk_header_checkpoint(bool flushpoint);
 
  public:
   JfrChunkWriter();
-  bool initialize();
+  ~JfrChunkWriter();
+
   int64_t size_written() const;
   int64_t last_checkpoint_offset() const;
   void set_last_checkpoint_offset(int64_t offset);
-  void time_stamp_chunk_now();
+  void set_last_metadata_offset(int64_t offset);
+
+  bool has_metadata() const;
+  void set_time_stamp();
 };
 
 #endif // SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNKWRITER_HPP
--- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -248,7 +248,6 @@
 }
 
 static const char* create_emergency_dump_path() {
-  assert(JfrStream_lock->owned_by_self(), "invariant");
   char* buffer = NEW_RESOURCE_ARRAY_RETURN_NULL(char, JVM_MAXPATHLEN);
   if (NULL == buffer) {
     return NULL;
@@ -291,7 +290,6 @@
 // Caller needs ResourceMark
 static const char* create_emergency_chunk_path(const char* repository_path) {
   assert(repository_path != NULL, "invariant");
-  assert(JfrStream_lock->owned_by_self(), "invariant");
   const size_t repository_path_len = strlen(repository_path);
   // date time
   char date_time_buffer[32] = { 0 };
@@ -307,12 +305,11 @@
     return NULL;
   }
   // append the individual substrings
-  jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_path_len, os::file_separator(), date_time_buffer, chunk_file_jfr_ext);
+  jio_snprintf(chunk_path, chunkname_max_len, "%s%s%s%s", repository_path, os::file_separator(), date_time_buffer, chunk_file_jfr_ext);
   return chunk_path;
 }
 
 static fio_fd emergency_dump_file_descriptor() {
-  assert(JfrStream_lock->owned_by_self(), "invariant");
   ResourceMark rm;
   const char* const emergency_dump_path = create_emergency_dump_path();
   return emergency_dump_path != NULL ? open_exclusivly(emergency_dump_path) : invalid_fd;
@@ -325,7 +322,6 @@
 void JfrEmergencyDump::on_vm_error(const char* repository_path) {
   assert(repository_path != NULL, "invariant");
   ResourceMark rm;
-  MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
   const fio_fd emergency_fd = emergency_dump_file_descriptor();
   if (emergency_fd != invalid_fd) {
     RepositoryIterator iterator(repository_path, strlen(repository_path));
@@ -340,17 +336,25 @@
 *
 * 1. if the thread state is not "_thread_in_vm", we will quick transition
 *    it to "_thread_in_vm".
-* 2. the nesting state for both resource and handle areas are unknown,
-*    so we allocate new fresh arenas, discarding the old ones.
-* 3. if the thread is the owner of some critical lock(s), unlock them.
+* 2. if the thread is the owner of some critical lock(s), unlock them.
 *
 * If we end up deadlocking in the attempt of dumping out jfr data,
 * we rely on the WatcherThread task "is_error_reported()",
-* to exit the VM after a hard-coded timeout.
+* to exit the VM after a hard-coded timeout (disallow WatcherThread to emergency dump).
 * This "safety net" somewhat explains the aggressiveness in this attempt.
 *
 */
-static void prepare_for_emergency_dump(Thread* thread) {
+static bool prepare_for_emergency_dump() {
+  if (JfrStream_lock->owned_by_self()) {
+    // crashed during jfr rotation, disallow recursion
+    return false;
+  }
+  Thread* const thread = Thread::current();
+  if (thread->is_Watcher_thread()) {
+    // need WatcherThread as a safeguard against potential deadlocks
+    return false;
+  }
+
   if (thread->is_Java_thread()) {
     ((JavaThread*)thread)->set_thread_state(_thread_in_vm);
   }
@@ -388,7 +392,6 @@
     VMOperationRequest_lock->unlock();
   }
 
-
   if (Service_lock->owned_by_self()) {
     Service_lock->unlock();
   }
@@ -413,13 +416,10 @@
     JfrBuffer_lock->unlock();
   }
 
-  if (JfrStream_lock->owned_by_self()) {
-    JfrStream_lock->unlock();
-  }
-
   if (JfrStacktrace_lock->owned_by_self()) {
     JfrStacktrace_lock->unlock();
   }
+  return true;
 }
 
 static volatile int jfr_shutdown_lock = 0;
@@ -429,24 +429,9 @@
 }
 
 void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) {
-  if (!guard_reentrancy()) {
+  if (!(guard_reentrancy() && prepare_for_emergency_dump())) {
     return;
   }
-  // function made non-reentrant
-  Thread* thread = Thread::current();
-  if (exception_handler) {
-    // we are crashing
-    if (thread->is_Watcher_thread()) {
-      // The Watcher thread runs the periodic thread sampling task.
-      // If it has crashed, it is likely that another thread is
-      // left in a suspended state. This would mean the system
-      // will not be able to ever move to a safepoint. We try
-      // to avoid issuing safepoint operations when attempting
-      // an emergency dump, but a safepoint might be already pending.
-      return;
-    }
-    prepare_for_emergency_dump(thread);
-  }
   EventDumpReason event;
   if (event.should_commit()) {
     event.set_reason(exception_handler ? "Crash" : "Out of Memory");
@@ -458,8 +443,6 @@
     LeakProfiler::emit_events(max_jlong, false);
   }
   const int messages = MSGBIT(MSG_VM_ERROR);
-  ResourceMark rm(thread);
-  HandleMark hm(thread);
   JfrRecorderService service;
   service.rotate(messages);
 }
--- a/src/hotspot/share/jfr/recorder/repository/jfrRepository.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrRepository.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -26,13 +26,14 @@
 #include "jfr/jfr.hpp"
 #include "jfr/jni/jfrJavaSupport.hpp"
 #include "jfr/recorder/jfrRecorder.hpp"
-#include "jfr/recorder/repository/jfrChunkState.hpp"
 #include "jfr/recorder/repository/jfrChunkWriter.hpp"
 #include "jfr/recorder/repository/jfrEmergencyDump.hpp"
 #include "jfr/recorder/repository/jfrRepository.hpp"
 #include "jfr/recorder/service/jfrPostBox.hpp"
+#include "logging/log.hpp"
 #include "memory/resourceArea.hpp"
 #include "runtime/mutex.hpp"
+#include "runtime/os.hpp"
 #include "runtime/thread.inline.hpp"
 
 static JfrRepository* _instance = NULL;
@@ -43,11 +44,6 @@
 
 static JfrChunkWriter* _chunkwriter = NULL;
 
-static bool initialize_chunkwriter() {
-  assert(_chunkwriter == NULL, "invariant");
-  _chunkwriter = new JfrChunkWriter();
-  return _chunkwriter != NULL && _chunkwriter->initialize();
-}
 
 JfrChunkWriter& JfrRepository::chunkwriter() {
   return *_chunkwriter;
@@ -56,7 +52,9 @@
 JfrRepository::JfrRepository(JfrPostBox& post_box) : _path(NULL), _post_box(post_box) {}
 
 bool JfrRepository::initialize() {
-  return initialize_chunkwriter();
+  assert(_chunkwriter == NULL, "invariant");
+  _chunkwriter = new JfrChunkWriter();
+  return _chunkwriter != NULL;
 }
 
 JfrRepository::~JfrRepository() {
@@ -84,7 +82,6 @@
 }
 
 void JfrRepository::on_vm_error() {
-  assert(!JfrStream_lock->owned_by_self(), "invariant");
   if (_path == NULL) {
     // completed already
     return;
@@ -107,15 +104,19 @@
   return true;
 }
 
-void JfrRepository::set_chunk_path(const char* path) {
-  assert(JfrStream_lock->owned_by_self(), "invariant");
-  chunkwriter().set_chunk_path(path);
+void JfrRepository::notify_on_new_chunk_path() {
+  if (Jfr::is_recording()) {
+    // rotations are synchronous, block until rotation completes
+    instance()._post_box.post(MSG_ROTATE);
+  }
 }
 
-void JfrRepository::notify_on_new_chunk_path() {
-  if (Jfr::is_recording()) {
-    instance()._post_box.post(MSG_ROTATE);
-  }
+void JfrRepository::set_chunk_path(const char* path) {
+  chunkwriter().set_path(path);
+}
+
+jlong JfrRepository::current_chunk_start_nanos() {
+  return chunkwriter().current_chunk_start_nanos();
 }
 
 /**
@@ -134,14 +135,11 @@
   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
   ResourceMark rm(jt);
   const char* const canonical_chunk_path = JfrJavaSupport::c_str(path, jt);
-  {
-    MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
-    if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
-      // new output is NULL and current output is NULL
-      return;
-    }
-    instance().set_chunk_path(canonical_chunk_path);
+  if (NULL == canonical_chunk_path && !_chunkwriter->is_valid()) {
+    // new output is NULL and current output is NULL
+    return;
   }
+  instance().set_chunk_path(canonical_chunk_path);
   notify_on_new_chunk_path();
 }
 
@@ -155,14 +153,28 @@
 }
 
 bool JfrRepository::open_chunk(bool vm_error /* false */) {
-  assert(JfrStream_lock->owned_by_self(), "invariant");
   if (vm_error) {
     ResourceMark rm;
-    _chunkwriter->set_chunk_path(JfrEmergencyDump::build_dump_path(_path));
+    _chunkwriter->set_path(JfrEmergencyDump::build_dump_path(_path));
   }
   return _chunkwriter->open();
 }
 
-size_t JfrRepository::close_chunk(int64_t metadata_offset) {
-  return _chunkwriter->close(metadata_offset);
+size_t JfrRepository::close_chunk() {
+  return _chunkwriter->close();
 }
+
+void JfrRepository::flush(JavaThread* jt) {
+  DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
+  if (!Jfr::is_recording()) {
+    return;
+  }
+  if (!_chunkwriter->is_valid()) {
+    return;
+  }
+  instance()._post_box.post(MSG_FLUSHPOINT);
+}
+
+size_t JfrRepository::flush_chunk() {
+  return _chunkwriter->flush_chunk(true);
+}
--- a/src/hotspot/share/jfr/recorder/repository/jfrRepository.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/repository/jfrRepository.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -55,8 +55,10 @@
   bool set_path(const char* path);
   void set_chunk_path(const char* path);
   bool open_chunk(bool vm_error = false);
-  size_t close_chunk(int64_t metadata_offset);
+  size_t close_chunk();
+  size_t flush_chunk();
   void on_vm_error();
+
   static void notify_on_new_chunk_path();
   static JfrChunkWriter& chunkwriter();
 
@@ -68,6 +70,8 @@
  public:
   static void set_path(jstring location, JavaThread* jt);
   static void set_chunk_path(jstring path, JavaThread* jt);
+  static void flush(JavaThread* jt);
+  static jlong current_chunk_start_nanos();
 };
 
 #endif // SHARE_JFR_RECORDER_REPOSITORY_JFRREPOSITORY_HPP
--- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -26,14 +26,14 @@
 #include "jfr/recorder/service/jfrPostBox.hpp"
 #include "jfr/utilities/jfrTryLock.hpp"
 #include "runtime/atomic.hpp"
-#include "runtime/orderAccess.hpp"
 #include "runtime/thread.inline.hpp"
 
 #define MSG_IS_SYNCHRONOUS ( (MSGBIT(MSG_ROTATE)) |          \
                              (MSGBIT(MSG_STOP))   |          \
                              (MSGBIT(MSG_START))  |          \
                              (MSGBIT(MSG_CLONE_IN_MEMORY)) | \
-                             (MSGBIT(MSG_VM_ERROR))          \
+                             (MSGBIT(MSG_VM_ERROR))        | \
+                             (MSGBIT(MSG_FLUSHPOINT))        \
                            )
 
 static JfrPostBox* _instance = NULL;
@@ -84,7 +84,7 @@
 
 void JfrPostBox::deposit(int new_messages) {
   while (true) {
-    const int current_msgs = OrderAccess::load_acquire(&_messages);
+    const int current_msgs = Atomic::load(&_messages);
     // OR the new message
     const int exchange_value = current_msgs | new_messages;
     const int result = Atomic::cmpxchg(exchange_value, &_messages, current_msgs);
@@ -114,7 +114,7 @@
   deposit(msg);
   // serial_id is used to check when what we send in has been processed.
   // _msg_read_serial is read under JfrMsg_lock protection.
-  const uintptr_t serial_id = OrderAccess::load_acquire(&_msg_read_serial) + 1;
+  const uintptr_t serial_id = Atomic::load(&_msg_read_serial) + 1;
   msg_lock.notify_all();
   while (!is_message_processed(serial_id)) {
     msg_lock.wait();
@@ -129,12 +129,12 @@
  */
 bool JfrPostBox::is_message_processed(uintptr_t serial_id) const {
   assert(JfrMsg_lock->owned_by_self(), "_msg_handled_serial must be read under JfrMsg_lock protection");
-  return serial_id <= OrderAccess::load_acquire(&_msg_handled_serial);
+  return serial_id <= Atomic::load(&_msg_handled_serial);
 }
 
 bool JfrPostBox::is_empty() const {
   assert(JfrMsg_lock->owned_by_self(), "not holding JfrMsg_lock!");
-  return OrderAccess::load_acquire(&_messages) == 0;
+  return Atomic::load(&_messages) == 0;
 }
 
 int JfrPostBox::collect() {
--- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -41,6 +41,7 @@
   MSG_SHUTDOWN,
   MSG_VM_ERROR,
   MSG_DEADBUFFER,
+  MSG_FLUSHPOINT,
   MSG_NO_OF_MSGS
 };
 
@@ -53,15 +54,16 @@
  *  MSG_START(1)            ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2
  *  MSG_STOP (2)            ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4
  *  MSG_ROTATE (3)          ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8
- *  MSG_VM_ERROR (8)        ; MSGBIT(MSG_VM_ERROR) == (1 << 8) == 0x100
+ *  MSG_VM_ERROR (8)        ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100
+ *  MSG_FLUSHPOINT (10)     ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0xa) == 0x400
  *
  *  Asynchronous messages (posting thread returns immediately upon deposit):
  *
  *  MSG_FULLBUFFER (4)      ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10
- *  MSG_CHECKPOINT (5)      ; MSGBIT(CHECKPOINT) == (1 << 5) == 0x20
- *  MSG_WAKEUP (6)          ; MSGBIT(WAKEUP) == (1 << 6) == 0x40
- *  MSG_SHUTDOWN (7)        ; MSGBIT(MSG_SHUTDOWN) == (1 << 7) == 0x80
- *  MSG_DEADBUFFER (9)      ; MSGBIT(MSG_DEADBUFFER) == (1 << 9) == 0x200
+ *  MSG_CHECKPOINT (5)      ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20
+ *  MSG_WAKEUP (6)          ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40
+ *  MSG_SHUTDOWN (7)        ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80
+ *  MSG_DEADBUFFER (9)      ; MSGBIT(MSG_DEADBUFFER) == (1 << 0x9) == 0x200
  */
 
 class JfrPostBox : public JfrCHeapObj {
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -23,6 +23,7 @@
  */
 
 #include "precompiled.hpp"
+#include "jfrfiles/jfrEventClasses.hpp"
 #include "jfr/jni/jfrJavaSupport.hpp"
 #include "jfr/leakprofiler/leakProfiler.hpp"
 #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
@@ -55,81 +56,102 @@
 #include "runtime/vmOperations.hpp"
 #include "runtime/vmThread.hpp"
 
-// set data iff *dest == NULL
-static bool try_set(void* const data, void** dest, bool clear) {
-  assert(data != NULL, "invariant");
-  const void* const current = OrderAccess::load_acquire(dest);
-  if (current != NULL) {
-    if (current != data) {
-      // already set
-      return false;
-    }
-    assert(current == data, "invariant");
-    if (!clear) {
-      // recursion disallowed
-      return false;
-    }
+// incremented on each flushpoint
+static u8 flushpoint_id = 0;
+
+template <typename E, typename Instance, size_t(Instance::*func)()>
+class Content {
+ private:
+  Instance& _instance;
+  u4 _elements;
+ public:
+  typedef E EventType;
+  Content(Instance& instance) : _instance(instance), _elements(0) {}
+  bool process() {
+    _elements = (u4)(_instance.*func)();
+    return true;
   }
-  return Atomic::cmpxchg(clear ? NULL : data, dest, current) == current;
-}
-
-static void* rotation_thread = NULL;
-static const int rotation_try_limit = 1000;
-static const int rotation_retry_sleep_millis = 10;
-
-class RotationLock : public StackObj {
- private:
-  Thread* const _thread;
-  bool _acquired;
+  u4 elements() const { return _elements; }
+};
 
-  void log(bool recursion) {
-    assert(!_acquired, "invariant");
-    const char* error_msg = NULL;
-    if (recursion) {
-      error_msg = "Unable to issue rotation due to recursive calls.";
-    }
-    else {
-      error_msg = "Unable to issue rotation due to wait timeout.";
-    }
-    log_info(jfr)( // For user, should not be "jfr, system"
-      "%s", error_msg);
-  }
+template <typename Content>
+class WriteContent : public StackObj {
+ protected:
+  const JfrTicks _start_time;
+  JfrTicks _end_time;
+  JfrChunkWriter& _cw;
+  Content& _content;
+  const int64_t _start_offset;
  public:
-  RotationLock(Thread* thread) : _thread(thread), _acquired(false) {
-    assert(_thread != NULL, "invariant");
-    if (_thread == rotation_thread) {
-      // recursion not supported
-      log(true);
-      return;
-    }
+  typedef typename Content::EventType EventType;
 
-    // limited to not spin indefinitely
-    for (int i = 0; i < rotation_try_limit; ++i) {
-      if (try_set(_thread, &rotation_thread, false)) {
-        _acquired = true;
-        assert(_thread == rotation_thread, "invariant");
-        return;
-      }
-      if (_thread->is_Java_thread()) {
-        // in order to allow the system to move to a safepoint
-        MutexLocker msg_lock(JfrMsg_lock);
-        JfrMsg_lock->wait(rotation_retry_sleep_millis);
-      }
-      else {
-        os::naked_short_sleep(rotation_retry_sleep_millis);
-      }
-    }
-    log(false);
+  WriteContent(JfrChunkWriter& cw, Content& content) :
+    _start_time(JfrTicks::now()),
+    _end_time(),
+    _cw(cw),
+    _content(content),
+    _start_offset(_cw.current_offset()) {
+    assert(_cw.is_valid(), "invariant");
+  }
+
+  bool process() {
+    // invocation
+    _content.process();
+    _end_time = JfrTicks::now();
+    return 0 != _content.elements();
   }
 
-  ~RotationLock() {
-    assert(_thread != NULL, "invariant");
-    if (_acquired) {
-      assert(_thread == rotation_thread, "invariant");
-      while (!try_set(_thread, &rotation_thread, true));
-    }
+  const JfrTicks& start_time() const {
+    return _start_time;
+  }
+
+  const JfrTicks& end_time() const {
+    return _end_time;
+  }
+
+  int64_t start_offset() const {
+    return _start_offset;
+  }
+
+  int64_t end_offset() const {
+    return current_offset();
+  }
+
+  int64_t current_offset() const {
+    return _cw.current_offset();
+  }
+
+  u4 elements() const {
+    return (u4) _content.elements();
   }
-  bool not_acquired() const { return !_acquired; }
+
+  u4 size() const {
+    return (u4)(end_offset() - start_offset());
+  }
+
+  static bool is_event_enabled() {
+    return EventType::is_enabled();
+  }
+
+  static u8 event_id() {
+    return EventType::eventId;
+  }
+
+  void write_elements(int64_t offset) {
+    _cw.write_padded_at_offset<u4>(elements(), offset);
+  }
+
+  void write_size() {
+    _cw.write_padded_at_offset<u4>(size(), start_offset());
+  }
+
+  void set_last_checkpoint() {
+    _cw.set_last_checkpoint_offset(start_offset());
+  }
+
+  void rewind() {
+    _cw.seek(start_offset());
+  }
 };
 
 static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) {
@@ -138,65 +160,176 @@
   cw.reserve(sizeof(u4));
   cw.write<u8>(EVENT_CHECKPOINT);
   cw.write(JfrTicks::now());
-  cw.write((int64_t)0); // duration
+  cw.write<u8>(0); // duration
   cw.write(delta_to_last_checkpoint);
-  cw.write<bool>(false); // flushpoint
-  cw.write((u4)1); // nof types in this checkpoint
+  cw.write<u4>(GENERIC); // checkpoint type
+  cw.write<u4>(1); // nof types in this checkpoint
   cw.write(type_id);
-  const int64_t number_of_elements_offset = cw.current_offset();
-  cw.reserve(sizeof(u4));
-  return number_of_elements_offset;
+  return cw.reserve(sizeof(u4));
 }
 
-template <typename ContentFunctor>
-class WriteCheckpointEvent : public StackObj {
+template <typename Content>
+class WriteCheckpointEvent : public WriteContent<Content> {
  private:
-  JfrChunkWriter& _cw;
-  u8 _type_id;
-  ContentFunctor& _content_functor;
+  const u8 _type_id;
  public:
-  WriteCheckpointEvent(JfrChunkWriter& cw, u8 type_id, ContentFunctor& functor) :
-    _cw(cw),
-    _type_id(type_id),
-    _content_functor(functor) {
-    assert(_cw.is_valid(), "invariant");
-  }
+  WriteCheckpointEvent(JfrChunkWriter& cw, Content& content, u8 type_id) :
+    WriteContent<Content>(cw, content), _type_id(type_id) {}
+
   bool process() {
-    // current_cp_offset is also offset for the event size header field
-    const int64_t current_cp_offset = _cw.current_offset();
-    const int64_t num_elements_offset = write_checkpoint_event_prologue(_cw, _type_id);
-    // invocation
-    _content_functor.process();
-    const u4 number_of_elements = (u4)_content_functor.processed();
-    if (number_of_elements == 0) {
+    const int64_t num_elements_offset = write_checkpoint_event_prologue(this->_cw, _type_id);
+    if (!WriteContent<Content>::process()) {
       // nothing to do, rewind writer to start
-      _cw.seek(current_cp_offset);
-      return true;
+      this->rewind();
+      assert(this->current_offset() == this->start_offset(), "invariant");
+      return false;
     }
-    assert(number_of_elements > 0, "invariant");
-    assert(_cw.current_offset() > num_elements_offset, "invariant");
-    _cw.write_padded_at_offset<u4>(number_of_elements, num_elements_offset);
-    _cw.write_padded_at_offset<u4>((u4)_cw.current_offset() - current_cp_offset, current_cp_offset);
-    // update writer with last checkpoint position
-    _cw.set_last_checkpoint_offset(current_cp_offset);
+    assert(this->elements() > 0, "invariant");
+    assert(this->current_offset() > num_elements_offset, "invariant");
+    this->write_elements(num_elements_offset);
+    this->write_size();
+    this->set_last_checkpoint();
     return true;
   }
 };
 
-template <typename Instance, size_t(Instance::*func)()>
-class ServiceFunctor {
+template <typename Functor>
+static u4 invoke(Functor& f) {
+  f.process();
+  return f.elements();
+}
+
+template <typename Functor>
+static void write_flush_event(Functor& f) {
+  if (Functor::is_event_enabled()) {
+    typename Functor::EventType e(UNTIMED);
+    e.set_starttime(f.start_time());
+    e.set_endtime(f.end_time());
+    e.set_flushId(flushpoint_id);
+    e.set_elements(f.elements());
+    e.set_size(f.size());
+    e.commit();
+  }
+}
+
+template <typename Functor>
+static u4 invoke_with_flush_event(Functor& f) {
+  const u4 elements = invoke(f);
+  write_flush_event(f);
+  return elements;
+}
+
+class StackTraceRepository : public StackObj {
  private:
-  Instance& _instance;
-  size_t _processed;
+  JfrStackTraceRepository& _repo;
+  JfrChunkWriter& _cw;
+  size_t _elements;
+  bool _clear;
+
  public:
-  ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {}
+  typedef EventFlushStacktrace EventType;
+  StackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) :
+    _repo(repo), _cw(cw), _elements(0), _clear(clear) {}
   bool process() {
-    _processed = (_instance.*func)();
+    _elements = _repo.write(_cw, _clear);
     return true;
   }
-  size_t processed() const { return _processed; }
+  size_t elements() const { return _elements; }
+  void reset() { _elements = 0; }
 };
 
+typedef WriteCheckpointEvent<StackTraceRepository> WriteStackTrace;
+
+static u4 flush_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter) {
+  StackTraceRepository str(stack_trace_repo, chunkwriter, false);
+  WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE);
+  return invoke_with_flush_event(wst);
+}
+
+static u4 write_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) {
+  StackTraceRepository str(stack_trace_repo, chunkwriter, clear);
+  WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE);
+  return invoke(wst);
+}
+
+typedef Content<EventFlushStorage, JfrStorage, &JfrStorage::write> Storage;
+typedef WriteContent<Storage> WriteStorage;
+
+static size_t flush_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) {
+  assert(chunkwriter.is_valid(), "invariant");
+  Storage fsf(storage);
+  WriteStorage fs(chunkwriter, fsf);
+  return invoke_with_flush_event(fs);
+}
+
+static size_t write_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) {
+  assert(chunkwriter.is_valid(), "invariant");
+  Storage fsf(storage);
+  WriteStorage fs(chunkwriter, fsf);
+  return invoke(fs);
+}
+
+typedef Content<EventFlushStringPool, JfrStringPool, &JfrStringPool::write> StringPool;
+typedef Content<EventFlushStringPool, JfrStringPool, &JfrStringPool::write_at_safepoint> StringPoolSafepoint;
+typedef WriteCheckpointEvent<StringPool> WriteStringPool;
+typedef WriteCheckpointEvent<StringPoolSafepoint> WriteStringPoolSafepoint;
+
+static u4 flush_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
+  StringPool sp(string_pool);
+  WriteStringPool wsp(chunkwriter, sp, TYPE_STRING);
+  return invoke_with_flush_event(wsp);
+}
+
+static u4 write_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
+  StringPool sp(string_pool);
+  WriteStringPool wsp(chunkwriter, sp, TYPE_STRING);
+  return invoke(wsp);
+}
+
+static u4 write_stringpool_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
+  StringPoolSafepoint sps(string_pool);
+  WriteStringPoolSafepoint wsps(chunkwriter, sps, TYPE_STRING);
+  return invoke(wsps);
+}
+
+typedef Content<EventFlushTypeSet, JfrCheckpointManager, &JfrCheckpointManager::flush_type_set> FlushTypeSetFunctor;
+typedef WriteContent<FlushTypeSetFunctor> FlushTypeSet;
+
+static u4 flush_typeset(JfrCheckpointManager& checkpoint_manager, JfrChunkWriter& chunkwriter) {
+  FlushTypeSetFunctor flush_type_set(checkpoint_manager);
+  FlushTypeSet fts(chunkwriter, flush_type_set);
+  return invoke_with_flush_event(fts);
+}
+
+class MetadataEvent : public StackObj {
+ private:
+  JfrChunkWriter& _cw;
+ public:
+  typedef EventFlushMetadata EventType;
+  MetadataEvent(JfrChunkWriter& cw) : _cw(cw) {}
+  bool process() {
+    JfrMetadataEvent::write(_cw);
+    return true;
+  }
+  size_t elements() const { return 1; }
+};
+
+typedef WriteContent<MetadataEvent> WriteMetadata;
+
+static u4 flush_metadata(JfrChunkWriter& chunkwriter) {
+  assert(chunkwriter.is_valid(), "invariant");
+  MetadataEvent me(chunkwriter);
+  WriteMetadata wm(chunkwriter, me);
+  return invoke_with_flush_event(wm);
+}
+
+static u4 write_metadata(JfrChunkWriter& chunkwriter) {
+  assert(chunkwriter.is_valid(), "invariant");
+  MetadataEvent me(chunkwriter);
+  WriteMetadata wm(chunkwriter, me);
+  return invoke(wm);
+}
+
 template <typename Instance, void(Instance::*func)()>
 class JfrVMOperation : public VM_Operation {
  private:
@@ -208,23 +341,13 @@
   Mode evaluation_mode() const { return _safepoint; } // default
 };
 
-class WriteStackTraceRepository : public StackObj {
- private:
-  JfrStackTraceRepository& _repo;
-  JfrChunkWriter& _cw;
-  size_t _elements_processed;
-  bool _clear;
-
- public:
-  WriteStackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) :
-    _repo(repo), _cw(cw), _elements_processed(0), _clear(clear) {}
-  bool process() {
-    _elements_processed = _repo.write(_cw, _clear);
-    return true;
-  }
-  size_t processed() const { return _elements_processed; }
-  void reset() { _elements_processed = 0; }
-};
+JfrRecorderService::JfrRecorderService() :
+  _checkpoint_manager(JfrCheckpointManager::instance()),
+  _chunkwriter(JfrRepository::chunkwriter()),
+  _repository(JfrRepository::instance()),
+  _stack_trace_repository(JfrStackTraceRepository::instance()),
+  _storage(JfrStorage::instance()),
+  _string_pool(JfrStringPool::instance()) {}
 
 static bool recording = false;
 
@@ -237,19 +360,8 @@
   return recording;
 }
 
-JfrRecorderService::JfrRecorderService() :
-  _checkpoint_manager(JfrCheckpointManager::instance()),
-  _chunkwriter(JfrRepository::chunkwriter()),
-  _repository(JfrRepository::instance()),
-  _stack_trace_repository(JfrStackTraceRepository::instance()),
-  _storage(JfrStorage::instance()),
-  _string_pool(JfrStringPool::instance()) {}
-
 void JfrRecorderService::start() {
-  RotationLock rl(Thread::current());
-  if (rl.not_acquired()) {
-    return;
-  }
+  MutexLocker lock(JfrStream_lock);
   log_debug(jfr, system)("Request to START recording");
   assert(!is_recording(), "invariant");
   clear();
@@ -268,9 +380,9 @@
 }
 
 void JfrRecorderService::pre_safepoint_clear() {
-  _stack_trace_repository.clear();
   _string_pool.clear();
   _storage.clear();
+  _stack_trace_repository.clear();
 }
 
 void JfrRecorderService::invoke_safepoint_clear() {
@@ -278,28 +390,28 @@
   VMThread::execute(&safepoint_task);
 }
 
-//
-// safepoint clear sequence
-//
-//  clear stacktrace repository ->
-//    clear string pool ->
-//      clear storage ->
-//        shift epoch ->
-//          update time
-//
 void JfrRecorderService::safepoint_clear() {
   assert(SafepointSynchronize::is_at_safepoint(), "invariant");
-  _stack_trace_repository.clear();
   _string_pool.clear();
   _storage.clear();
   _checkpoint_manager.shift_epoch();
-  _chunkwriter.time_stamp_chunk_now();
+  _chunkwriter.set_time_stamp();
+  _stack_trace_repository.clear();
 }
 
 void JfrRecorderService::post_safepoint_clear() {
   _checkpoint_manager.clear();
 }
 
+void JfrRecorderService::open_new_chunk(bool vm_error) {
+  JfrChunkRotation::on_rotation();
+  const bool valid_chunk = _repository.open_chunk(vm_error);
+  _storage.control().set_to_disk(valid_chunk);
+  if (valid_chunk) {
+    _checkpoint_manager.write_static_type_set_and_threads();
+  }
+}
+
 static void stop() {
   assert(JfrRecorderService::is_recording(), "invariant");
   log_debug(jfr, system)("Recording STOPPED");
@@ -307,11 +419,30 @@
   assert(!JfrRecorderService::is_recording(), "invariant");
 }
 
+void JfrRecorderService::prepare_for_vm_error_rotation() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
+  if (!_chunkwriter.is_valid()) {
+    open_new_chunk(true);
+  }
+  _checkpoint_manager.register_service_thread(Thread::current());
+}
+
+void JfrRecorderService::vm_error_rotation() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
+  if (_chunkwriter.is_valid()) {
+    Thread* const t = Thread::current();
+    _storage.flush_regular_buffer(t->jfr_thread_local()->native_buffer(), t);
+    invoke_flush();
+    _chunkwriter.set_time_stamp();
+    _repository.close_chunk();
+    assert(!_chunkwriter.is_valid(), "invariant");
+    _repository.on_vm_error();
+  }
+}
+
 void JfrRecorderService::rotate(int msgs) {
-  RotationLock rl(Thread::current());
-  if (rl.not_acquired()) {
-    return;
-  }
+  assert(!JfrStream_lock->owned_by_self(), "invariant");
+  MutexLocker lock(JfrStream_lock);
   static bool vm_error = false;
   if (msgs & MSGBIT(MSG_VM_ERROR)) {
     vm_error = true;
@@ -329,45 +460,19 @@
   }
 }
 
-void JfrRecorderService::prepare_for_vm_error_rotation() {
-  if (!_chunkwriter.is_valid()) {
-    open_new_chunk(true);
-  }
-  _checkpoint_manager.register_service_thread(Thread::current());
-  JfrMetadataEvent::lock();
-}
-
-void JfrRecorderService::open_new_chunk(bool vm_error) {
-  assert(!_chunkwriter.is_valid(), "invariant");
-  assert(!JfrStream_lock->owned_by_self(), "invariant");
-  JfrChunkRotation::on_rotation();
-  MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
-  if (!_repository.open_chunk(vm_error)) {
-    assert(!_chunkwriter.is_valid(), "invariant");
-    _storage.control().set_to_disk(false);
-    return;
-  }
-  assert(_chunkwriter.is_valid(), "invariant");
-  _storage.control().set_to_disk(true);
-}
-
 void JfrRecorderService::in_memory_rotation() {
-  assert(!_chunkwriter.is_valid(), "invariant");
+  assert(JfrStream_lock->owned_by_self(), "invariant");
   // currently running an in-memory recording
+  assert(!_storage.control().to_disk(), "invariant");
   open_new_chunk();
   if (_chunkwriter.is_valid()) {
     // dump all in-memory buffer data to the newly created chunk
-    serialize_storage_from_in_memory_recording();
+    write_storage(_storage, _chunkwriter);
   }
 }
 
-void JfrRecorderService::serialize_storage_from_in_memory_recording() {
-  assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!");
-  MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
-  _storage.write();
-}
-
 void JfrRecorderService::chunk_rotation() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
   finalize_current_chunk();
   open_new_chunk();
 }
@@ -375,7 +480,6 @@
 void JfrRecorderService::finalize_current_chunk() {
   assert(_chunkwriter.is_valid(), "invariant");
   write();
-  assert(!_chunkwriter.is_valid(), "invariant");
 }
 
 void JfrRecorderService::write() {
@@ -386,54 +490,20 @@
   post_safepoint_write();
 }
 
-typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write> WriteStringPool;
-typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write_at_safepoint> WriteStringPoolSafepoint;
-typedef WriteCheckpointEvent<WriteStackTraceRepository> WriteStackTraceCheckpoint;
-typedef WriteCheckpointEvent<WriteStringPool> WriteStringPoolCheckpoint;
-typedef WriteCheckpointEvent<WriteStringPoolSafepoint> WriteStringPoolCheckpointSafepoint;
-
-static void write_stacktrace_checkpoint(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) {
-  WriteStackTraceRepository write_stacktrace_repo(stack_trace_repo, chunkwriter, clear);
-  WriteStackTraceCheckpoint write_stack_trace_checkpoint(chunkwriter, TYPE_STACKTRACE, write_stacktrace_repo);
-  write_stack_trace_checkpoint.process();
-}
-static void write_stringpool_checkpoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
-  WriteStringPool write_string_pool(string_pool);
-  WriteStringPoolCheckpoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool);
-  write_string_pool_checkpoint.process();
-}
-
-static void write_stringpool_checkpoint_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) {
-  WriteStringPoolSafepoint write_string_pool(string_pool);
-  WriteStringPoolCheckpointSafepoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool);
-  write_string_pool_checkpoint.process();
-}
-
-//
-// pre-safepoint write sequence
-//
-//  lock stream lock ->
-//    write non-safepoint dependent types ->
-//      write checkpoint epoch transition list->
-//        write stack trace checkpoint ->
-//          write string pool checkpoint ->
-//            write object sample stacktraces ->
-//              write storage ->
-//                release stream lock
-//
 void JfrRecorderService::pre_safepoint_write() {
-  MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
   assert(_chunkwriter.is_valid(), "invariant");
-  _checkpoint_manager.write_types();
-  _checkpoint_manager.write_epoch_transition_mspace();
-  write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, false);
-  write_stringpool_checkpoint(_string_pool, _chunkwriter);
   if (LeakProfiler::is_running()) {
     // Exclusive access to the object sampler instance.
     // The sampler is released (unlocked) later in post_safepoint_write.
     ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire(), _stack_trace_repository);
   }
-  _storage.write();
+  if (_string_pool.is_modified()) {
+    write_stringpool(_string_pool, _chunkwriter);
+  }
+  write_storage(_storage, _chunkwriter);
+  if (_stack_trace_repository.is_modified()) {
+    write_stacktrace(_stack_trace_repository, _chunkwriter, false);
+  }
 }
 
 void JfrRecorderService::invoke_safepoint_write() {
@@ -441,50 +511,18 @@
   VMThread::execute(&safepoint_task);
 }
 
-//
-// safepoint write sequence
-//
-//   lock stream lock ->
-//       write stacktrace repository ->
-//         write string pool ->
-//           write safepoint dependent types ->
-//             write storage ->
-//                 shift_epoch ->
-//                   update time ->
-//                     lock metadata descriptor ->
-//                       release stream lock
-//
 void JfrRecorderService::safepoint_write() {
   assert(SafepointSynchronize::is_at_safepoint(), "invariant");
-  MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
-  write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, true);
-  write_stringpool_checkpoint_safepoint(_string_pool, _chunkwriter);
-  _checkpoint_manager.write_safepoint_types();
+  if (_string_pool.is_modified()) {
+    write_stringpool_safepoint(_string_pool, _chunkwriter);
+  }
+  _checkpoint_manager.on_rotation();
   _storage.write_at_safepoint();
   _checkpoint_manager.shift_epoch();
-  _chunkwriter.time_stamp_chunk_now();
-  JfrMetadataEvent::lock();
-}
-
-static int64_t write_metadata_event(JfrChunkWriter& chunkwriter) {
-  assert(chunkwriter.is_valid(), "invariant");
-  const int64_t metadata_offset = chunkwriter.current_offset();
-  JfrMetadataEvent::write(chunkwriter, metadata_offset);
-  return metadata_offset;
+  _chunkwriter.set_time_stamp();
+  write_stacktrace(_stack_trace_repository, _chunkwriter, true);
 }
 
-//
-// post-safepoint write sequence
-//
-//   write type set ->
-//     release object sampler ->
-//       lock stream lock ->
-//         write checkpoints ->
-//           write metadata event ->
-//             write chunk header ->
-//               close chunk fd ->
-//                 release stream lock
-//
 void JfrRecorderService::post_safepoint_write() {
   assert(_chunkwriter.is_valid(), "invariant");
   // During the safepoint tasks just completed, the system transitioned to a new epoch.
@@ -497,38 +535,84 @@
     // Note: There is a dependency on write_type_set() above, ensure the release is subsequent.
     ObjectSampler::release();
   }
-  MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
-  // serialize any outstanding checkpoint memory
-  _checkpoint_manager.write();
   // serialize the metadata descriptor event and close out the chunk
-  _repository.close_chunk(write_metadata_event(_chunkwriter));
-  assert(!_chunkwriter.is_valid(), "invariant");
+  write_metadata(_chunkwriter);
+  _repository.close_chunk();
+}
+
+static JfrBuffer* thread_local_buffer(Thread* t) {
+  assert(t != NULL, "invariant");
+  return t->jfr_thread_local()->native_buffer();
 }
 
-void JfrRecorderService::vm_error_rotation() {
-  if (_chunkwriter.is_valid()) {
-    finalize_current_chunk_on_vm_error();
-    assert(!_chunkwriter.is_valid(), "invariant");
-    _repository.on_vm_error();
+static void reset_buffer(JfrBuffer* buffer, Thread* t) {
+  assert(buffer != NULL, "invariant");
+  assert(t != NULL, "invariant");
+  assert(buffer == thread_local_buffer(t), "invariant");
+  buffer->set_pos(const_cast<u1*>(buffer->top()));
+}
+
+static void reset_thread_local_buffer(Thread* t) {
+  reset_buffer(thread_local_buffer(t), t);
+}
+
+static void write_thread_local_buffer(JfrChunkWriter& chunkwriter, Thread* t) {
+  JfrBuffer * const buffer = thread_local_buffer(t);
+  assert(buffer != NULL, "invariant");
+  if (!buffer->empty()) {
+    chunkwriter.write_unbuffered(buffer->top(), buffer->pos() - buffer->top());
+    reset_buffer(buffer, t);
   }
 }
 
-void JfrRecorderService::finalize_current_chunk_on_vm_error() {
+size_t JfrRecorderService::flush() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
+  size_t total_elements = flush_metadata(_chunkwriter);
+  const size_t storage_elements = flush_storage(_storage, _chunkwriter);
+  if (0 == storage_elements) {
+    return total_elements;
+  }
+  total_elements += storage_elements;
+  if (_string_pool.is_modified()) {
+    total_elements += flush_stringpool(_string_pool, _chunkwriter);
+  }
+  if (_stack_trace_repository.is_modified()) {
+    total_elements += flush_stacktrace(_stack_trace_repository, _chunkwriter);
+  }
+  if (_checkpoint_manager.is_type_set_required()) {
+    total_elements += flush_typeset(_checkpoint_manager, _chunkwriter);
+  } else if (_checkpoint_manager.is_static_type_set_required()) {
+    // don't tally this, it is only in order to flush the waiting constants
+    _checkpoint_manager.flush_static_type_set();
+  }
+  return total_elements;
+}
+
+typedef Content<EventFlush, JfrRecorderService, &JfrRecorderService::flush> FlushFunctor;
+typedef WriteContent<FlushFunctor> Flush;
+
+void JfrRecorderService::invoke_flush() {
+  assert(JfrStream_lock->owned_by_self(), "invariant");
   assert(_chunkwriter.is_valid(), "invariant");
-  pre_safepoint_write();
-  // Do not attempt safepoint dependent operations during emergency dump.
-  // Optimistically write tagged artifacts.
-  _checkpoint_manager.shift_epoch();
-  // update time
-  _chunkwriter.time_stamp_chunk_now();
-  post_safepoint_write();
-  assert(!_chunkwriter.is_valid(), "invariant");
+  Thread* const t = Thread::current();
+  ResourceMark rm(t);
+  HandleMark hm(t);
+  ++flushpoint_id;
+  reset_thread_local_buffer(t);
+  FlushFunctor flushpoint(*this);
+  Flush fl(_chunkwriter, flushpoint);
+  invoke_with_flush_event(fl);
+  write_thread_local_buffer(_chunkwriter, t);
+  _repository.flush_chunk();
+}
+
+void JfrRecorderService::flushpoint() {
+  MutexLocker lock(JfrStream_lock);
+  invoke_flush();
 }
 
 void JfrRecorderService::process_full_buffers() {
   if (_chunkwriter.is_valid()) {
-    assert(!JfrStream_lock->owned_by_self(), "invariant");
-    MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag);
     _storage.write_full();
   }
 }
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -46,11 +46,10 @@
   void open_new_chunk(bool vm_error = false);
   void chunk_rotation();
   void in_memory_rotation();
-  void serialize_storage_from_in_memory_recording();
   void finalize_current_chunk();
-  void finalize_current_chunk_on_vm_error();
   void prepare_for_vm_error_rotation();
   void vm_error_rotation();
+  void invoke_flush();
 
   void clear();
   void pre_safepoint_clear();
@@ -67,7 +66,9 @@
  public:
   JfrRecorderService();
   void start();
+  size_t flush();
   void rotate(int msgs);
+  void flushpoint();
   void process_full_buffers();
   void scavenge();
   void evaluate_chunk_size_for_rotation();
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThread.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -27,6 +27,7 @@
 #include "classfile/javaClasses.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
+#include "jfr/jfr.hpp"
 #include "jfr/jni/jfrJavaSupport.hpp"
 #include "jfr/recorder/jfrRecorder.hpp"
 #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
@@ -64,7 +65,6 @@
   if (allocation_failed) {
     JfrJavaSupport::throw_out_of_memory_error("Unable to create native recording thread for JFR", CHECK_NULL);
   }
-
   Thread::start(new_thread);
   return new_thread;
 }
@@ -98,8 +98,9 @@
   instanceHandle h_thread_oop(THREAD, (instanceOop)result.get_jobject());
   assert(h_thread_oop.not_null(), "invariant");
   // attempt thread start
-  const Thread* const t = start_thread(h_thread_oop, recorderthread_entry,THREAD);
+  Thread* const t = start_thread(h_thread_oop, recorderthread_entry,THREAD);
   if (!HAS_PENDING_EXCEPTION) {
+    Jfr::exclude_thread(t);
     cp_manager->register_service_thread(t);
     return true;
   }
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -40,6 +40,7 @@
   #define START (msgs & (MSGBIT(MSG_START)))
   #define SHUTDOWN (msgs & MSGBIT(MSG_SHUTDOWN))
   #define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)))
+  #define FLUSHPOINT (msgs & (MSGBIT(MSG_FLUSHPOINT)))
   #define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER)))
   #define SCAVENGE (msgs & (MSGBIT(MSG_DEADBUFFER)))
 
@@ -72,6 +73,8 @@
         service.start();
       } else if (ROTATE) {
         service.rotate(msgs);
+      } else if (FLUSHPOINT) {
+        service.flushpoint();
       }
       JfrMsg_lock->lock();
       post_box.notify_waiters();
@@ -90,6 +93,7 @@
   #undef START
   #undef SHUTDOWN
   #undef ROTATE
+  #undef FLUSHPOINT
   #undef PROCESS_FULL_BUFFERS
   #undef SCAVENGE
 }
--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -62,7 +62,7 @@
 };
 
 bool JfrStackTraceRepository::initialize() {
-  return JfrSerializer::register_serializer(TYPE_FRAMETYPE, false, true, new JfrFrameType());
+  return JfrSerializer::register_serializer(TYPE_FRAMETYPE, true, new JfrFrameType());
 }
 
 void JfrStackTraceRepository::destroy() {
@@ -71,7 +71,16 @@
   _instance = NULL;
 }
 
-size_t JfrStackTraceRepository::write_impl(JfrChunkWriter& sw, bool clear) {
+static traceid last_id = 0;
+
+bool JfrStackTraceRepository::is_modified() const {
+  return last_id != _next_id;
+}
+
+size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) {
+  if (_entries == 0) {
+    return 0;
+  }
   MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
   assert(_entries > 0, "invariant");
   int count = 0;
@@ -93,29 +102,10 @@
     memset(_table, 0, sizeof(_table));
     _entries = 0;
   }
+  last_id = _next_id;
   return count;
 }
 
-size_t JfrStackTraceRepository::write(JfrChunkWriter& sw, bool clear) {
-  return _entries > 0 ? write_impl(sw, clear) : 0;
-}
-
-traceid JfrStackTraceRepository::write(JfrCheckpointWriter& writer, traceid id, unsigned int hash) {
-  assert(JfrStacktrace_lock->owned_by_self(), "invariant");
-  const JfrStackTrace* const trace = lookup(hash, id);
-  assert(trace != NULL, "invariant");
-  assert(trace->hash() == hash, "invariant");
-  assert(trace->id() == id, "invariant");
-  trace->write(writer);
-  return id;
-}
-
-void JfrStackTraceRepository::write_metadata(JfrCheckpointWriter& writer) {
-  JfrFrameType fct;
-  writer.write_type(TYPE_FRAMETYPE);
-  fct.serialize(writer);
-}
-
 size_t JfrStackTraceRepository::clear() {
   MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag);
   if (_entries == 0) {
@@ -142,7 +132,7 @@
   if (tl->has_cached_stack_trace()) {
     return tl->cached_stack_trace_id();
   }
-  if (!thread->is_Java_thread() || thread->is_hidden_from_external_view()) {
+  if (!thread->is_Java_thread() || thread->is_hidden_from_external_view() || tl->is_excluded()) {
     return 0;
   }
   JfrStackFrame* frames = tl->stackframes();
--- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -40,7 +40,7 @@
   friend class ObjectSampleCheckpoint;
   friend class ObjectSampler;
   friend class StackTraceBlobInstaller;
-  friend class WriteStackTraceRepository;
+  friend class StackTraceRepository;
 
  private:
   static const u4 TABLE_SIZE = 2053;
@@ -51,19 +51,18 @@
   JfrStackTraceRepository();
   static JfrStackTraceRepository& instance();
   static JfrStackTraceRepository* create();
+  static void destroy();
   bool initialize();
-  static void destroy();
 
-  size_t write_impl(JfrChunkWriter& cw, bool clear);
-  static void write_metadata(JfrCheckpointWriter& cpw);
-  traceid write(JfrCheckpointWriter& cpw, traceid id, unsigned int hash);
+  bool is_modified() const;
   size_t write(JfrChunkWriter& cw, bool clear);
   size_t clear();
 
+  const JfrStackTrace* lookup(unsigned int hash, traceid id) const;
+
   traceid add_trace(const JfrStackTrace& stacktrace);
   static traceid add(const JfrStackTrace& stacktrace);
   traceid record_for(JavaThread* thread, int skip, JfrStackFrame* frames, u4 max_frames);
-  const JfrStackTrace* lookup(unsigned int hash, traceid id) const;
 
  public:
   static traceid record(Thread* thread, int skip = 0);
--- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -54,10 +54,18 @@
   return true;
 }
 
-void JfrBuffer::reinitialize() {
+void JfrBuffer::reinitialize(bool exclusion /* false */) {
   assert(!lease(), "invariant");
   assert(!transient(), "invariant");
   set_pos(start());
+  if (exclusion != excluded()) {
+    // update
+    if (exclusion) {
+      set_excluded();
+    } else {
+      clear_excluded();
+    }
+  }
   clear_retired();
   set_top(start());
 }
@@ -80,7 +88,7 @@
 const u1* JfrBuffer::stable_top() const {
   const u1* current_top;
   do {
-    current_top = OrderAccess::load_acquire(&_top);
+    current_top = Atomic::load(&_top);
   } while (MUTEX_CLAIM == current_top);
   return current_top;
 }
@@ -107,7 +115,8 @@
   assert(new_top <= end(), "invariant");
   assert(new_top >= start(), "invariant");
   assert(top() == MUTEX_CLAIM, "invariant");
-  OrderAccess::release_store(&_top, new_top);
+  OrderAccess::storestore();
+  _top = new_top;
 }
 
 size_t JfrBuffer::unflushed_size() const {
@@ -118,18 +127,19 @@
   assert(id != NULL, "invariant");
   const void* current_id;
   do {
-    current_id = OrderAccess::load_acquire(&_identity);
+    current_id = Atomic::load(&_identity);
   } while (current_id != NULL || Atomic::cmpxchg(id, &_identity, current_id) != current_id);
 }
 
 bool JfrBuffer::try_acquire(const void* id) {
   assert(id != NULL, "invariant");
-  const void* const current_id = OrderAccess::load_acquire(&_identity);
+  const void* const current_id = Atomic::load(&_identity);
   return current_id == NULL && Atomic::cmpxchg(id, &_identity, current_id) == current_id;
 }
 
 void JfrBuffer::release() {
-  OrderAccess::release_store(&_identity, (const void*)NULL);
+  OrderAccess::storestore();
+  _identity = NULL;
 }
 
 bool JfrBuffer::acquired_by(const void* id) const {
@@ -186,7 +196,8 @@
 enum FLAG {
   RETIRED = 1,
   TRANSIENT = 2,
-  LEASE = 4
+  LEASE = 4,
+  EXCLUDED = 8
 };
 
 bool JfrBuffer::transient() const {
@@ -221,27 +232,35 @@
   assert(!lease(), "invariant");
 }
 
-static u2 load_acquire_flags(const u2* const flags) {
-  return OrderAccess::load_acquire(flags);
+bool JfrBuffer::excluded() const {
+  return (u1)EXCLUDED == (_flags & (u1)EXCLUDED);
 }
 
-static void release_store_flags(u2* const flags, u2 new_flags) {
-  OrderAccess::release_store(flags, new_flags);
+void JfrBuffer::set_excluded() {
+  _flags |= (u1)EXCLUDED;
+  assert(excluded(), "invariant");
+}
+
+void JfrBuffer::clear_excluded() {
+  if (excluded()) {
+    OrderAccess::storestore();
+    _flags ^= (u1)EXCLUDED;
+  }
+  assert(!excluded(), "invariant");
 }
 
 bool JfrBuffer::retired() const {
-  return (u1)RETIRED == (load_acquire_flags(&_flags) & (u1)RETIRED);
+  return (_flags & (u1)RETIRED) == (u1)RETIRED;
 }
 
 void JfrBuffer::set_retired() {
-  const u2 new_flags = load_acquire_flags(&_flags) | (u1)RETIRED;
-  release_store_flags(&_flags, new_flags);
+  OrderAccess::storestore();
+  _flags |= (u1)RETIRED;
 }
 
 void JfrBuffer::clear_retired() {
-  u2 new_flags = load_acquire_flags(&_flags);
-  if ((u1)RETIRED == (new_flags & (u1)RETIRED)) {
-    new_flags ^= (u1)RETIRED;
-    release_store_flags(&_flags, new_flags);
+  if (retired()) {
+    OrderAccess::storestore();
+    _flags ^= (u1)RETIRED;
   }
 }
--- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -61,7 +61,7 @@
  public:
   JfrBuffer();
   bool initialize(size_t header_size, size_t size, const void* id = NULL);
-  void reinitialize();
+  void reinitialize(bool exclusion = false);
   void concurrent_reinitialization();
   size_t discard();
   JfrBuffer* next() const {
@@ -165,12 +165,15 @@
   bool retired() const;
   void set_retired();
   void clear_retired();
+
+  bool excluded() const;
+  void set_excluded();
+  void clear_excluded();
 };
 
 class JfrAgeNode : public JfrBuffer {
  private:
   JfrBuffer* _retired;
-
  public:
   JfrAgeNode() : _retired(NULL) {}
   void set_retired_buffer(JfrBuffer* retired) {
--- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -99,8 +99,8 @@
   template <typename IteratorCallback, typename IteratorType>
   void iterate(IteratorCallback& callback, bool full = true, jfr_iter_direction direction = forward);
 
-  debug_only(bool in_full_list(const Type* t) const { return _full.in_list(t); })
-  debug_only(bool in_free_list(const Type* t) const { return _free.in_list(t); })
+  bool in_full_list(const Type* t) const { return _full.in_list(t); }
+  bool in_free_list(const Type* t) const { return _free.in_list(t); }
 };
 
 #endif // SHARE_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_HPP
--- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -141,6 +141,7 @@
   }
   assert(t->empty(), "invariant");
   assert(!t->retired(), "invariant");
+  assert(!t->excluded(), "invariant");
   assert(t->identity() == NULL, "invariant");
   if (!should_populate_cache()) {
     remove_free(t);
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -26,6 +26,7 @@
 #include "jfr/jfrEvents.hpp"
 #include "jfr/jni/jfrJavaSupport.hpp"
 #include "jfr/recorder/jfrRecorder.hpp"
+#include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp"
 #include "jfr/recorder/repository/jfrChunkWriter.hpp"
 #include "jfr/recorder/service/jfrOptionSet.hpp"
 #include "jfr/recorder/service/jfrPostBox.hpp"
@@ -253,6 +254,18 @@
     assert(buffer->empty(), "invariant");
     return true;
   }
+
+  if (buffer->excluded()) {
+    const bool thread_is_excluded = thread->jfr_thread_local()->is_excluded();
+    buffer->reinitialize(thread_is_excluded);
+    assert(buffer->empty(), "invariant");
+    if (!thread_is_excluded) {
+      // state change from exclusion to inclusion requires a thread checkpoint
+      JfrCheckpointManager::write_thread_checkpoint(thread);
+    }
+    return true;
+  }
+
   BufferPtr const promotion_buffer = get_promotion_buffer(unflushed_size, _global_mspace, *this, promotion_retry, thread);
   if (promotion_buffer == NULL) {
     write_data_loss(buffer, thread);
@@ -301,7 +314,7 @@
   assert(buffer != NULL, "invariant");
   assert(buffer->retired(), "invariant");
   const size_t unflushed_size = buffer->unflushed_size();
-  buffer->reinitialize();
+  buffer->concurrent_reinitialization();
   log_registration_failure(unflushed_size);
 }
 
@@ -470,6 +483,7 @@
   assert(t != NULL, "invariant");
   assert(cur != NULL, "invariant");
   assert(cur->lease(), "invariant");
+  assert(!cur->excluded(), "invariant");
   assert(cur_pos != NULL, "invariant");
   assert(native ? t->jfr_thread_local()->native_buffer() == cur : t->jfr_thread_local()->java_buffer() == cur, "invariant");
   assert(t->jfr_thread_local()->shelved_buffer() != NULL, "invariant");
@@ -496,6 +510,9 @@
   // the case for stable thread local buffers; it is not the case for large buffers.
   if (!cur->empty()) {
     flush_regular_buffer(cur, t);
+    if (cur->excluded()) {
+      return cur;
+    }
   }
   assert(t->jfr_thread_local()->shelved_buffer() == NULL, "invariant");
   if (cur->free_size() >= req) {
@@ -584,28 +601,40 @@
 typedef UnBufferedWriteToChunk<JfrBuffer> WriteOperation;
 typedef MutexedWriteOp<WriteOperation> MutexedWriteOperation;
 typedef ConcurrentWriteOp<WriteOperation> ConcurrentWriteOperation;
-typedef ConcurrentWriteOpExcludeRetired<WriteOperation> ThreadLocalConcurrentWriteOperation;
+
+typedef Retired<JfrBuffer, true> NonRetired;
+typedef Excluded<JfrBuffer, true> NonExcluded;
+typedef CompositeOperation<NonRetired, NonExcluded> BufferPredicate;
+typedef PredicatedMutexedWriteOp<WriteOperation, BufferPredicate> ThreadLocalMutexedWriteOperation;
+typedef PredicatedConcurrentWriteOp<WriteOperation, BufferPredicate> ThreadLocalConcurrentWriteOperation;
 
 size_t JfrStorage::write() {
-  const size_t full_size_processed = write_full();
+  const size_t full_elements = write_full();
   WriteOperation wo(_chunkwriter);
-  ThreadLocalConcurrentWriteOperation tlwo(wo);
+  NonRetired nr;
+  NonExcluded ne;
+  BufferPredicate bp(&nr, &ne);
+  ThreadLocalConcurrentWriteOperation tlwo(wo, bp);
   process_full_list(tlwo, _thread_local_mspace);
   ConcurrentWriteOperation cwo(wo);
   process_free_list(cwo, _global_mspace);
-  return full_size_processed + wo.processed();
+  return full_elements + wo.elements();
 }
 
 size_t JfrStorage::write_at_safepoint() {
   assert(SafepointSynchronize::is_at_safepoint(), "invariant");
   WriteOperation wo(_chunkwriter);
   MutexedWriteOperation writer(wo); // mutexed write mode
-  process_full_list(writer, _thread_local_mspace);
+  NonRetired nr;
+  NonExcluded ne;
+  BufferPredicate bp(&nr, &ne);
+  ThreadLocalMutexedWriteOperation tlmwo(wo, bp);
+  process_full_list(tlmwo, _thread_local_mspace);
   assert(_transient_mspace->is_free_empty(), "invariant");
   process_full_list(writer, _transient_mspace);
   assert(_global_mspace->is_full_empty(), "invariant");
   process_free_list(writer, _global_mspace);
-  return wo.processed();
+  return wo.elements();
 }
 
 typedef DiscardOp<DefaultDiscarder<JfrStorage::Buffer> > DiscardOperation;
@@ -613,14 +642,14 @@
 typedef CompositeOperation<MutexedWriteOperation, ReleaseOperation> FullOperation;
 
 size_t JfrStorage::clear() {
-  const size_t full_size_processed = clear_full();
+  const size_t full_elements = clear_full();
   DiscardOperation discarder(concurrent); // concurrent discard mode
   process_full_list(discarder, _thread_local_mspace);
   assert(_transient_mspace->is_free_empty(), "invariant");
   process_full_list(discarder, _transient_mspace);
   assert(_global_mspace->is_full_empty(), "invariant");
   process_free_list(discarder, _global_mspace);
-  return full_size_processed + discarder.processed();
+  return full_elements + discarder.elements();
 }
 
 static void insert_free_age_nodes(JfrStorageAgeMspace* age_mspace, JfrAgeNode* head, JfrAgeNode* tail, size_t count) {
@@ -711,15 +740,25 @@
   ReleaseOperation ro(_transient_mspace, thread);
   FullOperation cmd(&writer, &ro);
   const size_t count = process_full(cmd, control(), _age_mspace);
-  log(count, writer.processed());
-  return writer.processed();
+  if (0 == count) {
+    assert(0 == writer.elements(), "invariant");
+    return 0;
+  }
+  const size_t size = writer.size();
+  log(count, size);
+  return count;
 }
 
 size_t JfrStorage::clear_full() {
   DiscardOperation discarder(mutexed); // a retired buffer implies mutexed access
   const size_t count = process_full(discarder, control(), _age_mspace);
-  log(count, discarder.processed(), true);
-  return discarder.processed();
+  if (0 == count) {
+    assert(0 == discarder.elements(), "invariant");
+    return 0;
+  }
+  const size_t size = discarder.size();
+  log(count, size, true);
+  return count;
 }
 
 static void scavenge_log(size_t count, size_t amount, size_t current) {
@@ -749,6 +788,10 @@
       assert(!t->lease(), "invariant");
       ++_count;
       _amount += t->total_size();
+      if (t->excluded()) {
+        t->clear_excluded();
+      }
+      assert(!t->excluded(), "invariant");
       t->clear_retired();
       t->release();
       _control.decrement_dead();
@@ -767,6 +810,11 @@
   }
   Scavenger<JfrThreadLocalMspace> scavenger(ctrl, _thread_local_mspace);
   process_full_list(scavenger, _thread_local_mspace);
-  scavenge_log(scavenger.processed(), scavenger.amount(), ctrl.dead_count());
-  return scavenger.processed();
+  const size_t count = scavenger.processed();
+  if (0 == count) {
+    assert(0 == scavenger.amount(), "invariant");
+    return 0;
+  }
+  scavenge_log(count, scavenger.amount(), ctrl.dead_count());
+  return count;
 }
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -68,7 +68,6 @@
 
   size_t clear();
   size_t clear_full();
-  size_t write();
   size_t write_full();
   size_t write_at_safepoint();
   size_t scavenge();
@@ -89,6 +88,8 @@
   void discard_oldest(Thread* t);
   static JfrStorageControl& control();
 
+  size_t write();
+
   friend class JfrRecorder;
   friend class JfrRecorderService;
   template <typename, template <typename> class, typename>
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorageControl.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageControl.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -26,14 +26,13 @@
 #include "jfr/recorder/storage/jfrStorageControl.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/mutexLocker.hpp"
-#include "runtime/orderAccess.hpp"
 
 // returns the updated value
 static jlong atomic_add(size_t value, size_t volatile* const dest) {
   size_t compare_value;
   size_t exchange_value;
   do {
-    compare_value = OrderAccess::load_acquire(dest);
+    compare_value = *dest;
     exchange_value = compare_value + value;
   } while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
   return exchange_value;
@@ -43,7 +42,7 @@
   size_t compare_value;
   size_t exchange_value;
   do {
-    compare_value = OrderAccess::load_acquire(dest);
+    compare_value = *dest;
     assert(compare_value >= 1, "invariant");
     exchange_value = compare_value - 1;
   } while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
@@ -102,7 +101,7 @@
 // concurrent with accuracy requirement
 
 size_t JfrStorageControl::global_lease_count() const {
-  return OrderAccess::load_acquire(&_global_lease_count);
+  return Atomic::load(&_global_lease_count);
 }
 
 size_t JfrStorageControl::increment_leased() {
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -31,7 +31,21 @@
 #include "jfr/utilities/jfrTypes.hpp"
 #include "runtime/thread.hpp"
 
-template <typename Operation, typename NextOperation>
+class CompositeOperationOr {
+ public:
+  static bool evaluate(bool value) {
+    return !value;
+  }
+};
+
+class CompositeOperationAnd {
+ public:
+  static bool evaluate(bool value) {
+    return value;
+  }
+};
+
+template <typename Operation, typename NextOperation, typename TruthFunction = CompositeOperationAnd>
 class CompositeOperation {
  private:
   Operation* _op;
@@ -41,11 +55,15 @@
     assert(_op != NULL, "invariant");
   }
   typedef typename Operation::Type Type;
-  bool process(Type* t = NULL) {
-    return _next == NULL ? _op->process(t) : _op->process(t) && _next->process(t);
+  bool process(Type* t) {
+    const bool op_result = _op->process(t);
+    return _next == NULL ? op_result : TruthFunction::evaluate(op_result) ? _next->process(t) : op_result;
   }
-  size_t processed() const {
-    return _next == NULL ? _op->processed() : _op->processed() + _next->processed();
+  size_t elements() const {
+    return _next == NULL ? _op->elements() : _op->elements() + _next->elements();
+  }
+  size_t size() const {
+    return _next == NULL ? _op->size() : _op->size() + _next->size();
   }
 };
 
@@ -53,23 +71,71 @@
 class UnBufferedWriteToChunk {
  private:
   JfrChunkWriter& _writer;
-  size_t _processed;
+  size_t _elements;
+  size_t _size;
  public:
   typedef T Type;
-  UnBufferedWriteToChunk(JfrChunkWriter& writer) : _writer(writer), _processed(0) {}
+  UnBufferedWriteToChunk(JfrChunkWriter& writer) : _writer(writer), _elements(0), _size(0) {}
   bool write(Type* t, const u1* data, size_t size);
-  size_t processed() { return _processed; }
+  size_t elements() const { return _elements; }
+  size_t size() const { return _size; }
 };
 
 template <typename T>
 class DefaultDiscarder {
  private:
-  size_t _processed;
+  size_t _elements;
+  size_t _size;
+ public:
+  typedef T Type;
+  DefaultDiscarder() : _elements(0), _size(0) {}
+  bool discard(Type* t, const u1* data, size_t size);
+  size_t elements() const { return _elements; }
+  size_t size() const { return _size; }
+};
+
+template <typename T, bool negation>
+class Retired {
+ public:
+  typedef T Type;
+  bool process(Type* t) {
+    assert(t != NULL, "invariant");
+    return negation ? !t->retired() : t->retired();
+  }
+};
+
+template <typename T, bool negation>
+class Excluded {
  public:
   typedef T Type;
-  DefaultDiscarder() : _processed() {}
-  bool discard(Type* t, const u1* data, size_t size);
-  size_t processed() const { return _processed; }
+  bool process(Type* t) {
+    assert(t != NULL, "invariant");
+    return negation ? !t->excluded() : t->excluded();
+  }
+};
+
+template <typename Operation>
+class MutexedWriteOp {
+ private:
+  Operation& _operation;
+ public:
+  typedef typename Operation::Type Type;
+  MutexedWriteOp(Operation& operation) : _operation(operation) {}
+  bool process(Type* t);
+  size_t elements() const { return _operation.elements(); }
+  size_t size() const { return _operation.size(); }
+};
+
+template <typename Operation, typename Predicate>
+class PredicatedMutexedWriteOp : public MutexedWriteOp<Operation> {
+ private:
+  Predicate& _predicate;
+ public:
+  PredicatedMutexedWriteOp(Operation& operation, Predicate& predicate) :
+    MutexedWriteOp<Operation>(operation), _predicate(predicate) {}
+  bool process(typename Operation::Type* t) {
+    return _predicate.process(t) ? MutexedWriteOp<Operation>::process(t) : true;
+  }
 };
 
 template <typename Operation>
@@ -80,27 +146,20 @@
   typedef typename Operation::Type Type;
   ConcurrentWriteOp(Operation& operation) : _operation(operation) {}
   bool process(Type* t);
-  size_t processed() const { return _operation.processed(); }
+  size_t elements() const { return _operation.elements(); }
+  size_t size() const { return _operation.size(); }
 };
 
-template <typename Operation>
-class ConcurrentWriteOpExcludeRetired : private ConcurrentWriteOp<Operation> {
+template <typename Operation, typename Predicate>
+class PredicatedConcurrentWriteOp : public ConcurrentWriteOp<Operation> {
+ private:
+  Predicate& _predicate;
  public:
-  typedef typename Operation::Type Type;
-  ConcurrentWriteOpExcludeRetired(Operation& operation) : ConcurrentWriteOp<Operation>(operation) {}
-  bool process(Type* t);
-  size_t processed() const { return ConcurrentWriteOp<Operation>::processed(); }
-};
-
-template <typename Operation>
-class MutexedWriteOp {
- private:
-  Operation& _operation;
- public:
-  typedef typename Operation::Type Type;
-  MutexedWriteOp(Operation& operation) : _operation(operation) {}
-  bool process(Type* t);
-  size_t processed() const { return _operation.processed(); }
+  PredicatedConcurrentWriteOp(Operation& operation, Predicate& predicate) :
+    ConcurrentWriteOp<Operation>(operation), _predicate(predicate) {}
+  bool process(typename Operation::Type* t) {
+    return _predicate.process(t) ? ConcurrentWriteOp<Operation>::process(t) : true;
+  }
 };
 
 template <typename Operation>
@@ -126,7 +185,8 @@
   typedef typename Operation::Type Type;
   DiscardOp(jfr_operation_mode mode = concurrent) : _operation(), _mode(mode) {}
   bool process(Type* t);
-  size_t processed() const { return _operation.processed(); }
+  size_t elements() const { return _operation.elements(); }
+  size_t size() const { return _operation.size(); }
 };
 
 #endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_HPP
--- a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -31,13 +31,15 @@
 template <typename T>
 inline bool UnBufferedWriteToChunk<T>::write(T* t, const u1* data, size_t size) {
   _writer.write_unbuffered(data, size);
-  _processed += size;
+  ++_elements;
+  _size += size;
   return true;
 }
 
 template <typename T>
 inline bool DefaultDiscarder<T>::discard(T* t, const u1* data, size_t size) {
-  _processed += size;
+  ++_elements;
+  _size += size;
   return true;
 }
 
@@ -55,15 +57,6 @@
 }
 
 template <typename Operation>
-inline bool ConcurrentWriteOpExcludeRetired<Operation>::process(typename Operation::Type* t) {
-  if (t->retired()) {
-    assert(t->empty(), "invariant");
-    return true;
-  }
-  return ConcurrentWriteOp<Operation>::process(t);
-}
-
-template <typename Operation>
 inline bool MutexedWriteOp<Operation>::process(typename Operation::Type* t) {
   assert(t != NULL, "invariant");
   const u1* const current_top = t->top();
--- a/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -26,7 +26,6 @@
 #include "jfr/recorder/storage/jfrVirtualMemory.hpp"
 #include "memory/virtualspace.hpp"
 #include "runtime/globals.hpp"
-#include "runtime/orderAccess.hpp"
 #include "runtime/os.hpp"
 #include "services/memTracker.hpp"
 #include "utilities/globalDefinitions.hpp"
--- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -33,7 +33,6 @@
 #include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp"
 #include "jfr/utilities/jfrTypes.hpp"
 #include "logging/log.hpp"
-#include "runtime/atomic.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/orderAccess.hpp"
 #include "runtime/safepoint.hpp"
@@ -42,12 +41,42 @@
 typedef JfrStringPool::Buffer* BufferPtr;
 
 static JfrStringPool* _instance = NULL;
+static uint64_t store_generation = 0;
+static uint64_t serialized_generation = 0;
+
+inline void set_generation(uint64_t value, uint64_t* const dest) {
+  assert(dest != NULL, "invariant");
+  OrderAccess::release_store(dest, value);
+}
+static void increment_store_generation() {
+  const uint64_t current_serialized = OrderAccess::load_acquire(&serialized_generation);
+  const uint64_t current_stored = OrderAccess::load_acquire(&store_generation);
+  if (current_serialized == current_stored) {
+    set_generation(current_serialized + 1, &store_generation);
+  }
+}
+
+static bool increment_serialized_generation() {
+  const uint64_t current_stored = OrderAccess::load_acquire(&store_generation);
+  const uint64_t current_serialized = OrderAccess::load_acquire(&serialized_generation);
+  if (current_stored != current_serialized) {
+    set_generation(current_stored, &serialized_generation);
+    return true;
+  }
+  return false;
+}
+
+bool JfrStringPool::is_modified() {
+  return increment_serialized_generation();
+}
 
 JfrStringPool& JfrStringPool::instance() {
   return *_instance;
 }
 
 JfrStringPool* JfrStringPool::create(JfrChunkWriter& cw) {
+  store_generation = 0;
+  serialized_generation = 0;
   assert(_instance == NULL, "invariant");
   _instance = new JfrStringPool(cw);
   return _instance;
@@ -131,12 +160,16 @@
 bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) {
   assert(jt != NULL, "invariant");
   const bool current_epoch = JfrTraceIdEpoch::epoch();
-  if (current_epoch == epoch) {
+  if (current_epoch != epoch) {
+    return current_epoch;
+  }
+  {
     JfrStringPoolWriter writer(jt);
     writer.write(id);
     writer.write(string);
     writer.inc_nof_strings();
   }
+  increment_store_generation();
   return current_epoch;
 }
 
@@ -163,9 +196,10 @@
   size_t processed() { return _strings_processed; }
 };
 
-template <typename Type>
+template <typename T>
 class StringPoolDiscarderStub {
  public:
+  typedef T Type;
   bool write(Type* buffer, const u1* data, size_t size) {
     // stub only, discard happens at higher level
     return true;
@@ -197,6 +231,7 @@
 }
 
 size_t JfrStringPool::clear() {
+  increment_serialized_generation();
   DiscardOperation discard_operation;
   ExclusiveDiscardOperation edo(discard_operation);
   StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false);
--- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -71,6 +71,7 @@
   static JfrStringPool* create(JfrChunkWriter& cw);
   bool initialize();
   static void destroy();
+  static bool is_modified();
 
   friend class JfrRecorder;
   friend class JfrRecorderService;
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -55,6 +55,7 @@
   _stack_trace_hash(0),
   _stackdepth(0),
   _entering_suspend_flag(0),
+  _excluded(false),
   _dead(false) {}
 
 u8 JfrThreadLocal::add_data_lost(u8 value) {
@@ -84,9 +85,13 @@
 void JfrThreadLocal::on_start(Thread* t) {
   assert(t != NULL, "invariant");
   assert(Thread::current() == t, "invariant");
+  JfrJavaSupport::on_thread_start(t);
   if (JfrRecorder::is_recording()) {
-    if (t->is_Java_thread()) {
-      send_java_thread_start_event((JavaThread*)t);
+    if (!t->jfr_thread_local()->is_excluded()) {
+      JfrCheckpointManager::write_thread_checkpoint(t);
+      if (t->is_Java_thread()) {
+        send_java_thread_start_event((JavaThread*)t);
+      }
     }
   }
 }
@@ -103,48 +108,68 @@
   }
 }
 
+void JfrThreadLocal::release(Thread* t) {
+  if (has_java_event_writer()) {
+    assert(t->is_Java_thread(), "invariant");
+    JfrJavaSupport::destroy_global_jni_handle(java_event_writer());
+    _java_event_writer = NULL;
+  }
+  if (has_native_buffer()) {
+    JfrStorage::release_thread_local(native_buffer(), t);
+    _native_buffer = NULL;
+  }
+  if (has_java_buffer()) {
+    JfrStorage::release_thread_local(java_buffer(), t);
+    _java_buffer = NULL;
+  }
+  if (_stackframes != NULL) {
+    FREE_C_HEAP_ARRAY(JfrStackFrame, _stackframes);
+    _stackframes = NULL;
+  }
+}
+
 void JfrThreadLocal::release(JfrThreadLocal* tl, Thread* t) {
   assert(tl != NULL, "invariant");
   assert(t != NULL, "invariant");
   assert(Thread::current() == t, "invariant");
   assert(!tl->is_dead(), "invariant");
   assert(tl->shelved_buffer() == NULL, "invariant");
-  if (tl->has_native_buffer()) {
-    JfrStorage::release_thread_local(tl->native_buffer(), t);
-  }
-  if (tl->has_java_buffer()) {
-    JfrStorage::release_thread_local(tl->java_buffer(), t);
-  }
-  if (tl->has_java_event_writer()) {
-    assert(t->is_Java_thread(), "invariant");
-    JfrJavaSupport::destroy_global_jni_handle(tl->java_event_writer());
-  }
-  FREE_C_HEAP_ARRAY(JfrStackFrame, tl->_stackframes);
   tl->_dead = true;
+  tl->release(t);
 }
 
 void JfrThreadLocal::on_exit(Thread* t) {
   assert(t != NULL, "invariant");
   JfrThreadLocal * const tl = t->jfr_thread_local();
   assert(!tl->is_dead(), "invariant");
-  if (t->is_Java_thread()) {
-    JavaThread* const jt = (JavaThread*)t;
-    ObjectSampleCheckpoint::on_thread_exit(jt);
-    send_java_thread_end_events(tl->thread_id(), jt);
+  if (JfrRecorder::is_recording()) {
+    if (t->is_Java_thread()) {
+      JavaThread* const jt = (JavaThread*)t;
+      ObjectSampleCheckpoint::on_thread_exit(jt);
+      send_java_thread_end_events(tl->thread_id(), jt);
+    }
   }
   release(tl, Thread::current()); // because it could be that Thread::current() != t
 }
 
+static JfrBuffer* acquire_buffer(bool excluded) {
+  JfrBuffer* const buffer = JfrStorage::acquire_thread_local(Thread::current());
+  if (buffer != NULL && excluded) {
+    buffer->set_excluded();
+  }
+  return buffer;
+}
+
 JfrBuffer* JfrThreadLocal::install_native_buffer() const {
   assert(!has_native_buffer(), "invariant");
-  _native_buffer = JfrStorage::acquire_thread_local(Thread::current());
+  _native_buffer = acquire_buffer(_excluded);
   return _native_buffer;
 }
 
 JfrBuffer* JfrThreadLocal::install_java_buffer() const {
   assert(!has_java_buffer(), "invariant");
   assert(!has_java_event_writer(), "invariant");
-  _java_buffer = JfrStorage::acquire_thread_local(Thread::current());
+  _java_buffer = acquire_buffer(_excluded);
   return _java_buffer;
 }
 
@@ -162,6 +187,18 @@
   return in_ByteSize(offset_of(JfrThreadLocal, _java_event_writer));
 }
 
+void JfrThreadLocal::exclude(Thread* t) {
+  assert(t != NULL, "invariant");
+  t->jfr_thread_local()->_excluded = true;
+  t->jfr_thread_local()->release(t);
+}
+
+void JfrThreadLocal::include(Thread* t) {
+  assert(t != NULL, "invariant");
+  t->jfr_thread_local()->_excluded = false;
+  t->jfr_thread_local()->release(t);
+}
+
 u4 JfrThreadLocal::stackdepth() const {
   return _stackdepth != 0 ? _stackdepth : (u4)JfrOptionSet::stackdepth();
 }
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -50,12 +50,13 @@
   unsigned int _stack_trace_hash;
   mutable u4 _stackdepth;
   volatile jint _entering_suspend_flag;
+  bool _excluded;
   bool _dead;
 
   JfrBuffer* install_native_buffer() const;
   JfrBuffer* install_java_buffer() const;
   JfrStackFrame* install_stackframes() const;
-
+  void release(Thread* t);
   static void release(JfrThreadLocal* tl, Thread* t);
 
  public:
@@ -203,6 +204,10 @@
     _trace_id = id;
   }
 
+  bool is_excluded() const {
+    return _excluded;
+  }
+
   bool is_dead() const {
     return _dead;
   }
@@ -211,6 +216,9 @@
   void set_thread_blob(const JfrBlobHandle& handle);
   const JfrBlobHandle& thread_blob() const;
 
+  static void exclude(Thread* t);
+  static void include(Thread* t);
+
   static void on_start(Thread* t);
   static void on_exit(Thread* t);
 
--- a/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -69,7 +69,7 @@
 
   jbyte* meta_addr() const {
 #ifdef VM_LITTLE_ENDIAN
-    return (jbyte*)(&_flags) + 1;
+    return ((jbyte*)&_flags) + 1;
 #else
     return (jbyte*)&_flags;
 #endif
--- a/src/hotspot/share/jfr/utilities/jfrAllocation.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrAllocation.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -28,7 +28,6 @@
 #include "logging/log.hpp"
 #include "memory/allocation.inline.hpp"
 #include "runtime/atomic.hpp"
-#include "runtime/orderAccess.hpp"
 #include "runtime/vm_version.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/macros.hpp"
@@ -40,7 +39,7 @@
   jlong compare_value;
   jlong exchange_value;
   do {
-    compare_value = OrderAccess::load_acquire(dest);
+    compare_value = *dest;
     exchange_value = compare_value + value;
   } while (Atomic::cmpxchg(exchange_value, dest, compare_value) != compare_value);
   return exchange_value;
--- a/src/hotspot/share/jfr/utilities/jfrDoublyLinkedList.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrDoublyLinkedList.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -48,8 +48,8 @@
   void prepend(T* const node);
   void append(T* const node);
   void append_list(T* const head_node, T* const tail_node, size_t count);
-  debug_only(bool in_list(const T* const target_node) const;)
-  debug_only(bool locate(const T* start_node, const T* const target_node) const;)
+  bool in_list(const T* const target_node) const;
+  bool locate(const T* start_node, const T* const target_node) const;
 };
 
 template <typename T>
@@ -153,7 +153,6 @@
   return node;
 }
 
-#ifdef ASSERT
 template <typename T>
 bool JfrDoublyLinkedList<T>::locate(const T* node, const T* const target) const {
   assert(target != NULL, "invariant");
@@ -182,7 +181,6 @@
   }
   assert(count_param == count, "invariant");
 }
-#endif // ASSERT
 
 template <typename T>
 void JfrDoublyLinkedList<T>::append_list(T* const head_node, T* const tail_node, size_t count) {
--- a/src/hotspot/share/jfr/utilities/jfrLogTagSets.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrLogTagSets.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -53,6 +53,7 @@
   JFR_LOG_TAG(jfr, system, bytecode) \
   JFR_LOG_TAG(jfr, system, parser) \
   JFR_LOG_TAG(jfr, system, metadata) \
+  JFR_LOG_TAG(jfr, system, streaming) \
   JFR_LOG_TAG(jfr, metadata) \
   JFR_LOG_TAG(jfr, event) \
   JFR_LOG_TAG(jfr, setting) \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrThreadIterator.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jfr/support/jfrThreadLocal.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
+#include "runtime/thread.inline.hpp"
+
+static bool thread_inclusion_predicate(Thread* t) {
+  assert(t != NULL, "invariant");
+  return !t->jfr_thread_local()->is_dead();
+}
+
+static bool java_thread_inclusion_predicate(JavaThread* jt) {
+  assert(jt != NULL, "invariant");
+  return thread_inclusion_predicate(jt) && jt->thread_state() != _thread_new;
+}
+
+static JavaThread* next_java_thread(JavaThreadIteratorWithHandle& iter) {
+  JavaThread* next = iter.next();
+  while (next != NULL && !java_thread_inclusion_predicate(next)) {
+    next = iter.next();
+  }
+  return next;
+}
+
+static NonJavaThread* next_non_java_thread(NonJavaThread::Iterator& iter) {
+  NonJavaThread* next = NULL;
+  while (!iter.end()) {
+    next = iter.current();
+    iter.step();
+    assert(next != NULL, "invariant");
+    if (!thread_inclusion_predicate(next)) {
+      continue;
+    }
+  }
+  return next;
+}
+
+JfrJavaThreadIteratorAdapter::JfrJavaThreadIteratorAdapter() : _iter(), _next(next_java_thread(_iter)) {}
+
+JavaThread* JfrJavaThreadIteratorAdapter::next() {
+  assert(has_next(), "invariant");
+  Type* const temp = _next;
+  _next = next_java_thread(_iter);
+  assert(temp != _next, "invariant");
+  return temp;
+}
+
+JfrNonJavaThreadIteratorAdapter::JfrNonJavaThreadIteratorAdapter() : _iter(), _next(next_non_java_thread(_iter)) {}
+
+bool JfrNonJavaThreadIteratorAdapter::has_next() const {
+  return _next != NULL;
+}
+
+NonJavaThread* JfrNonJavaThreadIteratorAdapter::next() {
+  assert(has_next(), "invariant");
+  Type* const temp = _next;
+  _next = next_non_java_thread(_iter);
+  assert(temp != _next, "invariant");
+  return temp;
+}
+
+// explicit instantiations
+template class JfrThreadIterator<JfrJavaThreadIteratorAdapter, StackObj>;
+template class JfrThreadIterator<JfrNonJavaThreadIteratorAdapter, StackObj>;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+#ifndef SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
+#define SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
+
+#include "memory/allocation.hpp"
+#include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
+
+template <typename Adapter, typename AP = StackObj>
+class JfrThreadIterator : public AP {
+ private:
+  Adapter _adapter;
+ public:
+  JfrThreadIterator() : _adapter() {}
+  typename Adapter::Type* next() {
+    assert(has_next(), "invariant");
+    return _adapter.next();
+  }
+  bool has_next() const {
+    return _adapter.has_next();
+  }
+};
+
+class JfrJavaThreadIteratorAdapter {
+ private:
+  JavaThreadIteratorWithHandle _iter;
+  JavaThread* _next;
+ public:
+  typedef JavaThread Type;
+  JfrJavaThreadIteratorAdapter();
+  bool has_next() const {
+    return _next != NULL;
+  }
+  Type* next();
+};
+
+class JfrNonJavaThreadIteratorAdapter {
+ private:
+  NonJavaThread::Iterator _iter;
+  NonJavaThread* _next;
+ public:
+  typedef NonJavaThread Type;
+  JfrNonJavaThreadIteratorAdapter();
+  bool has_next() const;
+  Type* next();
+};
+
+typedef JfrThreadIterator<JfrJavaThreadIteratorAdapter, StackObj> JfrJavaThreadIterator;
+typedef JfrThreadIterator<JfrNonJavaThreadIteratorAdapter, StackObj> JfrNonJavaThreadIterator;
+
+#endif // SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP
--- a/src/hotspot/share/jfr/utilities/jfrTypes.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/utilities/jfrTypes.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -26,11 +26,14 @@
 #define SHARE_JFR_UTILITIES_JFRTYPES_HPP
 
 #include "jfrfiles/jfrEventIds.hpp"
+#include "utilities/globalDefinitions.hpp"
 
 typedef u8 traceid;
 typedef int fio_fd;
+
 const int invalid_fd = -1;
 const jlong invalid_offset = -1;
+const int64_t invalid_time = -1;
 const u4 STACK_DEPTH_DEFAULT = 64;
 const u4 MIN_STACK_DEPTH = 1;
 const u4 MAX_STACK_DEPTH = 2048;
@@ -48,4 +51,12 @@
   TIMED
 };
 
+enum JfrCheckpointType {
+  GENERIC,
+  FLUSH,
+  HEADER,
+  STATICS = 4,
+  THREADS = 8
+};
+
 #endif // SHARE_JFR_UTILITIES_JFRTYPES_HPP
--- a/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/writers/jfrJavaEventWriter.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -32,9 +32,9 @@
 class Thread;
 
 class JfrJavaEventWriter : AllStatic {
-  friend class JfrCheckpointThreadClosure;
+  friend class JfrNotifyClosure;
+  friend class JfrJavaEventWriterNotifyOperation;
   friend class JfrJavaEventWriterNotificationClosure;
-  friend class JfrJavaEventWriterNotifyOperation;
   friend class JfrRecorder;
  private:
   static bool initialize();
--- a/src/hotspot/share/jfr/writers/jfrStorageAdapter.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/writers/jfrStorageAdapter.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -82,7 +82,7 @@
     assert(_thread != NULL, "invariant");
     Flush f(_storage, used, requested, _thread);
     _storage = f.result();
-    return _storage != NULL;
+    return _storage != NULL && !_storage->excluded();
   }
 
   void release() {
@@ -236,7 +236,8 @@
   void release() {}
   bool flush(size_t used, size_t requested) {
     // don't flush/expand a buffer that is not our own
-    return false;
+    _pos = _start;
+    return true;
   }
 };
 
--- a/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jfr/writers/jfrWriterHost.inline.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -114,10 +114,7 @@
 template <typename BE, typename IE, typename WriterPolicyImpl >
 template <typename T>
 inline void WriterHost<BE, IE, WriterPolicyImpl>::be_write(T value) {
-  u1* const pos = ensure_size(sizeof(T));
-  if (pos) {
-    this->set_current_pos(BE::be_write(&value, 1, pos));
-  }
+  be_write(&value, 1);
 }
 
 template <typename BE, typename IE, typename WriterPolicyImpl >
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -22,6 +22,7 @@
  */
 
 #include "precompiled.hpp"
+#include "classfile/classLoaderData.inline.hpp"
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/stringTable.hpp"
 #include "classfile/symbolTable.hpp"
@@ -2032,7 +2033,7 @@
     JVMCI_THROW_0(NullPointerException);
   }
   InstanceKlass* ik = InstanceKlass::cast(JVMCIENV->asKlass(JVMCIENV->wrap(holder)));
-  if (ik->class_loader_data()->is_builtin_class_loader_data()) {
+  if (ik->class_loader_data()->is_boot_class_loader_data() || ik->class_loader_data()->is_platform_class_loader_data()) {
     return true;
   }
   return false;
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -620,21 +620,6 @@
   return (jint) obj->identity_hash();
 JRT_END
 
-JRT_ENTRY(jboolean, JVMCIRuntime::thread_is_interrupted(JavaThread* thread, oopDesc* receiver, jboolean clear_interrupted))
-  Handle receiverHandle(thread, receiver);
-  // A nested ThreadsListHandle may require the Threads_lock which
-  // requires thread_in_vm which is why this method cannot be JRT_LEAF.
-  ThreadsListHandle tlh;
-
-  JavaThread* receiverThread = java_lang_Thread::thread(receiverHandle());
-  if (receiverThread == NULL || (EnableThreadSMRExtraValidityChecks && !tlh.includes(receiverThread))) {
-    // The other thread may exit during this process, which is ok so return false.
-    return JNI_FALSE;
-  } else {
-    return (jint) receiverThread->is_interrupted(clear_interrupted != 0);
-  }
-JRT_END
-
 JRT_ENTRY(jint, JVMCIRuntime::test_deoptimize_call_int(JavaThread* thread, int value))
   deopt_caller();
   return (jint) value;
--- a/src/hotspot/share/jvmci/jvmciRuntime.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -309,7 +309,6 @@
   static void dynamic_new_array_or_null(JavaThread* thread, oopDesc* element_mirror, jint length) { dynamic_new_array_common(thread, element_mirror, length, true); }
   static void dynamic_new_instance_or_null(JavaThread* thread, oopDesc* type_mirror) { dynamic_new_instance_common(thread, type_mirror, true); }
 
-  static jboolean thread_is_interrupted(JavaThread* thread, oopDesc* obj, jboolean clear_interrupted);
   static void vm_message(jboolean vmError, jlong format, jlong v1, jlong v2, jlong v3);
   static jint identity_hash_code(JavaThread* thread, oopDesc* obj);
   static address exception_handler_for_pc(JavaThread* thread);
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -263,8 +263,6 @@
                                                                                                                                      \
   static_field(os,                             _polling_page,                                 address)                               \
                                                                                                                                      \
-  volatile_nonstatic_field(OSThread,           _interrupted,                                  jint)                                  \
-                                                                                                                                     \
   static_field(StubRoutines,                _verify_oop_count,                                jint)                                  \
                                                                                                                                      \
   static_field(StubRoutines,                _throw_delayed_StackOverflowError_entry,          address)                               \
@@ -641,7 +639,6 @@
   declare_function(JVMCIRuntime::dynamic_new_array_or_null) \
   declare_function(JVMCIRuntime::dynamic_new_instance_or_null) \
   \
-  declare_function(JVMCIRuntime::thread_is_interrupted) \
   declare_function(JVMCIRuntime::vm_message) \
   declare_function(JVMCIRuntime::identity_hash_code) \
   declare_function(JVMCIRuntime::exception_handler_for_pc) \
--- a/src/hotspot/share/logging/logDecorations.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/logging/logDecorations.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -65,6 +65,8 @@
   if (decorators.is_decorator(LogDecorators::full_name##_decorator)) { \
     _decoration_offset[LogDecorators::full_name##_decorator] = position; \
     position = create_##full_name##_decoration(position) + 1; \
+  } else { \
+    _decoration_offset[LogDecorators::full_name##_decorator] = NULL; \
   }
   DECORATOR_LIST
 #undef DECORATOR
--- a/src/hotspot/share/logging/logTag.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/logging/logTag.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -148,6 +148,7 @@
   LOG_TAG(startuptime) \
   LOG_TAG(state) \
   LOG_TAG(stats) \
+  LOG_TAG(streaming) \
   LOG_TAG(stringdedup) \
   LOG_TAG(stringtable) \
   LOG_TAG(symboltable) \
--- a/src/hotspot/share/memory/operator_new.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/memory/operator_new.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -89,4 +89,12 @@
   fatal("Should not call global delete []");
 }
 
+void operator delete(void* p, size_t size) throw() {
+  fatal("Should not call global sized delete");
+}
+
+void operator delete [](void* p, size_t size) throw() {
+  fatal("Should not call global sized delete []");
+}
+
 #endif // Non-product
--- a/src/hotspot/share/oops/constMethod.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/oops/constMethod.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -532,6 +532,10 @@
   int  size_of_parameters() const                { return _size_of_parameters; }
   void set_size_of_parameters(int size)          { _size_of_parameters = size; }
 
+  // result type (basic type of return value)
+  BasicType result_type() const                  { assert(_result_type >= T_BOOLEAN, "Must be set");
+                                                   return (BasicType)_result_type; }
+
   void set_result_type(BasicType rt)             { assert(rt < 16, "result type too large");
                                                    _result_type = (u1)rt; }
   // Deallocation for RedefineClasses
--- a/src/hotspot/share/oops/instanceKlass.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/oops/instanceKlass.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -3626,7 +3626,7 @@
     Array<int>* method_ordering = this->method_ordering();
     int length = method_ordering->length();
     if (JvmtiExport::can_maintain_original_method_order() ||
-        ((UseSharedSpaces || DumpSharedSpaces) && length != 0)) {
+        ((UseSharedSpaces || Arguments::is_dumping_archive()) && length != 0)) {
       guarantee(length == methods()->length(), "invalid method ordering length");
       jlong sum = 0;
       for (int j = 0; j < length; j++) {
--- a/src/hotspot/share/oops/klass.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/oops/klass.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -564,12 +564,6 @@
     // Restore class_loader_data to the null class loader data
     set_class_loader_data(loader_data);
 
-    // Workaround for suspected bug.  Make sure other threads see this assignment.
-    // This shouldn't be necessary but the compiler thread seems to be behind
-    // the times, even though this thread takes MethodCompileQueue_lock and the thread
-    // that doesn't see this value also takes that lock.
-    OrderAccess::fence();
-
     // Add to null class loader list first before creating the mirror
     // (same order as class file parsing)
     loader_data->add_class(this);
--- a/src/hotspot/share/oops/method.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/oops/method.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -572,24 +572,16 @@
   return extra_stack_entries() * Interpreter::stackElementSize;
 }
 
-
 void Method::compute_size_of_parameters(Thread *thread) {
   ArgumentSizeComputer asc(signature());
   set_size_of_parameters(asc.size() + (is_static() ? 0 : 1));
 }
 
-BasicType Method::result_type() const {
-  ResultTypeFinder rtf(signature());
-  return rtf.type();
-}
-
-
 bool Method::is_empty_method() const {
   return  code_size() == 1
       && *code_base() == Bytecodes::_return;
 }
 
-
 bool Method::is_vanilla_constructor() const {
   // Returns true if this method is a vanilla constructor, i.e. an "<init>" "()V" method
   // which only calls the superclass vanilla constructor and possibly does stores of
--- a/src/hotspot/share/oops/method.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/oops/method.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -608,7 +608,7 @@
 
   void compute_size_of_parameters(Thread *thread); // word size of parameters (receiver if any + arguments)
   Symbol* klass_name() const;                    // returns the name of the method holder
-  BasicType result_type() const;                 // type of the method result
+  BasicType result_type() const                  { return constMethod()->result_type(); }
   bool is_returning_oop() const                  { BasicType r = result_type(); return is_reference_type(r); }
   bool is_returning_fp() const                   { BasicType r = result_type(); return (r == T_FLOAT || r == T_DOUBLE); }
 
--- a/src/hotspot/share/oops/oop.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/oops/oop.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -173,6 +173,8 @@
 
   jboolean bool_field(int offset) const;
   void bool_field_put(int offset, jboolean contents);
+  jboolean bool_field_volatile(int offset) const;
+  void bool_field_put_volatile(int offset, jboolean contents);
 
   jint int_field(int offset) const;
   jint int_field_raw(int offset) const;
--- a/src/hotspot/share/oops/oop.inline.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/oops/oop.inline.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -301,9 +301,10 @@
 inline jchar oopDesc::char_field(int offset) const                  { return HeapAccess<>::load_at(as_oop(), offset);  }
 inline void  oopDesc::char_field_put(int offset, jchar value)       { HeapAccess<>::store_at(as_oop(), offset, value); }
 
-inline jboolean oopDesc::bool_field(int offset) const               { return HeapAccess<>::load_at(as_oop(), offset);                }
+inline jboolean oopDesc::bool_field(int offset) const               { return HeapAccess<>::load_at(as_oop(), offset); }
 inline void     oopDesc::bool_field_put(int offset, jboolean value) { HeapAccess<>::store_at(as_oop(), offset, jboolean(value & 1)); }
-
+inline jboolean oopDesc::bool_field_volatile(int offset) const      { return HeapAccess<MO_SEQ_CST>::load_at(as_oop(), offset); }
+inline void     oopDesc::bool_field_put_volatile(int offset, jboolean value) { HeapAccess<MO_SEQ_CST>::store_at(as_oop(), offset, jboolean(value & 1)); }
 inline jshort oopDesc::short_field(int offset) const                { return HeapAccess<>::load_at(as_oop(), offset);  }
 inline void   oopDesc::short_field_put(int offset, jshort value)    { HeapAccess<>::store_at(as_oop(), offset, value); }
 
--- a/src/hotspot/share/opto/c2compiler.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/opto/c2compiler.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -582,7 +582,6 @@
   case vmIntrinsics::_storeFence:
   case vmIntrinsics::_fullFence:
   case vmIntrinsics::_currentThread:
-  case vmIntrinsics::_isInterrupted:
 #ifdef JFR_HAVE_INTRINSICS
   case vmIntrinsics::_counterTime:
   case vmIntrinsics::_getClassId:
--- a/src/hotspot/share/opto/library_call.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/opto/library_call.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -264,7 +264,6 @@
   bool inline_native_classID();
   bool inline_native_getEventWriter();
 #endif
-  bool inline_native_isInterrupted();
   bool inline_native_Class_query(vmIntrinsics::ID id);
   bool inline_native_subtype_check();
   bool inline_native_getLength();
@@ -752,7 +751,6 @@
   case vmIntrinsics::_onSpinWait:               return inline_onspinwait();
 
   case vmIntrinsics::_currentThread:            return inline_native_currentThread();
-  case vmIntrinsics::_isInterrupted:            return inline_native_isInterrupted();
 
 #ifdef JFR_HAVE_INTRINSICS
   case vmIntrinsics::_counterTime:              return inline_native_time_funcs(CAST_FROM_FN_PTR(address, JFR_TIME_FUNCTION), "counterTime");
@@ -3041,128 +3039,6 @@
   return true;
 }
 
-//------------------------inline_native_isInterrupted------------------
-// private native boolean java.lang.Thread.isInterrupted(boolean ClearInterrupted);
-bool LibraryCallKit::inline_native_isInterrupted() {
-  // Add a fast path to t.isInterrupted(clear_int):
-  //   (t == Thread.current() &&
-  //    (!TLS._osthread._interrupted || WINDOWS_ONLY(false) NOT_WINDOWS(!clear_int)))
-  //   ? TLS._osthread._interrupted : /*slow path:*/ t.isInterrupted(clear_int)
-  // So, in the common case that the interrupt bit is false,
-  // we avoid making a call into the VM.  Even if the interrupt bit
-  // is true, if the clear_int argument is false, we avoid the VM call.
-  // However, if the receiver is not currentThread, we must call the VM,
-  // because there must be some locking done around the operation.
-
-  // We only go to the fast case code if we pass two guards.
-  // Paths which do not pass are accumulated in the slow_region.
-
-  enum {
-    no_int_result_path   = 1, // t == Thread.current() && !TLS._osthread._interrupted
-    no_clear_result_path = 2, // t == Thread.current() &&  TLS._osthread._interrupted && !clear_int
-    slow_result_path     = 3, // slow path: t.isInterrupted(clear_int)
-    PATH_LIMIT
-  };
-
-  // Ensure that it's not possible to move the load of TLS._osthread._interrupted flag
-  // out of the function.
-  insert_mem_bar(Op_MemBarCPUOrder);
-
-  RegionNode* result_rgn = new RegionNode(PATH_LIMIT);
-  PhiNode*    result_val = new PhiNode(result_rgn, TypeInt::BOOL);
-
-  RegionNode* slow_region = new RegionNode(1);
-  record_for_igvn(slow_region);
-
-  // (a) Receiving thread must be the current thread.
-  Node* rec_thr = argument(0);
-  Node* tls_ptr = NULL;
-  Node* cur_thr = generate_current_thread(tls_ptr);
-
-  // Resolve oops to stable for CmpP below.
-  cur_thr = access_resolve(cur_thr, 0);
-  rec_thr = access_resolve(rec_thr, 0);
-
-  Node* cmp_thr = _gvn.transform(new CmpPNode(cur_thr, rec_thr));
-  Node* bol_thr = _gvn.transform(new BoolNode(cmp_thr, BoolTest::ne));
-
-  generate_slow_guard(bol_thr, slow_region);
-
-  // (b) Interrupt bit on TLS must be false.
-  Node* p = basic_plus_adr(top()/*!oop*/, tls_ptr, in_bytes(JavaThread::osthread_offset()));
-  Node* osthread = make_load(NULL, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered);
-  p = basic_plus_adr(top()/*!oop*/, osthread, in_bytes(OSThread::interrupted_offset()));
-
-  // Set the control input on the field _interrupted read to prevent it floating up.
-  Node* int_bit = make_load(control(), p, TypeInt::BOOL, T_INT, MemNode::unordered);
-  Node* cmp_bit = _gvn.transform(new CmpINode(int_bit, intcon(0)));
-  Node* bol_bit = _gvn.transform(new BoolNode(cmp_bit, BoolTest::ne));
-
-  IfNode* iff_bit = create_and_map_if(control(), bol_bit, PROB_UNLIKELY_MAG(3), COUNT_UNKNOWN);
-
-  // First fast path:  if (!TLS._interrupted) return false;
-  Node* false_bit = _gvn.transform(new IfFalseNode(iff_bit));
-  result_rgn->init_req(no_int_result_path, false_bit);
-  result_val->init_req(no_int_result_path, intcon(0));
-
-  // drop through to next case
-  set_control( _gvn.transform(new IfTrueNode(iff_bit)));
-
-#ifndef _WINDOWS
-  // (c) Or, if interrupt bit is set and clear_int is false, use 2nd fast path.
-  Node* clr_arg = argument(1);
-  Node* cmp_arg = _gvn.transform(new CmpINode(clr_arg, intcon(0)));
-  Node* bol_arg = _gvn.transform(new BoolNode(cmp_arg, BoolTest::ne));
-  IfNode* iff_arg = create_and_map_if(control(), bol_arg, PROB_FAIR, COUNT_UNKNOWN);
-
-  // Second fast path:  ... else if (!clear_int) return true;
-  Node* false_arg = _gvn.transform(new IfFalseNode(iff_arg));
-  result_rgn->init_req(no_clear_result_path, false_arg);
-  result_val->init_req(no_clear_result_path, intcon(1));
-
-  // drop through to next case
-  set_control( _gvn.transform(new IfTrueNode(iff_arg)));
-#else
-  // To return true on Windows you must read the _interrupted field
-  // and check the event state i.e. take the slow path.
-#endif // _WINDOWS
-
-  // (d) Otherwise, go to the slow path.
-  slow_region->add_req(control());
-  set_control( _gvn.transform(slow_region));
-
-  if (stopped()) {
-    // There is no slow path.
-    result_rgn->init_req(slow_result_path, top());
-    result_val->init_req(slow_result_path, top());
-  } else {
-    // non-virtual because it is a private non-static
-    CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_isInterrupted);
-
-    Node* slow_val = set_results_for_java_call(slow_call);
-    // this->control() comes from set_results_for_java_call
-
-    Node* fast_io  = slow_call->in(TypeFunc::I_O);
-    Node* fast_mem = slow_call->in(TypeFunc::Memory);
-
-    // These two phis are pre-filled with copies of of the fast IO and Memory
-    PhiNode* result_mem  = PhiNode::make(result_rgn, fast_mem, Type::MEMORY, TypePtr::BOTTOM);
-    PhiNode* result_io   = PhiNode::make(result_rgn, fast_io,  Type::ABIO);
-
-    result_rgn->init_req(slow_result_path, control());
-    result_io ->init_req(slow_result_path, i_o());
-    result_mem->init_req(slow_result_path, reset_memory());
-    result_val->init_req(slow_result_path, slow_val);
-
-    set_all_memory(_gvn.transform(result_mem));
-    set_i_o(       _gvn.transform(result_io));
-  }
-
-  C->set_has_split_ifs(true); // Has chance for split-if optimization
-  set_result(result_rgn, result_val);
-  return true;
-}
-
 //---------------------------load_mirror_from_klass----------------------------
 // Given a klass oop, load its java mirror (a java.lang.Class oop).
 Node* LibraryCallKit::load_mirror_from_klass(Node* klass) {
--- a/src/hotspot/share/prims/jvm.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/prims/jvm.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -3052,50 +3052,6 @@
   return JNIHandles::make_local(env, jthread);
 JVM_END
 
-class CountStackFramesTC : public ThreadClosure {
-  int _count;
-  bool _suspended;
- public:
-  CountStackFramesTC() : _count(0), _suspended(false) {}
-  virtual void do_thread(Thread* thread) {
-    JavaThread* jt = (JavaThread*)thread;
-    if (!jt->is_external_suspend()) {
-      // To keep same behavior we fail this operation,
-      // even if it would work perfectly.
-      return;
-    }
-    _suspended = true;
-     // Count all java activation, i.e., number of vframes.
-    for (vframeStream vfst(jt); !vfst.at_end(); vfst.next()) {
-      // Native frames are not counted.
-      if (!vfst.method()->is_native()) _count++;
-    }
-  }
-  int count() { return _count; }
-  int suspended() { return _suspended; }
-};
-
-JVM_ENTRY(jint, JVM_CountStackFrames(JNIEnv* env, jobject jthread))
-  JVMWrapper("JVM_CountStackFrames");
-
-  ThreadsListHandle tlh(thread);
-  JavaThread* receiver = NULL;
-  bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
-  if (is_alive) {
-    // jthread refers to a live JavaThread.
-    CountStackFramesTC csf;
-    Handshake::execute(&csf, receiver);
-    if (!csf.suspended()) {
-      THROW_MSG_0(vmSymbols::java_lang_IllegalThreadStateException(),
-                  "this thread is not suspended");
-    }
-    return csf.count();
-  }
-  // Implied else: if JavaThread is not alive simply return a count of 0.
-  return 0;
-JVM_END
-
-
 JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
   JVMWrapper("JVM_Interrupt");
 
@@ -3109,21 +3065,6 @@
 JVM_END
 
 
-JVM_ENTRY(jboolean, JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clear_interrupted))
-  JVMWrapper("JVM_IsInterrupted");
-
-  ThreadsListHandle tlh(thread);
-  JavaThread* receiver = NULL;
-  bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
-  if (is_alive) {
-    // jthread refers to a live JavaThread.
-    return (jboolean) receiver->is_interrupted(clear_interrupted != 0);
-  } else {
-    return JNI_FALSE;
-  }
-JVM_END
-
-
 // Return true iff the current thread has locked the object passed in
 
 JVM_ENTRY(jboolean, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj))
--- a/src/hotspot/share/prims/jvmtiEnv.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -879,8 +879,7 @@
     if (jts == _thread_in_native) {
       state |= JVMTI_THREAD_STATE_IN_NATIVE;
     }
-    OSThread* osThread = java_thread->osthread();
-    if (osThread != NULL && osThread->interrupted()) {
+    if (java_thread->is_interrupted(false)) {
       state |= JVMTI_THREAD_STATE_INTERRUPTED;
     }
   }
@@ -1092,7 +1091,6 @@
 // thread - NOT pre-checked
 jvmtiError
 JvmtiEnv::InterruptThread(jthread thread) {
-  // TODO: this is very similar to JVM_Interrupt(); share code in future
   JavaThread* current_thread  = JavaThread::current();
   JavaThread* java_thread = NULL;
   ThreadsListHandle tlh(current_thread);
@@ -1100,7 +1098,11 @@
   if (err != JVMTI_ERROR_NONE) {
     return err;
   }
-
+  // Really this should be a Java call to Thread.interrupt to ensure the same
+  // semantics, however historically this has not been done for some reason.
+  // So we continue with that (which means we don't interact with any Java-level
+  // Interruptible object) but we must set the Java-level interrupted state.
+  java_lang_Thread::set_interrupted(JNIHandles::resolve(thread), true);
   java_thread->interrupt();
 
   return JVMTI_ERROR_NONE;
--- a/src/hotspot/share/prims/jvmtiEnvBase.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1214,8 +1214,7 @@
     if (jts == _thread_in_native) {
       state |= JVMTI_THREAD_STATE_IN_NATIVE;
     }
-    OSThread* osThread = thr->osthread();
-    if (osThread != NULL && osThread->interrupted()) {
+    if (thr->is_interrupted(false)) {
       state |= JVMTI_THREAD_STATE_INTERRUPTED;
     }
   }
--- a/src/hotspot/share/runtime/arguments.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/arguments.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -60,12 +60,6 @@
 #include "jfr/jfr.hpp"
 #endif
 
-// Note: This is a special bug reporting site for the JVM
-#ifdef VENDOR_URL_VM_BUG
-# define DEFAULT_VENDOR_URL_BUG VENDOR_URL_VM_BUG
-#else
-# define DEFAULT_VENDOR_URL_BUG "http://bugreport.java.com/bugreport/crash.jsp"
-#endif
 #define DEFAULT_JAVA_LAUNCHER  "generic"
 
 char*  Arguments::_jvm_flags_file               = NULL;
@@ -80,7 +74,7 @@
 Arguments::Mode Arguments::_mode                = _mixed;
 bool   Arguments::_java_compiler                = false;
 bool   Arguments::_xdebug_mode                  = false;
-const char*  Arguments::_java_vendor_url_bug    = DEFAULT_VENDOR_URL_BUG;
+const char*  Arguments::_java_vendor_url_bug    = NULL;
 const char*  Arguments::_sun_java_launcher      = DEFAULT_JAVA_LAUNCHER;
 bool   Arguments::_sun_java_launcher_is_altjvm  = false;
 
@@ -1422,12 +1416,16 @@
         os::free(old_java_command);
       }
     } else if (strcmp(key, "java.vendor.url.bug") == 0) {
+      // If this property is set on the command line then its value will be
+      // displayed in VM error logs as the URL at which to submit such logs.
+      // Normally the URL displayed in error logs is different from the value
+      // of this system property, so a different property should have been
+      // used here, but we leave this as-is in case someone depends upon it.
       const char* old_java_vendor_url_bug = _java_vendor_url_bug;
       // save it in _java_vendor_url_bug, so JVM fatal error handler can access
       // its value without going through the property list or making a Java call.
       _java_vendor_url_bug = os::strdup_check_oom(value, mtArguments);
-      if (old_java_vendor_url_bug != DEFAULT_VENDOR_URL_BUG) {
-        assert(old_java_vendor_url_bug != NULL, "_java_vendor_url_bug is NULL");
+      if (old_java_vendor_url_bug != NULL) {
         os::free((void *)old_java_vendor_url_bug);
       }
     }
@@ -2185,7 +2183,8 @@
 
 // Parse JavaVMInitArgs structure
 
-jint Arguments::parse_vm_init_args(const JavaVMInitArgs *java_tool_options_args,
+jint Arguments::parse_vm_init_args(const JavaVMInitArgs *vm_options_args,
+                                   const JavaVMInitArgs *java_tool_options_args,
                                    const JavaVMInitArgs *java_options_args,
                                    const JavaVMInitArgs *cmd_line_args) {
   bool patch_mod_javabase = false;
@@ -2203,9 +2202,15 @@
   // Setup flags for mixed which is the default
   set_mode_flags(_mixed);
 
+  // Parse args structure generated from java.base vm options resource
+  jint result = parse_each_vm_init_arg(vm_options_args, &patch_mod_javabase, JVMFlag::JIMAGE_RESOURCE);
+  if (result != JNI_OK) {
+    return result;
+  }
+
   // Parse args structure generated from JAVA_TOOL_OPTIONS environment
   // variable (if present).
-  jint result = parse_each_vm_init_arg(java_tool_options_args, &patch_mod_javabase, JVMFlag::ENVIRON_VAR);
+  result = parse_each_vm_init_arg(java_tool_options_args, &patch_mod_javabase, JVMFlag::ENVIRON_VAR);
   if (result != JNI_OK) {
     return result;
   }
@@ -2711,7 +2716,6 @@
         needs_module_property_warning = true;
         continue;
       }
-
       if (!add_property(tail)) {
         return JNI_ENOMEM;
       }
@@ -3143,7 +3147,13 @@
   NOT_PRODUCT(UNSUPPORTED_OPTION(TraceProfileInterpreter));
 #endif
 
-#ifndef TIERED
+
+#ifdef TIERED
+  // Parse the CompilationMode flag
+  if (!CompilationModeFlag::initialize()) {
+    return JNI_ERR;
+  }
+#else
   // Tiered compilation is undefined.
   UNSUPPORTED_OPTION(TieredCompilation);
 #endif
@@ -3832,16 +3842,19 @@
   const char* hotspotrc = ".hotspotrc";
   bool settings_file_specified = false;
   bool needs_hotspotrc_warning = false;
+  ScopedVMInitArgs initial_vm_options_args("");
   ScopedVMInitArgs initial_java_tool_options_args("env_var='JAVA_TOOL_OPTIONS'");
   ScopedVMInitArgs initial_java_options_args("env_var='_JAVA_OPTIONS'");
 
   // Pointers to current working set of containers
   JavaVMInitArgs* cur_cmd_args;
+  JavaVMInitArgs* cur_vm_options_args;
   JavaVMInitArgs* cur_java_options_args;
   JavaVMInitArgs* cur_java_tool_options_args;
 
   // Containers for modified/expanded options
   ScopedVMInitArgs mod_cmd_args("cmd_line_args");
+  ScopedVMInitArgs mod_vm_options_args("vm_options_args");
   ScopedVMInitArgs mod_java_tool_options_args("env_var='JAVA_TOOL_OPTIONS'");
   ScopedVMInitArgs mod_java_options_args("env_var='_JAVA_OPTIONS'");
 
@@ -3857,6 +3870,16 @@
     return code;
   }
 
+  // Parse the options in the /java.base/jdk/internal/vm/options resource, if present
+  char *vmoptions = ClassLoader::lookup_vm_options();
+  if (vmoptions != NULL) {
+    code = parse_options_buffer("vm options resource", vmoptions, strlen(vmoptions), &initial_vm_options_args);
+    FREE_C_HEAP_ARRAY(char, vmoptions);
+    if (code != JNI_OK) {
+      return code;
+    }
+  }
+
   code = expand_vm_options_as_needed(initial_java_tool_options_args.get(),
                                      &mod_java_tool_options_args,
                                      &cur_java_tool_options_args);
@@ -3878,6 +3901,13 @@
     return code;
   }
 
+  code = expand_vm_options_as_needed(initial_vm_options_args.get(),
+                                     &mod_vm_options_args,
+                                     &cur_vm_options_args);
+  if (code != JNI_OK) {
+    return code;
+  }
+
   const char* flags_file = Arguments::get_jvm_flags_file();
   settings_file_specified = (flags_file != NULL);
 
@@ -3915,7 +3945,8 @@
   }
 
   // Parse JavaVMInitArgs structure passed in, as well as JAVA_TOOL_OPTIONS and _JAVA_OPTIONS
-  jint result = parse_vm_init_args(cur_java_tool_options_args,
+  jint result = parse_vm_init_args(cur_vm_options_args,
+                                   cur_java_tool_options_args,
                                    cur_java_options_args,
                                    cur_cmd_args);
 
--- a/src/hotspot/share/runtime/arguments.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/arguments.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -427,7 +427,8 @@
 
   static void handle_extra_cms_flags(const char* msg);
 
-  static jint parse_vm_init_args(const JavaVMInitArgs *java_tool_options_args,
+  static jint parse_vm_init_args(const JavaVMInitArgs *vm_options_args,
+                                 const JavaVMInitArgs *java_tool_options_args,
                                  const JavaVMInitArgs *java_options_args,
                                  const JavaVMInitArgs *cmd_line_args);
   static jint parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_mod_javabase, JVMFlag::Flags origin);
--- a/src/hotspot/share/runtime/flags/jvmFlag.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/flags/jvmFlag.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -709,6 +709,8 @@
       st->print("attach"); break;
     case INTERNAL:
       st->print("internal"); break;
+    case JIMAGE_RESOURCE:
+      st->print("jimage"); break;
   }
   st->print("}");
 }
--- a/src/hotspot/share/runtime/flags/jvmFlag.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/flags/jvmFlag.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -44,8 +44,9 @@
     ERGONOMIC        = 5,
     ATTACH_ON_DEMAND = 6,
     INTERNAL         = 7,
+    JIMAGE_RESOURCE  = 8,
 
-    LAST_VALUE_ORIGIN = INTERNAL,
+    LAST_VALUE_ORIGIN = JIMAGE_RESOURCE,
     VALUE_ORIGIN_BITS = 4,
     VALUE_ORIGIN_MASK = right_n_bits(VALUE_ORIGIN_BITS),
 
--- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -60,16 +60,29 @@
  * 4) The JVM is build using the compilers and tiered compilation is enabled. The option
  *    'TieredStopAtLevel = CompLevel_full_optimization' (the default value). As a result,
  *    the minimum number of compiler threads is 2.
+ * 5) Non-tiered emulation mode is on. CompilationModeFlag::disable_intermediate() == true.
+ *    The mininum number of threads is 2. But if CompilationModeFlag::quick_internal() == false, then it's 1.
  */
 JVMFlag::Error CICompilerCountConstraintFunc(intx value, bool verbose) {
   int min_number_of_compiler_threads = 0;
 #if !defined(COMPILER1) && !defined(COMPILER2) && !INCLUDE_JVMCI
   // case 1
 #else
-  if (!TieredCompilation || (TieredStopAtLevel < CompLevel_full_optimization)) {
-    min_number_of_compiler_threads = 1; // case 2 or case 3
+  if (TieredCompilation) {
+    if (TieredStopAtLevel < CompLevel_full_optimization || CompilationModeFlag::quick_only()) {
+      min_number_of_compiler_threads = 1; // case 3
+    } else if (CompilationModeFlag::disable_intermediate()) {
+      // case 5
+      if (CompilationModeFlag::quick_internal()) {
+        min_number_of_compiler_threads = 2;
+      } else {
+        min_number_of_compiler_threads = 1;
+      }
+    } else {
+      min_number_of_compiler_threads = 2;   // case 4 (tiered)
+    }
   } else {
-    min_number_of_compiler_threads = 2;   // case 4 (tiered)
+    min_number_of_compiler_threads = 1; // case 2
   }
 #endif
 
--- a/src/hotspot/share/runtime/globals.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/globals.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -2059,6 +2059,35 @@
           "if coming from AOT")                                             \
           range(0, max_jint)                                                \
                                                                             \
+  diagnostic(intx, Tier0AOTInvocationThreshold, 200,                        \
+          "Switch to interpreter to profile if the number of method "       \
+          "invocations crosses this threshold if coming from AOT "          \
+          "(applicable only with "                                          \
+          "CompilationMode=high-only|high-only-quick-internal)")            \
+          range(0, max_jint)                                                \
+                                                                            \
+  diagnostic(intx, Tier0AOTMinInvocationThreshold, 100,                     \
+          "Minimum number of invocations to switch to interpreter "         \
+          "to profile if coming from AOT "                                  \
+          "(applicable only with "                                          \
+          "CompilationMode=high-only|high-only-quick-internal)")            \
+          range(0, max_jint)                                                \
+                                                                            \
+  diagnostic(intx, Tier0AOTCompileThreshold, 2000,                          \
+          "Threshold at which to switch to interpreter to profile "         \
+          "if coming from AOT "                                             \
+          "(invocation minimum must be satisfied, "                         \
+          "applicable only with "                                           \
+          "CompilationMode=high-only|high-only-quick-internal)")            \
+          range(0, max_jint)                                                \
+                                                                            \
+  diagnostic(intx, Tier0AOTBackEdgeThreshold,  60000,                       \
+          "Back edge threshold at which to switch to interpreter "          \
+          "to profile if coming from AOT "                                  \
+          "(applicable only with "                                          \
+          "CompilationMode=high-only|high-only-quick-internal)")            \
+          range(0, max_jint)                                                \
+                                                                            \
   product(intx, Tier4InvocationThreshold, 5000,                             \
           "Compile if number of method invocations crosses this "           \
           "threshold")                                                      \
@@ -2070,13 +2099,44 @@
                                                                             \
   product(intx, Tier4CompileThreshold, 15000,                               \
           "Threshold at which tier 4 compilation is invoked (invocation "   \
-          "minimum must be satisfied")                                      \
+          "minimum must be satisfied)")                                     \
           range(0, max_jint)                                                \
                                                                             \
   product(intx, Tier4BackEdgeThreshold, 40000,                              \
           "Back edge threshold at which tier 4 OSR compilation is invoked") \
           range(0, max_jint)                                                \
                                                                             \
+  diagnostic(intx, Tier40InvocationThreshold, 5000,                         \
+          "Compile if number of method invocations crosses this "           \
+          "threshold (applicable only with "                                \
+          "CompilationMode=high-only|high-only-quick-internal)")            \
+          range(0, max_jint)                                                \
+                                                                            \
+  diagnostic(intx, Tier40MinInvocationThreshold, 600,                       \
+          "Minimum number of invocations to compile at tier 4 "             \
+          "(applicable only with "                                          \
+          "CompilationMode=high-only|high-only-quick-internal)")            \
+          range(0, max_jint)                                                \
+                                                                            \
+  diagnostic(intx, Tier40CompileThreshold, 10000,                           \
+          "Threshold at which tier 4 compilation is invoked (invocation "   \
+          "minimum must be satisfied, applicable only with "                \
+          "CompilationMode=high-only|high-only-quick-internal)")            \
+          range(0, max_jint)                                                \
+                                                                            \
+  diagnostic(intx, Tier40BackEdgeThreshold, 15000,                          \
+          "Back edge threshold at which tier 4 OSR compilation is invoked " \
+          "(applicable only with "                                          \
+          "CompilationMode=high-only|high-only-quick-internal)")            \
+          range(0, max_jint)                                                \
+                                                                            \
+  diagnostic(intx, Tier0Delay, 5,                                           \
+          "If C2 queue size grows over this amount per compiler thread "    \
+          "do not start profiling in the interpreter "                      \
+          "(applicable only with "                                          \
+          "CompilationMode=high-only|high-only-quick-internal)")            \
+          range(0, max_jint)                                                \
+                                                                            \
   product(intx, Tier3DelayOn, 5,                                            \
           "If C2 queue size grows over this amount per compiler thread "    \
           "stop compiling at tier 3 and start compiling at tier 2")         \
@@ -2108,7 +2168,9 @@
                                                                             \
   product(intx, Tier0ProfilingStartPercentage, 200,                         \
           "Start profiling in interpreter if the counters exceed tier 3 "   \
-          "thresholds by the specified percentage")                         \
+          "thresholds (tier 4 thresholds with "                             \
+          "CompilationMode=high-only|high-only-quick-internal)"             \
+          "by the specified percentage")                                    \
           range(0, max_jint)                                                \
                                                                             \
   product(uintx, IncreaseFirstTierCompileThresholdAt, 50,                   \
@@ -2124,6 +2186,14 @@
           "Maximum rate sampling interval (in milliseconds)")               \
           range(0, max_intx)                                                \
                                                                             \
+  product(ccstr, CompilationMode, "default",                                \
+          "Compilation modes: "                                             \
+          "default: normal tiered compilation; "                            \
+          "quick-only: C1-only mode; "                                      \
+          "high-only: C2/JVMCI-only mode; "                                 \
+          "high-only-quick-internal: C2/JVMCI-only mode, "                  \
+          "with JVMCI compiler compiled with C1.")                          \
+                                                                            \
   product_pd(bool, TieredCompilation,                                       \
           "Enable tiered compilation")                                      \
                                                                             \
--- a/src/hotspot/share/runtime/java.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/java.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -690,6 +690,8 @@
 JDK_Version JDK_Version::_current;
 const char* JDK_Version::_runtime_name;
 const char* JDK_Version::_runtime_version;
+const char* JDK_Version::_runtime_vendor_version;
+const char* JDK_Version::_runtime_vendor_vm_bug_url;
 
 void JDK_Version::initialize() {
   jdk_version_info info;
--- a/src/hotspot/share/runtime/java.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/java.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -67,6 +67,8 @@
   static JDK_Version _current;
   static const char* _runtime_name;
   static const char* _runtime_version;
+  static const char* _runtime_vendor_version;
+  static const char* _runtime_vendor_vm_bug_url;
 
   uint8_t _major;
   uint8_t _minor;
@@ -142,6 +144,20 @@
     _runtime_version = version;
   }
 
+  static const char* runtime_vendor_version() {
+    return _runtime_vendor_version;
+  }
+  static void set_runtime_vendor_version(const char* vendor_version) {
+    _runtime_vendor_version = vendor_version;
+  }
+
+  static const char* runtime_vendor_vm_bug_url() {
+    return _runtime_vendor_vm_bug_url;
+  }
+  static void set_runtime_vendor_vm_bug_url(const char* vendor_vm_bug_url) {
+    _runtime_vendor_vm_bug_url = vendor_vm_bug_url;
+  }
+
 };
 
 #endif // SHARE_RUNTIME_JAVA_HPP
--- a/src/hotspot/share/runtime/mutexLocker.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/mutexLocker.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -312,7 +312,7 @@
 #if INCLUDE_JFR
   def(JfrMsg_lock                  , PaddedMonitor, leaf,        true,  _safepoint_check_always);
   def(JfrBuffer_lock               , PaddedMutex  , leaf,        true,  _safepoint_check_never);
-  def(JfrStream_lock               , PaddedMutex  , leaf+1,      true,  _safepoint_check_never);      // ensure to rank lower than 'safepoint'
+  def(JfrStream_lock               , PaddedMutex  , nonleaf + 1, false, _safepoint_check_always);
   def(JfrStacktrace_lock           , PaddedMutex  , special,     true,  _safepoint_check_never);
   def(JfrThreadSampler_lock        , PaddedMonitor, leaf,        true,  _safepoint_check_never);
 #endif
--- a/src/hotspot/share/runtime/osThread.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/osThread.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -30,7 +30,6 @@
   pd_initialize();
   set_start_proc(start_proc);
   set_start_parm(start_parm);
-  _interrupted = 0;
 }
 
 OSThread::~OSThread() {
--- a/src/hotspot/share/runtime/osThread.hpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/osThread.hpp	Mon Nov 04 11:25:55 2019 +0000
@@ -62,12 +62,6 @@
   OSThreadStartFunc _start_proc;  // Thread start routine
   void* _start_parm;              // Thread start routine parameter
   volatile ThreadState _state;    // Thread state *hint*
-  volatile jint _interrupted;     // Thread.isInterrupted state
-
-  // Note:  _interrupted must be jint, so that Java intrinsics can access it.
-  // The value stored there must be either 0 or 1.  It must be possible
-  // for Java to emulate Thread.currentThread().isInterrupted() by performing
-  // the double indirection Thread::current()->_osthread->_interrupted.
 
   // Methods
  public:
@@ -82,18 +76,14 @@
   void set_start_proc(OSThreadStartFunc start_proc) { _start_proc = start_proc; }
   void* start_parm() const                          { return _start_parm; }
   void set_start_parm(void* start_parm)             { _start_parm = start_parm; }
-  // These are specialized on Windows.
+  // This is specialized on Windows.
 #ifndef _WINDOWS
-  volatile bool interrupted() const                 { return _interrupted != 0; }
-  void set_interrupted(bool z)                      { _interrupted = z ? 1 : 0; }
+  void set_interrupted(bool z)                      { /* nothing to do */ }
 #endif
   // Printing
   void print_on(outputStream* st) const;
   void print() const;
 
-  // For java intrinsics:
-  static ByteSize interrupted_offset()            { return byte_offset_of(OSThread, _interrupted); }
-
   // Platform dependent stuff
 #include OS_HEADER(osThread)
 
--- a/src/hotspot/share/runtime/thread.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/thread.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1090,6 +1090,8 @@
 
 char java_runtime_name[128] = "";
 char java_runtime_version[128] = "";
+char java_runtime_vendor_version[128] = "";
+char java_runtime_vendor_vm_bug_url[128] = "";
 
 // extract the JRE name from java.lang.VersionProps.java_runtime_name
 static const char* get_java_runtime_name(TRAPS) {
@@ -1135,6 +1137,50 @@
   }
 }
 
+// extract the JRE vendor version from java.lang.VersionProps.VENDOR_VERSION
+static const char* get_java_runtime_vendor_version(TRAPS) {
+  Klass* k = SystemDictionary::find(vmSymbols::java_lang_VersionProps(),
+                                    Handle(), Handle(), CHECK_AND_CLEAR_NULL);
+  fieldDescriptor fd;
+  bool found = k != NULL &&
+               InstanceKlass::cast(k)->find_local_field(vmSymbols::java_runtime_vendor_version_name(),
+                                                        vmSymbols::string_signature(), &fd);
+  if (found) {
+    oop name_oop = k->java_mirror()->obj_field(fd.offset());
+    if (name_oop == NULL) {
+      return NULL;
+    }
+    const char* name = java_lang_String::as_utf8_string(name_oop,
+                                                        java_runtime_vendor_version,
+                                                        sizeof(java_runtime_vendor_version));
+    return name;
+  } else {
+    return NULL;
+  }
+}
+
+// extract the JRE vendor VM bug URL from java.lang.VersionProps.VENDOR_URL_VM_BUG
+static const char* get_java_runtime_vendor_vm_bug_url(TRAPS) {
+  Klass* k = SystemDictionary::find(vmSymbols::java_lang_VersionProps(),
+                                    Handle(), Handle(), CHECK_AND_CLEAR_NULL);
+  fieldDescriptor fd;
+  bool found = k != NULL &&
+               InstanceKlass::cast(k)->find_local_field(vmSymbols::java_runtime_vendor_vm_bug_url_name(),
+                                                        vmSymbols::string_signature(), &fd);
+  if (found) {
+    oop name_oop = k->java_mirror()->obj_field(fd.offset());
+    if (name_oop == NULL) {
+      return NULL;
+    }
+    const char* name = java_lang_String::as_utf8_string(name_oop,
+                                                        java_runtime_vendor_vm_bug_url,
+                                                        sizeof(java_runtime_vendor_vm_bug_url));
+    return name;
+  } else {
+    return NULL;
+  }
+}
+
 // General purpose hook into Java code, run once when the VM is initialized.
 // The Java library method itself may be changed independently from the VM.
 static void call_postVMInitHook(TRAPS) {
@@ -1679,19 +1725,13 @@
 void JavaThread::interrupt() {
   debug_only(check_for_dangling_thread_pointer(this);)
 
-  if (!osthread()->interrupted()) {
-    osthread()->set_interrupted(true);
-    // More than one thread can get here with the same value of osthread,
-    // resulting in multiple notifications.  We do, however, want the store
-    // to interrupted() to be visible to other threads before we execute unpark().
-    OrderAccess::fence();
-
-    // For JavaThread::sleep. Historically we only unpark if changing to the interrupted
-    // state, in contrast to the other events below. Not clear exactly why.
-    _SleepEvent->unpark();
-  }
-
-  // For JSR166. Unpark even if interrupt status already was set.
+  // For Windows _interrupt_event
+  osthread()->set_interrupted(true);
+
+  // For Thread.sleep
+  _SleepEvent->unpark();
+
+  // For JSR166 LockSupport.park
   parker()->unpark();
 
   // For ObjectMonitor and JvmtiRawMonitor
@@ -1701,11 +1741,11 @@
 
 bool JavaThread::is_interrupted(bool clear_interrupted) {
   debug_only(check_for_dangling_thread_pointer(this);)
-  bool interrupted = osthread()->interrupted();
+  bool interrupted = java_lang_Thread::interrupted(threadObj());
 
   // NOTE that since there is no "lock" around the interrupt and
   // is_interrupted operations, there is the possibility that the
-  // interrupted flag (in osThread) will be "false" but that the
+  // interrupted flag will be "false" but that the
   // low-level events will be in the signaled state. This is
   // intentional. The effect of this is that Object.wait() and
   // LockSupport.park() will appear to have a spurious wakeup, which
@@ -1715,9 +1755,12 @@
   // to JavaThread::sleep, so there is no early return. It has also been
   // recommended not to put the interrupted flag into the "event"
   // structure because it hides the issue.
+  // Also, because there is no lock, we must only clear the interrupt
+  // state if we are going to report that we were interrupted; otherwise
+  // an interrupt that happens just after we read the field would be lost.
   if (interrupted && clear_interrupted) {
+    java_lang_Thread::set_interrupted(threadObj(), false);
     osthread()->set_interrupted(false);
-    // consider thread->_SleepEvent->reset() ... optional optimization
   }
 
   return interrupted;
@@ -2371,6 +2414,7 @@
 
 
   // Interrupt thread so it will wake up from a potential wait()/sleep()/park()
+  java_lang_Thread::set_interrupted(threadObj(), true);
   this->interrupt();
 }
 
@@ -3656,9 +3700,11 @@
   // Phase 1 of the system initialization in the library, java.lang.System class initialization
   call_initPhase1(CHECK);
 
-  // get the Java runtime name after java.lang.System is initialized
+  // get the Java runtime name, version, and vendor info after java.lang.System is initialized
   JDK_Version::set_runtime_name(get_java_runtime_name(THREAD));
   JDK_Version::set_runtime_version(get_java_runtime_version(THREAD));
+  JDK_Version::set_runtime_vendor_version(get_java_runtime_vendor_version(THREAD));
+  JDK_Version::set_runtime_vendor_vm_bug_url(get_java_runtime_vendor_vm_bug_url(THREAD));
 
   // an instance of OutOfMemory exception has been allocated earlier
   initialize_class(vmSymbols::java_lang_OutOfMemoryError(), CHECK);
--- a/src/hotspot/share/runtime/vmStructs.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/runtime/vmStructs.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -786,7 +786,6 @@
   /* OSThread */                                                                                                                     \
   /************/                                                                                                                     \
                                                                                                                                      \
-  volatile_nonstatic_field(OSThread,           _interrupted,                                  jint)                                  \
   volatile_nonstatic_field(OSThread,           _state,                                        ThreadState)                           \
                                                                                                                                      \
   /************************/                                                                                                         \
@@ -2612,6 +2611,7 @@
   declare_constant(JVMFlag::ERGONOMIC)                                    \
   declare_constant(JVMFlag::ATTACH_ON_DEMAND)                             \
   declare_constant(JVMFlag::INTERNAL)                                     \
+  declare_constant(JVMFlag::JIMAGE_RESOURCE)                              \
   declare_constant(JVMFlag::VALUE_ORIGIN_MASK)                            \
   declare_constant(JVMFlag::ORIG_COMMAND_LINE)
 
--- a/src/hotspot/share/utilities/vmError.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/hotspot/share/utilities/vmError.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -128,9 +128,14 @@
 
 static void print_bug_submit_message(outputStream *out, Thread *thread) {
   if (out == NULL) return;
-  out->print_raw_cr("# If you would like to submit a bug report, please visit:");
-  out->print_raw   ("#   ");
-  out->print_raw_cr(Arguments::java_vendor_url_bug());
+  const char *url = Arguments::java_vendor_url_bug();
+  if (url == NULL || *url == '\0')
+    url = JDK_Version::runtime_vendor_vm_bug_url();
+  if (url != NULL && *url != '\0') {
+    out->print_raw_cr("# If you would like to submit a bug report, please visit:");
+    out->print_raw   ("#   ");
+    out->print_raw_cr(url);
+  }
   // If the crash is in native code, encourage user to submit a bug to the
   // provider of that code.
   if (thread && thread->is_Java_thread() &&
@@ -321,15 +326,19 @@
                                 JDK_Version::runtime_name() : "";
    const char* runtime_version = JDK_Version::runtime_version() != NULL ?
                                    JDK_Version::runtime_version() : "";
+   const char* vendor_version = JDK_Version::runtime_vendor_version() != NULL ?
+                                  JDK_Version::runtime_vendor_version() : "";
    const char* jdk_debug_level = VM_Version::printable_jdk_debug_level() != NULL ?
                                    VM_Version::printable_jdk_debug_level() : "";
 
-   st->print_cr("# JRE version: %s (%s) (%sbuild %s)", runtime_name, buf,
-                 jdk_debug_level, runtime_version);
+   st->print_cr("# JRE version: %s%s%s (%s) (%sbuild %s)", runtime_name,
+                (*vendor_version != '\0') ? " " : "", vendor_version,
+                buf, jdk_debug_level, runtime_version);
 
    // This is the long version with some default settings added
-   st->print_cr("# Java VM: %s (%s%s, %s%s%s%s%s, %s, %s)",
+   st->print_cr("# Java VM: %s%s%s (%s%s, %s%s%s%s%s, %s, %s)",
                  VM_Version::vm_name(),
+                (*vendor_version != '\0') ? " " : "", vendor_version,
                  jdk_debug_level,
                  VM_Version::vm_release(),
                  VM_Version::vm_info_string(),
--- a/src/java.base/share/classes/java/lang/Thread.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/lang/Thread.java	Mon Nov 04 11:25:55 2019 +0000
@@ -154,6 +154,9 @@
     /* Whether or not the thread is a daemon thread. */
     private boolean daemon = false;
 
+    /* Interrupt state of the thread - read/written directly by JVM */
+    private volatile boolean interrupted;
+
     /* Fields reserved for exclusive use by the JVM */
     private boolean stillborn = false;
     private long eetop;
@@ -971,10 +974,14 @@
      *
      * <p> Interrupting a thread that is not alive need not have any effect.
      *
+     * @implNote In the JDK Reference Implementation, interruption of a thread
+     * that is not alive still records that the interrupt request was made and
+     * will report it via {@link #interrupted} and {@link #isInterrupted()}.
+     *
      * @throws  SecurityException
      *          if the current thread cannot modify this thread
      *
-     * @revised 6.0
+     * @revised 6.0, 14
      * @spec JSR-51
      */
     public void interrupt() {
@@ -985,14 +992,15 @@
             synchronized (blockerLock) {
                 Interruptible b = blocker;
                 if (b != null) {
-                    interrupt0();  // set interrupt status
+                    interrupted = true;
+                    interrupt0();  // inform VM of interrupt
                     b.interrupt(this);
                     return;
                 }
             }
         }
-
-        // set interrupt status
+        interrupted = true;
+        // inform VM of interrupt
         interrupt0();
     }
 
@@ -1004,45 +1012,38 @@
      * interrupted again, after the first call had cleared its interrupted
      * status and before the second call had examined it).
      *
-     * <p>A thread interruption ignored because a thread was not alive
-     * at the time of the interrupt will be reflected by this method
-     * returning false.
-     *
      * @return  {@code true} if the current thread has been interrupted;
      *          {@code false} otherwise.
      * @see #isInterrupted()
-     * @revised 6.0
+     * @revised 6.0, 14
      */
     public static boolean interrupted() {
-        return currentThread().isInterrupted(true);
+        Thread t = currentThread();
+        boolean interrupted = t.interrupted;
+        // We may have been interrupted the moment after we read the field,
+        // so only clear the field if we saw that it was set and will return
+        // true; otherwise we could lose an interrupt.
+        if (interrupted) {
+            t.interrupted = false;
+            clearInterruptEvent();
+        }
+        return interrupted;
     }
 
     /**
      * Tests whether this thread has been interrupted.  The <i>interrupted
      * status</i> of the thread is unaffected by this method.
      *
-     * <p>A thread interruption ignored because a thread was not alive
-     * at the time of the interrupt will be reflected by this method
-     * returning false.
-     *
      * @return  {@code true} if this thread has been interrupted;
      *          {@code false} otherwise.
      * @see     #interrupted()
-     * @revised 6.0
+     * @revised 6.0, 14
      */
     public boolean isInterrupted() {
-        return isInterrupted(false);
+        return interrupted;
     }
 
     /**
-     * Tests if some Thread has been interrupted.  The interrupted state
-     * is reset or not based on the value of ClearInterrupted that is
-     * passed.
-     */
-    @HotSpotIntrinsicCandidate
-    private native boolean isInterrupted(boolean ClearInterrupted);
-
-    /**
      * Tests if this thread is alive. A thread is alive if it has
      * been started and has not yet died.
      *
@@ -1251,20 +1252,20 @@
     }
 
     /**
-     * Counts the number of stack frames in this thread. The thread must
-     * be suspended.
+     * Throws {@code UnsupportedOperationException}.
+     *
+     * @return     nothing
      *
-     * @return     the number of stack frames in this thread.
-     * @throws     IllegalThreadStateException  if this thread is not
-     *             suspended.
-     * @deprecated The definition of this call depends on {@link #suspend},
-     *             which is deprecated.  Further, the results of this call
-     *             were never well-defined.
+     * @deprecated This method was originally designed to count the number of
+     *             stack frames but the results were never well-defined and it
+     *             depended on thread-suspension.
      *             This method is subject to removal in a future version of Java SE.
      * @see        StackWalker
      */
     @Deprecated(since="1.2", forRemoval=true)
-    public native int countStackFrames();
+    public int countStackFrames() {
+        throw new UnsupportedOperationException();
+    }
 
     /**
      * Waits at most {@code millis} milliseconds for this thread to
@@ -2080,5 +2081,6 @@
     private native void suspend0();
     private native void resume0();
     private native void interrupt0();
+    private static native void clearInterruptEvent();
     private native void setNativeName(String name);
 }
--- a/src/java.base/share/classes/java/lang/VersionProps.java.template	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/lang/VersionProps.java.template	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -42,9 +42,11 @@
     private static final String java_version_date =
         "@@VERSION_DATE@@";
 
+    // This field is read by HotSpot
     private static final String java_runtime_name =
         "@@RUNTIME_NAME@@";
 
+    // This field is read by HotSpot
     private static final String java_runtime_version =
         "@@VERSION_STRING@@";
 
@@ -69,22 +71,26 @@
     private static final String CLASSFILE_MAJOR_MINOR =
         "@@VERSION_CLASSFILE_MAJOR@@.@@VERSION_CLASSFILE_MINOR@@";
 
-    private static final String VENDOR_VERSION_STRING =
-        "@@VENDOR_VERSION_STRING@@";
-
-    private static final String vendor_version =
-        (!VENDOR_VERSION_STRING.isEmpty()
-         ? " " + VENDOR_VERSION_STRING : "");
-
     private static final String VENDOR =
         "@@VENDOR@@";
 
     private static final String VENDOR_URL =
         "@@VENDOR_URL@@";
 
-    private static final String VENDOR_URL_BUG =
+    // The remaining VENDOR_* fields must not be final,
+    // so that they can be redefined by jlink plugins
+
+    // This field is read by HotSpot
+    private static String VENDOR_VERSION =
+        "@@VENDOR_VERSION_STRING@@";
+
+    private static String VENDOR_URL_BUG =
         "@@VENDOR_URL_BUG@@";
 
+    // This field is read by HotSpot
+    private static String VENDOR_URL_VM_BUG =
+        "@@VENDOR_URL_VM_BUG@@";
+
     /**
      * Initialize system properties using build provided values.
      *
@@ -95,8 +101,8 @@
         props.put("java.version.date", java_version_date);
         props.put("java.runtime.version", java_runtime_version);
         props.put("java.runtime.name", java_runtime_name);
-        if (!VENDOR_VERSION_STRING.isEmpty())
-            props.put("java.vendor.version", VENDOR_VERSION_STRING);
+        if (!VENDOR_VERSION.isEmpty())
+            props.put("java.vendor.version", VENDOR_VERSION);
 
         props.put("java.class.version", CLASSFILE_MAJOR_MINOR);
 
@@ -216,6 +222,9 @@
             jdk_debug_level = jdk_debug_level + " ";
         }
 
+        String vendor_version = (VENDOR_VERSION.isEmpty()
+                                 ? "" : " " + VENDOR_VERSION);
+
         ps.println(java_runtime_name + vendor_version
                    + " (" + jdk_debug_level + "build " + java_runtime_version + ")");
 
--- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Mon Nov 04 11:25:55 2019 +0000
@@ -29,6 +29,7 @@
 import sun.invoke.util.BytecodeDescriptor;
 import jdk.internal.misc.Unsafe;
 import sun.security.action.GetPropertyAction;
+import sun.security.action.GetBooleanAction;
 
 import java.io.FilePermission;
 import java.io.Serializable;
@@ -87,10 +88,15 @@
     // For dumping generated classes to disk, for debugging purposes
     private static final ProxyClassesDumper dumper;
 
+    private static final boolean disableEagerInitialization;
+
     static {
-        final String key = "jdk.internal.lambda.dumpProxyClasses";
-        String path = GetPropertyAction.privilegedGetProperty(key);
-        dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
+        final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
+        String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
+        dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);
+
+        final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
+        disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
     }
 
     // See context values in AbstractValidatingLambdaMetafactory
@@ -187,7 +193,9 @@
     @Override
     CallSite buildCallSite() throws LambdaConversionException {
         final Class<?> innerClass = spinInnerClass();
-        if (invokedType.parameterCount() == 0) {
+        if (invokedType.parameterCount() == 0 && !disableEagerInitialization) {
+            // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
+            // unless we've suppressed eager initialization
             final Constructor<?>[] ctrs = AccessController.doPrivileged(
                     new PrivilegedAction<>() {
                 @Override
@@ -215,7 +223,9 @@
             }
         } else {
             try {
-                UNSAFE.ensureClassInitialized(innerClass);
+                if (!disableEagerInitialization) {
+                    UNSAFE.ensureClassInitialized(innerClass);
+                }
                 return new ConstantCallSite(
                         MethodHandles.Lookup.IMPL_LOOKUP
                              .findStatic(innerClass, NAME_FACTORY, invokedType));
@@ -273,7 +283,7 @@
 
         generateConstructor();
 
-        if (invokedType.parameterCount() != 0) {
+        if (invokedType.parameterCount() != 0 || disableEagerInitialization) {
             generateFactory();
         }
 
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Mon Nov 04 11:25:55 2019 +0000
@@ -203,7 +203,7 @@
      * @param targetClass the target class
      * @param caller the caller lookup object
      * @return a lookup object for the target class, with private access
-     * @throws IllegalArgumentException if {@code targetClass} is a primitive type or array class
+     * @throws IllegalArgumentException if {@code targetClass} is a primitive type or void or array class
      * @throws NullPointerException if {@code targetClass} or {@code caller} is {@code null}
      * @throws SecurityException if denied by the security manager
      * @throws IllegalAccessException if any of the other access checks specified above fails
@@ -1385,7 +1385,7 @@
         private Lookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) {
             assert prevLookupClass == null || ((allowedModes & MODULE) == 0
                     && prevLookupClass.getModule() != lookupClass.getModule());
-
+            assert !lookupClass.isArray() && !lookupClass.isPrimitive();
             this.lookupClass = lookupClass;
             this.prevLookupClass = prevLookupClass;
             this.allowedModes = allowedModes;
@@ -1443,6 +1443,7 @@
          * @param requestedLookupClass the desired lookup class for the new lookup object
          * @return a lookup object which reports the desired lookup class, or the same object
          * if there is no change
+         * @throws IllegalArgumentException if {@code requestedLookupClass} is a primitive type or void or array class
          * @throws NullPointerException if the argument is null
          *
          * @revised 9
@@ -1452,6 +1453,11 @@
          */
         public Lookup in(Class<?> requestedLookupClass) {
             Objects.requireNonNull(requestedLookupClass);
+            if (requestedLookupClass.isPrimitive())
+                throw new IllegalArgumentException(requestedLookupClass + " is a primitive class");
+            if (requestedLookupClass.isArray())
+                throw new IllegalArgumentException(requestedLookupClass + " is an array class");
+
             if (allowedModes == TRUSTED)  // IMPL_LOOKUP can make any lookup at all
                 return new Lookup(requestedLookupClass, null, FULL_POWER_MODES);
             if (requestedLookupClass == this.lookupClass)
--- a/src/java.base/share/classes/java/net/DatagramSocket.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/net/DatagramSocket.java	Mon Nov 04 11:25:55 2019 +0000
@@ -606,7 +606,6 @@
      * @see #bind(SocketAddress)
      * @since 1.4
      */
-
     public SocketAddress getLocalSocketAddress() {
         if (isClosed())
             return null;
@@ -853,7 +852,7 @@
     public InetAddress getLocalAddress() {
         if (isClosed())
             return null;
-        InetAddress in = null;
+        InetAddress in;
         try {
             in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR);
             if (in.isAnyLocalAddress()) {
@@ -874,8 +873,8 @@
      * is bound.
      *
      * @return  the port number on the local host to which this socket is bound,
-                {@code -1} if the socket is closed, or
-                {@code 0} if it is not bound yet.
+     *          {@code -1} if the socket is closed, or
+     *          {@code 0} if it is not bound yet.
      */
     public int getLocalPort() {
         if (isClosed())
@@ -887,15 +886,16 @@
         }
     }
 
-    /** Enable/disable SO_TIMEOUT with the specified timeout, in
-     *  milliseconds. With this option set to a positive timeout value,
-     *  a call to receive() for this DatagramSocket
-     *  will block for only this amount of time.  If the timeout expires,
-     *  a <B>java.net.SocketTimeoutException</B> is raised, though the
-     *  DatagramSocket is still valid. A timeout of zero is interpreted
-     *  as an infinite timeout.
-     *  The option <B>must</B> be enabled prior to entering the blocking
-     *  operation to have effect.
+    /**
+     * Enable/disable SO_TIMEOUT with the specified timeout, in
+     * milliseconds. With this option set to a positive timeout value,
+     * a call to receive() for this DatagramSocket
+     * will block for only this amount of time.  If the timeout expires,
+     * a <B>java.net.SocketTimeoutException</B> is raised, though the
+     * DatagramSocket is still valid. A timeout of zero is interpreted
+     * as an infinite timeout.
+     * The option <B>must</B> be enabled prior to entering the blocking
+     * operation to have effect.
      *
      * @param timeout the specified timeout in milliseconds.
      * @throws SocketException if there is an error in the underlying protocol, such as an UDP error.
@@ -963,8 +963,7 @@
      * negative.
      * @see #getSendBufferSize()
      */
-    public synchronized void setSendBufferSize(int size)
-    throws SocketException{
+    public synchronized void setSendBufferSize(int size) throws SocketException {
         if (!(size > 0)) {
             throw new IllegalArgumentException("negative send size");
         }
@@ -1021,8 +1020,7 @@
      * negative.
      * @see #getReceiveBufferSize()
      */
-    public synchronized void setReceiveBufferSize(int size)
-    throws SocketException{
+    public synchronized void setReceiveBufferSize(int size) throws SocketException {
         if (size <= 0) {
             throw new IllegalArgumentException("invalid receive size");
         }
@@ -1039,8 +1037,7 @@
      * @throws    SocketException if there is an error in the underlying protocol, such as an UDP error.
      * @see #setReceiveBufferSize(int)
      */
-    public synchronized int getReceiveBufferSize()
-    throws SocketException{
+    public synchronized int getReceiveBufferSize() throws SocketException {
         if (isClosed())
             throw new SocketException("Socket is closed");
         int result = 0;
--- a/src/java.base/share/classes/java/net/HttpURLConnection.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/net/HttpURLConnection.java	Mon Nov 04 11:25:55 2019 +0000
@@ -618,8 +618,13 @@
 
     /**
      * Indicates if the connection is going through a proxy.
-     * @return a boolean indicating if the connection is
-     * using a proxy.
+     *
+     * This method returns {@code true} if the connection is known
+     * to be going or has gone through proxies, and returns {@code false}
+     * if the connection will never go through a proxy or if
+     * the use of a proxy cannot be determined.
+     *
+     * @return a boolean indicating if the connection is using a proxy.
      */
     public abstract boolean usingProxy();
 
--- a/src/java.base/share/classes/java/nio/file/FileStore.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/nio/file/FileStore.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2019, 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
@@ -84,7 +84,9 @@
     public abstract boolean isReadOnly();
 
     /**
-     * Returns the size, in bytes, of the file store.
+     * Returns the size, in bytes, of the file store. If the total number of
+     * bytes in the file store is greater than {@link Long#MAX_VALUE}, then
+     * {@code Long.MAX_VALUE} will be returned.
      *
      * @return  the size of the file store, in bytes
      *
@@ -95,7 +97,8 @@
 
     /**
      * Returns the number of bytes available to this Java virtual machine on the
-     * file store.
+     * file store.  If the number of bytes available is greater than
+     * {@link Long#MAX_VALUE}, then {@code Long.MAX_VALUE} will be returned.
      *
      * <p> The returned number of available bytes is a hint, but not a
      * guarantee, that it is possible to use most or any of these bytes.  The
@@ -112,14 +115,33 @@
     public abstract long getUsableSpace() throws IOException;
 
     /**
+     * Returns the number of unallocated bytes in the file store.
+     * If the number of unallocated bytes is greater than
+     * {@link Long#MAX_VALUE}, then {@code Long.MAX_VALUE} will be returned.
+     *
+     * <p> The returned number of unallocated bytes is a hint, but not a
+     * guarantee, that it is possible to use most or any of these bytes.  The
+     * number of unallocated bytes is most likely to be accurate immediately
+     * after the space attributes are obtained. It is likely to be
+     * made inaccurate by any external I/O operations including those made on
+     * the system outside of this virtual machine.
+     *
+     * @return  the number of unallocated bytes
+     *
+     * @throws  IOException
+     *          if an I/O error occurs
+     */
+    public abstract long getUnallocatedSpace() throws IOException;
+
+    /**
      * Returns the number of bytes per block in this file store.
      *
      * <p> File storage is typically organized into discrete sequences of bytes
      * called <i>blocks</i>. A block is the smallest storage unit of a file store.
      * Every read and write operation is performed on a multiple of blocks.
      *
-     * @implSpec The implementation in this class throws an
-     *         {@code UnsupportedOperationException}.
+     * @implSpec The implementation in this class throws
+     *           {@code UnsupportedOperationException}.
      *
      * @return  a positive value representing the block size of this file store,
      *          in bytes
@@ -137,23 +159,6 @@
     }
 
     /**
-     * Returns the number of unallocated bytes in the file store.
-     *
-     * <p> The returned number of unallocated bytes is a hint, but not a
-     * guarantee, that it is possible to use most or any of these bytes.  The
-     * number of unallocated bytes is most likely to be accurate immediately
-     * after the space attributes are obtained. It is likely to be
-     * made inaccurate by any external I/O operations including those made on
-     * the system outside of this virtual machine.
-     *
-     * @return  the number of unallocated bytes
-     *
-     * @throws  IOException
-     *          if an I/O error occurs
-     */
-    public abstract long getUnallocatedSpace() throws IOException;
-
-    /**
      * Tells whether or not this file store supports the file attributes
      * identified by the given file attribute view.
      *
--- a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java	Mon Nov 04 11:25:55 2019 +0000
@@ -623,8 +623,8 @@
         final CompletableFuture<V> tryFire(int mode) {
             CompletableFuture<V> d; CompletableFuture<T> a;
             Object r; Throwable x; Function<? super T,? extends V> f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null)
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null || (f = fn) == null)
                 return null;
             tryComplete: if (d.result == null) {
                 if (r instanceof AltResult) {
@@ -645,7 +645,7 @@
                     d.completeThrowable(ex);
                 }
             }
-            dep = null; src = null; fn = null;
+            src = null; dep = null; fn = null;
             return d.postFire(a, mode);
         }
     }
@@ -695,8 +695,8 @@
         final CompletableFuture<Void> tryFire(int mode) {
             CompletableFuture<Void> d; CompletableFuture<T> a;
             Object r; Throwable x; Consumer<? super T> f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null)
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null || (f = fn) == null)
                 return null;
             tryComplete: if (d.result == null) {
                 if (r instanceof AltResult) {
@@ -718,7 +718,7 @@
                     d.completeThrowable(ex);
                 }
             }
-            dep = null; src = null; fn = null;
+            src = null; dep = null; fn = null;
             return d.postFire(a, mode);
         }
     }
@@ -769,8 +769,8 @@
         final CompletableFuture<Void> tryFire(int mode) {
             CompletableFuture<Void> d; CompletableFuture<T> a;
             Object r; Throwable x; Runnable f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null)
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null || (f = fn) == null)
                 return null;
             if (d.result == null) {
                 if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
@@ -787,7 +787,7 @@
                         d.completeThrowable(ex);
                     }
             }
-            dep = null; src = null; fn = null;
+            src = null; dep = null; fn = null;
             return d.postFire(a, mode);
         }
     }
@@ -832,11 +832,11 @@
         final CompletableFuture<T> tryFire(int mode) {
             CompletableFuture<T> d; CompletableFuture<T> a;
             Object r; BiConsumer<? super T, ? super Throwable> f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null || (f = fn) == null
                 || !d.uniWhenComplete(r, f, mode > 0 ? null : this))
                 return null;
-            dep = null; src = null; fn = null;
+            src = null; dep = null; fn = null;
             return d.postFire(a, mode);
         }
     }
@@ -902,11 +902,11 @@
         final CompletableFuture<V> tryFire(int mode) {
             CompletableFuture<V> d; CompletableFuture<T> a;
             Object r; BiFunction<? super T, Throwable, ? extends V> f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null || (f = fn) == null
                 || !d.uniHandle(r, f, mode > 0 ? null : this))
                 return null;
-            dep = null; src = null; fn = null;
+            src = null; dep = null; fn = null;
             return d.postFire(a, mode);
         }
     }
@@ -965,11 +965,11 @@
         final CompletableFuture<T> tryFire(int mode) {
             CompletableFuture<T> d; CompletableFuture<T> a;
             Object r; Function<? super Throwable, ? extends T> f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null || (f = fn) == null
                 || !d.uniExceptionally(r, f, mode > 0 ? null : this))
                 return null;
-            dep = null; src = null; fn = null;
+            src = null; dep = null; fn = null;
             return d.postFire(a, mode);
         }
     }
@@ -1024,8 +1024,8 @@
             CompletableFuture<T> d; CompletableFuture<T> a;
             Function<Throwable, ? extends CompletionStage<T>> f;
             Object r; Throwable x;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null)
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null || (f = fn) == null)
                 return null;
             if (d.result == null) {
                 if ((r instanceof AltResult) &&
@@ -1048,7 +1048,7 @@
                 else
                     d.internalComplete(r);
             }
-            dep = null; src = null; fn = null;
+            src = null; dep = null; fn = null;
             return d.postFire(a, mode);
         }
     }
@@ -1086,8 +1086,8 @@
         }
         final CompletableFuture<U> tryFire(int mode) {
             CompletableFuture<U> d; CompletableFuture<T> a; Object r;
-            if ((d = dep) == null
-                || (a = src) == null || (r = a.result) == null)
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null)
                 return null;
             if (d.result == null)
                 d.completeRelay(r);
@@ -1128,8 +1128,8 @@
             CompletableFuture<V> d; CompletableFuture<T> a;
             Function<? super T, ? extends CompletionStage<V>> f;
             Object r; Throwable x;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null)
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null || (f = fn) == null)
                 return null;
             tryComplete: if (d.result == null) {
                 if (r instanceof AltResult) {
@@ -1155,7 +1155,7 @@
                     d.completeThrowable(ex);
                 }
             }
-            dep = null; src = null; fn = null;
+            src = null; dep = null; fn = null;
             return d.postFire(a, mode);
         }
     }
@@ -1270,12 +1270,12 @@
             CompletableFuture<T> a;
             CompletableFuture<U> b;
             Object r, s; BiFunction<? super T,? super U,? extends V> f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null
+            if (   (a = src) == null || (r = a.result) == null
                 || (b = snd) == null || (s = b.result) == null
+                || (d = dep) == null || (f = fn) == null
                 || !d.biApply(r, s, f, mode > 0 ? null : this))
                 return null;
-            dep = null; src = null; snd = null; fn = null;
+            src = null; snd = null; dep = null; fn = null;
             return d.postFire(a, b, mode);
         }
     }
@@ -1345,12 +1345,12 @@
             CompletableFuture<T> a;
             CompletableFuture<U> b;
             Object r, s; BiConsumer<? super T,? super U> f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null
+            if (   (a = src) == null || (r = a.result) == null
                 || (b = snd) == null || (s = b.result) == null
+                || (d = dep) == null || (f = fn) == null
                 || !d.biAccept(r, s, f, mode > 0 ? null : this))
                 return null;
-            dep = null; src = null; snd = null; fn = null;
+            src = null; snd = null; dep = null; fn = null;
             return d.postFire(a, b, mode);
         }
     }
@@ -1421,12 +1421,12 @@
             CompletableFuture<T> a;
             CompletableFuture<U> b;
             Object r, s; Runnable f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (r = a.result) == null
+            if (   (a = src) == null || (r = a.result) == null
                 || (b = snd) == null || (s = b.result) == null
+                || (d = dep) == null || (f = fn) == null
                 || !d.biRun(r, s, f, mode > 0 ? null : this))
                 return null;
-            dep = null; src = null; snd = null; fn = null;
+            src = null; snd = null; dep = null; fn = null;
             return d.postFire(a, b, mode);
         }
     }
@@ -1482,9 +1482,9 @@
             CompletableFuture<T> a;
             CompletableFuture<U> b;
             Object r, s, z; Throwable x;
-            if ((d = dep) == null
-                || (a = src) == null || (r = a.result) == null
-                || (b = snd) == null || (s = b.result) == null)
+            if (   (a = src) == null || (r = a.result) == null
+                || (b = snd) == null || (s = b.result) == null
+                || (d = dep) == null)
                 return null;
             if (d.result == null) {
                 if ((r instanceof AltResult
@@ -1557,13 +1557,11 @@
             super(executor, dep, src, snd); this.fn = fn;
         }
         final CompletableFuture<V> tryFire(int mode) {
-            CompletableFuture<V> d;
-            CompletableFuture<T> a;
-            CompletableFuture<U> b;
+            CompletableFuture<V> d; CompletableFuture<? extends T> a, b;
             Object r; Throwable x; Function<? super T,? extends V> f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (b = snd) == null
-                || ((r = a.result) == null && (r = b.result) == null))
+            if ((a = src) == null || (b = snd) == null
+                || ((r = a.result) == null && (r = b.result) == null)
+                || (d = dep) == null || (f = fn) == null)
                 return null;
             tryComplete: if (d.result == null) {
                 try {
@@ -1582,7 +1580,7 @@
                     d.completeThrowable(ex);
                 }
             }
-            dep = null; src = null; snd = null; fn = null;
+            src = null; snd = null; dep = null; fn = null;
             return d.postFire(a, b, mode);
         }
     }
@@ -1612,13 +1610,11 @@
             super(executor, dep, src, snd); this.fn = fn;
         }
         final CompletableFuture<Void> tryFire(int mode) {
-            CompletableFuture<Void> d;
-            CompletableFuture<T> a;
-            CompletableFuture<U> b;
+            CompletableFuture<Void> d; CompletableFuture<? extends T> a, b;
             Object r; Throwable x; Consumer<? super T> f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (b = snd) == null
-                || ((r = a.result) == null && (r = b.result) == null))
+            if ((a = src) == null || (b = snd) == null
+                || ((r = a.result) == null && (r = b.result) == null)
+                || (d = dep) == null || (f = fn) == null)
                 return null;
             tryComplete: if (d.result == null) {
                 try {
@@ -1638,7 +1634,7 @@
                     d.completeThrowable(ex);
                 }
             }
-            dep = null; src = null; snd = null; fn = null;
+            src = null; snd = null; dep = null; fn = null;
             return d.postFire(a, b, mode);
         }
     }
@@ -1668,13 +1664,11 @@
             super(executor, dep, src, snd); this.fn = fn;
         }
         final CompletableFuture<Void> tryFire(int mode) {
-            CompletableFuture<Void> d;
-            CompletableFuture<T> a;
-            CompletableFuture<U> b;
+            CompletableFuture<Void> d; CompletableFuture<?> a, b;
             Object r; Throwable x; Runnable f;
-            if ((d = dep) == null || (f = fn) == null
-                || (a = src) == null || (b = snd) == null
-                || ((r = a.result) == null && (r = b.result) == null))
+            if ((a = src) == null || (b = snd) == null
+                || ((r = a.result) == null && (r = b.result) == null)
+                || (d = dep) == null || (f = fn) == null)
                 return null;
             if (d.result == null) {
                 try {
@@ -1691,7 +1685,7 @@
                     d.completeThrowable(ex);
                 }
             }
-            dep = null; src = null; snd = null; fn = null;
+            src = null; snd = null; dep = null; fn = null;
             return d.postFire(a, b, mode);
         }
     }
@@ -1726,11 +1720,10 @@
             CompletableFuture<Object> d; CompletableFuture<?> a;
             CompletableFuture<?>[] as;
             Object r;
-            if ((d = dep) == null
-                || (a = src) == null || (r = a.result) == null
-                || (as = srcs) == null)
+            if ((a = src) == null || (r = a.result) == null
+                || (d = dep) == null || (as = srcs) == null)
                 return null;
-            dep = null; src = null; srcs = null;
+            src = null; dep = null; srcs = null;
             if (d.completeRelay(r)) {
                 for (CompletableFuture<?> b : as)
                     if (b != a)
--- a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1667,11 +1667,14 @@
      * If the specified key is not already associated with a value,
      * attempts to compute its value using the given mapping function
      * and enters it into this map unless {@code null}.  The entire
-     * method invocation is performed atomically, so the function is
-     * applied at most once per key.  Some attempted update operations
-     * on this map by other threads may be blocked while computation
-     * is in progress, so the computation should be short and simple,
-     * and must not attempt to update any other mappings of this map.
+     * method invocation is performed atomically.  The supplied
+     * function is invoked exactly once per invocation of this method
+     * if the key is absent, else not at all.  Some attempted update
+     * operations on this map by other threads may be blocked while
+     * computation is in progress, so the computation should be short
+     * and simple.
+     *
+     * <p>The mapping function must not modify this map during computation.
      *
      * @param key key with which the specified value is to be associated
      * @param mappingFunction the function to compute a value
@@ -1778,10 +1781,13 @@
      * If the value for the specified key is present, attempts to
      * compute a new mapping given the key and its current mapped
      * value.  The entire method invocation is performed atomically.
-     * Some attempted update operations on this map by other threads
-     * may be blocked while computation is in progress, so the
-     * computation should be short and simple, and must not attempt to
-     * update any other mappings of this map.
+     * The supplied function is invoked exactly once per invocation of
+     * this method if the key is present, else not at all.  Some
+     * attempted update operations on this map by other threads may be
+     * blocked while computation is in progress, so the computation
+     * should be short and simple.
+     *
+     * <p>The remapping function must not modify this map during computation.
      *
      * @param key key with which a value may be associated
      * @param remappingFunction the function to compute a value
@@ -1870,10 +1876,12 @@
      * Attempts to compute a mapping for the specified key and its
      * current mapped value (or {@code null} if there is no current
      * mapping). The entire method invocation is performed atomically.
-     * Some attempted update operations on this map by other threads
-     * may be blocked while computation is in progress, so the
-     * computation should be short and simple, and must not attempt to
-     * update any other mappings of this Map.
+     * The supplied function is invoked exactly once per invocation of
+     * this method.  Some attempted update operations on this map by
+     * other threads may be blocked while computation is in progress,
+     * so the computation should be short and simple.
+     *
+     * <p>The remapping function must not modify this map during computation.
      *
      * @param key key with which the specified value is to be associated
      * @param remappingFunction the function to compute a value
--- a/src/java.base/share/classes/java/util/zip/ZipFile.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/java/util/zip/ZipFile.java	Mon Nov 04 11:25:55 2019 +0000
@@ -867,6 +867,7 @@
     private class ZipFileInputStream extends InputStream {
         private volatile boolean closeRequested;
         private   long pos;     // current position within entry data
+        private   long startingPos; // Start position for the entry data
         protected long rem;     // number of remaining bytes within entry
         protected long size;    // uncompressed size of this entry
 
@@ -938,6 +939,7 @@
                     throw new ZipException("ZipFile invalid LOC header (bad signature)");
                 }
                 pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
+                startingPos = pos; // Save starting position for the entry
             }
             return pos;
         }
@@ -979,8 +981,19 @@
         public long skip(long n) throws IOException {
             synchronized (ZipFile.this) {
                 initDataOffset();
-                if (n > rem) {
-                    n = rem;
+                long newPos = pos + n;
+                if (n > 0) {
+                    // If we overflowed adding the skip value or are moving
+                    // past EOF, set the skip value to number of bytes remaining
+                    // to reach EOF
+                    if (newPos < 0 || n > rem) {
+                        n = rem;
+                    }
+                } else if (newPos < startingPos) {
+                    // Tried to position before BOF so set position to the
+                    // BOF and return the number of bytes we moved backwards
+                    // to reach BOF
+                    n = startingPos - pos;
                 }
                 pos += n;
                 rem -= n;
--- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Mon Nov 04 11:25:55 2019 +0000
@@ -312,6 +312,8 @@
     private CookieHandler cookieHandler;
     private final ResponseCache cacheHandler;
 
+    private volatile boolean usingProxy;
+
     // the cached response, and cached response headers and body
     protected CacheResponse cachedResponse;
     private MessageHeader cachedHeaders;
@@ -320,7 +322,6 @@
     /* output stream to server */
     protected PrintStream ps = null;
 
-
     /* buffered error stream */
     private InputStream errorStream = null;
 
@@ -1240,6 +1241,7 @@
                 }
             }
 
+            usingProxy = usingProxy || usingProxyInternal();
             ps = (PrintStream)http.getOutputStream();
         } catch (IOException e) {
             throw e;
@@ -2917,7 +2919,7 @@
      * closed the connection to the web server.
      */
     private void disconnectWeb() throws IOException {
-        if (usingProxy() && http.isKeepingAlive()) {
+        if (usingProxyInternal() && http.isKeepingAlive()) {
             responseCode = -1;
             // clean up, particularly, skip the content part
             // of a 401 error response
@@ -3020,13 +3022,31 @@
         }
     }
 
-    public boolean usingProxy() {
+    /**
+     * Returns true only if the established connection is using a proxy
+     */
+    boolean usingProxyInternal() {
         if (http != null) {
             return (http.getProxyHostUsed() != null);
         }
         return false;
     }
 
+    /**
+     * Returns true if the established connection is using a proxy
+     * or if a proxy is specified for the inactive connection
+     */
+    @Override
+    public boolean usingProxy() {
+        if (usingProxy || usingProxyInternal())
+            return true;
+
+        if (instProxy != null)
+            return instProxy.type().equals(Proxy.Type.HTTP);
+
+        return false;
+    }
+
     // constant strings represent set-cookie header names
     private static final String SET_COOKIE = "set-cookie";
     private static final String SET_COOKIE2 = "set-cookie2";
--- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -28,6 +28,8 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
 import java.lang.ref.Cleaner.Cleanable;
 import java.net.DatagramSocket;
 import java.net.Inet4Address;
@@ -39,6 +41,7 @@
 import java.net.ProtocolFamily;
 import java.net.SocketAddress;
 import java.net.SocketOption;
+import java.net.SocketTimeoutException;
 import java.net.StandardProtocolFamily;
 import java.net.StandardSocketOptions;
 import java.nio.ByteBuffer;
@@ -47,6 +50,7 @@
 import java.nio.channels.AsynchronousCloseException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.DatagramChannel;
+import java.nio.channels.IllegalBlockingModeException;
 import java.nio.channels.MembershipKey;
 import java.nio.channels.NotYetConnectedException;
 import java.nio.channels.SelectionKey;
@@ -113,8 +117,17 @@
     private InetSocketAddress localAddress;
     private InetSocketAddress remoteAddress;
 
-    // Our socket adaptor, if any
-    private DatagramSocket socket;
+    // Socket adaptor, created lazily
+    private static final VarHandle SOCKET;
+    static {
+        try {
+            MethodHandles.Lookup l = MethodHandles.lookup();
+            SOCKET = l.findVarHandle(DatagramChannelImpl.class, "socket", DatagramSocket.class);
+        } catch (Exception e) {
+            throw new InternalError(e);
+        }
+    }
+    private volatile DatagramSocket socket;
 
     // Multicast support
     private MembershipRegistry registry;
@@ -199,11 +212,14 @@
 
     @Override
     public DatagramSocket socket() {
-        synchronized (stateLock) {
-            if (socket == null)
-                socket = DatagramSocketAdaptor.create(this);
-            return socket;
+        DatagramSocket socket = this.socket;
+        if (socket == null) {
+            socket = DatagramSocketAdaptor.create(this);
+            if (!SOCKET.compareAndSet(this, null, socket)) {
+                socket = this.socket;
+            }
         }
+        return socket;
     }
 
     @Override
@@ -408,62 +424,35 @@
     public SocketAddress receive(ByteBuffer dst) throws IOException {
         if (dst.isReadOnly())
             throw new IllegalArgumentException("Read-only buffer");
-
         readLock.lock();
         try {
             boolean blocking = isBlocking();
+            boolean completed = false;
             int n = 0;
-            ByteBuffer bb = null;
             try {
                 SocketAddress remote = beginRead(blocking, false);
                 boolean connected = (remote != null);
                 SecurityManager sm = System.getSecurityManager();
+
                 if (connected || (sm == null)) {
                     // connected or no security manager
-                    n = receive(fd, dst, connected);
+                    n = receive(dst, connected);
                     if (blocking) {
                         while (IOStatus.okayToRetry(n) && isOpen()) {
                             park(Net.POLLIN);
-                            n = receive(fd, dst, connected);
+                            n = receive(dst, connected);
                         }
-                    } else if (n == IOStatus.UNAVAILABLE) {
-                        return null;
                     }
                 } else {
-                    // Cannot receive into user's buffer when running with a
-                    // security manager and not connected
-                    bb = Util.getTemporaryDirectBuffer(dst.remaining());
-                    for (;;) {
-                        n = receive(fd, bb, connected);
-                        if (blocking) {
-                            while (IOStatus.okayToRetry(n) && isOpen()) {
-                                park(Net.POLLIN);
-                                n = receive(fd, bb, connected);
-                            }
-                        } else if (n == IOStatus.UNAVAILABLE) {
-                            return null;
-                        }
-                        InetSocketAddress isa = (InetSocketAddress)sender;
-                        try {
-                            sm.checkAccept(isa.getAddress().getHostAddress(),
-                                           isa.getPort());
-                        } catch (SecurityException se) {
-                            // Ignore packet
-                            bb.clear();
-                            n = 0;
-                            continue;
-                        }
-                        bb.flip();
-                        dst.put(bb);
-                        break;
-                    }
+                    // security manager and unconnected
+                    n = untrustedReceive(dst);
                 }
-                assert sender != null;
+                if (n == IOStatus.UNAVAILABLE)
+                    return null;
+                completed = (n > 0) || (n == 0 && isOpen());
                 return sender;
             } finally {
-                if (bb != null)
-                    Util.releaseTemporaryDirectBuffer(bb);
-                endRead(blocking, n > 0);
+                endRead(blocking, completed);
                 assert IOStatus.check(n);
             }
         } finally {
@@ -471,15 +460,164 @@
         }
     }
 
-    private int receive(FileDescriptor fd, ByteBuffer dst, boolean connected)
+    /**
+     * Receives a datagram into an untrusted buffer. When there is a security
+     * manager set, and the socket is not connected, datagrams have to be received
+     * into a buffer that is not accessible to the user. The datagram is copied
+     * into the user's buffer when the sender address is accepted by the security
+     * manager.
+     *
+     * @return the size of the datagram or IOStatus.UNAVAILABLE
+     */
+    private int untrustedReceive(ByteBuffer dst) throws IOException {
+        SecurityManager sm = System.getSecurityManager();
+        assert readLock.isHeldByCurrentThread()
+                && sm != null && remoteAddress == null;
+
+        ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());
+        try {
+            boolean blocking = isBlocking();
+            for (;;) {
+                int n = receive(bb, false);
+                if (blocking) {
+                    while (IOStatus.okayToRetry(n) && isOpen()) {
+                        park(Net.POLLIN);
+                        n = receive(bb, false);
+                    }
+                } else if (n == IOStatus.UNAVAILABLE) {
+                    return n;
+                }
+                InetSocketAddress isa = (InetSocketAddress) sender;
+                try {
+                    sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
+                    bb.flip();
+                    dst.put(bb);
+                    return n;
+                } catch (SecurityException se) {
+                    // ignore datagram
+                    bb.clear();
+                }
+            }
+        } finally {
+            Util.releaseTemporaryDirectBuffer(bb);
+        }
+    }
+
+    /**
+     * Receives a datagram into the given buffer.
+     *
+     * @apiNote This method is for use by the socket adaptor. The buffer is
+     * assumed to be trusted, meaning it is not accessible to user code.
+     *
+     * @throws IllegalBlockingModeException if the channel is non-blocking
+     * @throws SocketTimeoutException if the timeout elapses
+     */
+    SocketAddress blockingReceive(ByteBuffer dst, long nanos) throws IOException {
+        readLock.lock();
+        try {
+            ensureOpen();
+            if (!isBlocking())
+                throw new IllegalBlockingModeException();
+            SecurityManager sm = System.getSecurityManager();
+            boolean connected = isConnected();
+            SocketAddress sender;
+            do {
+                if (nanos > 0) {
+                    sender = trustedBlockingReceive(dst, nanos);
+                } else {
+                    sender = trustedBlockingReceive(dst);
+                }
+                // check sender when security manager set and not connected
+                if (sm != null && !connected) {
+                    InetSocketAddress isa = (InetSocketAddress) sender;
+                    try {
+                        sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
+                    } catch (SecurityException e) {
+                        sender = null;
+                    }
+                }
+            } while (sender == null);
+            return sender;
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    /**
+     * Receives a datagram into given buffer. This method is used to support
+     * the socket adaptor. The buffer is assumed to be trusted.
+     * @throws SocketTimeoutException if the timeout elapses
+     */
+    private SocketAddress trustedBlockingReceive(ByteBuffer dst)
         throws IOException
     {
+        assert readLock.isHeldByCurrentThread() && isBlocking();
+        boolean completed = false;
+        int n = 0;
+        try {
+            SocketAddress remote = beginRead(true, false);
+            boolean connected = (remote != null);
+            n = receive(dst, connected);
+            while (n == IOStatus.UNAVAILABLE && isOpen()) {
+                park(Net.POLLIN);
+                n = receive(dst, connected);
+            }
+            completed = (n > 0) || (n == 0 && isOpen());
+            return sender;
+        } finally {
+            endRead(true, completed);
+            assert IOStatus.check(n);
+        }
+    }
+
+    /**
+     * Receives a datagram into given buffer with a timeout. This method is
+     * used to support the socket adaptor. The buffer is assumed to be trusted.
+     * @throws SocketTimeoutException if the timeout elapses
+     */
+    private SocketAddress trustedBlockingReceive(ByteBuffer dst, long nanos)
+        throws IOException
+    {
+        assert readLock.isHeldByCurrentThread() && isBlocking();
+        boolean completed = false;
+        int n = 0;
+        try {
+            SocketAddress remote = beginRead(true, false);
+            boolean connected = (remote != null);
+
+            // change socket to non-blocking
+            lockedConfigureBlocking(false);
+            try {
+                long startNanos = System.nanoTime();
+                n = receive(dst, connected);
+                while (n == IOStatus.UNAVAILABLE && isOpen()) {
+                    long remainingNanos = nanos - (System.nanoTime() - startNanos);
+                    if (remainingNanos <= 0) {
+                        throw new SocketTimeoutException("Receive timed out");
+                    }
+                    park(Net.POLLIN, remainingNanos);
+                    n = receive(dst, connected);
+                }
+                completed = (n > 0) || (n == 0 && isOpen());
+                return sender;
+            } finally {
+                // restore socket to blocking mode (if channel is open)
+                tryLockedConfigureBlocking(true);
+            }
+
+        } finally {
+            endRead(true, completed);
+            assert IOStatus.check(n);
+        }
+    }
+
+    private int receive(ByteBuffer dst, boolean connected) throws IOException {
         int pos = dst.position();
         int lim = dst.limit();
         assert (pos <= lim);
         int rem = (pos <= lim ? lim - pos : 0);
         if (dst instanceof DirectBuffer && rem > 0)
-            return receiveIntoNativeBuffer(fd, dst, rem, pos, connected);
+            return receiveIntoNativeBuffer(dst, rem, pos, connected);
 
         // Substitute a native buffer. If the supplied buffer is empty
         // we must instead use a nonempty buffer, otherwise the call
@@ -487,7 +625,7 @@
         int newSize = Math.max(rem, 1);
         ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
         try {
-            int n = receiveIntoNativeBuffer(fd, bb, newSize, 0, connected);
+            int n = receiveIntoNativeBuffer(bb, newSize, 0, connected);
             bb.flip();
             if (n > 0 && rem > 0)
                 dst.put(bb);
@@ -497,8 +635,8 @@
         }
     }
 
-    private int receiveIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb,
-                                        int rem, int pos, boolean connected)
+    private int receiveIntoNativeBuffer(ByteBuffer bb, int rem, int pos,
+                                        boolean connected)
         throws IOException
     {
         int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem, connected);
@@ -563,6 +701,25 @@
         }
     }
 
+    /**
+     * Sends a datagram from the bytes in given buffer.
+     *
+     * @apiNote This method is for use by the socket adaptor.
+     *
+     * @throws IllegalBlockingModeException if the channel is non-blocking
+     */
+    void blockingSend(ByteBuffer src, SocketAddress target) throws IOException {
+        writeLock.lock();
+        try {
+            ensureOpen();
+            if (!isBlocking())
+                throw new IllegalBlockingModeException();
+            send(src, target);
+        } finally {
+            writeLock.unlock();
+        }
+    }
+
     private int send(FileDescriptor fd, ByteBuffer src, InetSocketAddress target)
         throws IOException
     {
@@ -785,10 +942,7 @@
         try {
             writeLock.lock();
             try {
-                synchronized (stateLock) {
-                    ensureOpen();
-                    IOUtil.configureBlocking(fd, block);
-                }
+                lockedConfigureBlocking(block);
             } finally {
                 writeLock.unlock();
             }
@@ -797,6 +951,36 @@
         }
     }
 
+    /**
+     * Adjusts the blocking mode. readLock or writeLock must already be held.
+     */
+    private void lockedConfigureBlocking(boolean block) throws IOException {
+        assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
+        synchronized (stateLock) {
+            ensureOpen();
+            IOUtil.configureBlocking(fd, block);
+        }
+    }
+
+    /**
+     * Adjusts the blocking mode if the channel is open. readLock or writeLock
+     * must already be held.
+     *
+     * @return {@code true} if the blocking mode was adjusted, {@code false} if
+     *         the blocking mode was not adjusted because the channel is closed
+     */
+    private boolean tryLockedConfigureBlocking(boolean block) throws IOException {
+        assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
+        synchronized (stateLock) {
+            if (isOpen()) {
+                IOUtil.configureBlocking(fd, block);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
     InetSocketAddress localAddress() {
         synchronized (stateLock) {
             return localAddress;
@@ -861,6 +1045,16 @@
 
     @Override
     public DatagramChannel connect(SocketAddress sa) throws IOException {
+        return connect(sa, true);
+    }
+
+    /**
+     * Connects the channel's socket.
+     *
+     * @param sa the remote address to which this channel is to be connected
+     * @param check true to check if the channel is already connected.
+     */
+    DatagramChannel connect(SocketAddress sa, boolean check) throws IOException {
         InetSocketAddress isa = Net.checkAddress(sa, family);
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
@@ -879,7 +1073,7 @@
             try {
                 synchronized (stateLock) {
                     ensureOpen();
-                    if (state == ST_CONNECTED)
+                    if (check && state == ST_CONNECTED)
                         throw new AlreadyConnectedException();
 
                     // ensure that the socket is bound
@@ -908,7 +1102,7 @@
                     }
                     try {
                         ByteBuffer buf = ByteBuffer.allocate(100);
-                        while (receive(fd, buf, false) > 0) {
+                        while (receive(buf, false) >= 0) {
                             buf.clear();
                         }
                     } finally {
@@ -1332,30 +1526,6 @@
     }
 
     /**
-     * Poll this channel's socket for reading up to the given timeout.
-     * @return {@code true} if the socket is polled
-     */
-    boolean pollRead(long timeout) throws IOException {
-        boolean blocking = isBlocking();
-        assert Thread.holdsLock(blockingLock()) && blocking;
-
-        readLock.lock();
-        try {
-            boolean polled = false;
-            try {
-                beginRead(blocking, false);
-                int events = Net.poll(fd, Net.POLLIN, timeout);
-                polled = (events != 0);
-            } finally {
-                endRead(blocking, polled);
-            }
-            return polled;
-        } finally {
-            readLock.unlock();
-        }
-    }
-
-    /**
      * Translates an interest operation set into a native poll event set
      */
     public int translateInterestOps(int ops) {
--- a/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Mon Nov 04 11:25:55 2019 +0000
@@ -26,6 +26,9 @@
 package sun.nio.ch;
 
 import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.VarHandle;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.DatagramSocketImpl;
@@ -35,15 +38,16 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.net.SocketOption;
-import java.net.SocketTimeoutException;
 import java.net.StandardSocketOptions;
 import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyConnectedException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.DatagramChannel;
-import java.nio.channels.IllegalBlockingModeException;
-import java.util.Objects;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Set;
 
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
 // Make a datagram-socket channel look like a datagram socket.
 //
@@ -61,13 +65,9 @@
     // Timeout "option" value for receives
     private volatile int timeout;
 
-    // ## super will create a useless impl
+    // create DatagramSocket with useless impl
     private DatagramSocketAdaptor(DatagramChannelImpl dc) {
-        // Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
-        // passing a dummy DatagramSocketImpl object to avoid any native
-        // resource allocation in super class and invoking our bind method
-        // before the dc field is initialized.
-        super(dummyDatagramSocket);
+        super(new DummyDatagramSocketImpl());
         this.dc = dc;
     }
 
@@ -75,17 +75,9 @@
         return new DatagramSocketAdaptor(dc);
     }
 
-    private void connectInternal(SocketAddress remote)
-        throws SocketException
-    {
-        InetSocketAddress isa = Net.asInetSocketAddress(remote);
-        int port = isa.getPort();
-        if (port < 0 || port > 0xFFFF)
-            throw new IllegalArgumentException("connect: " + port);
-        if (remote == null)
-            throw new IllegalArgumentException("connect: null address");
+    private void connectInternal(SocketAddress remote) throws SocketException {
         try {
-            dc.connect(remote);
+            dc.connect(remote, false); // skips check for already connected
         } catch (ClosedChannelException e) {
             // ignore
         } catch (Exception x) {
@@ -95,9 +87,12 @@
 
     @Override
     public void bind(SocketAddress local) throws SocketException {
+        if (local != null) {
+            local = Net.asInetSocketAddress(local);
+        } else {
+            local = new InetSocketAddress(0);
+        }
         try {
-            if (local == null)
-                local = new InetSocketAddress(0);
             dc.bind(local);
         } catch (Exception x) {
             Net.translateToSocketException(x);
@@ -106,17 +101,20 @@
 
     @Override
     public void connect(InetAddress address, int port) {
+        if (address == null)
+            throw new IllegalArgumentException("Address can't be null");
         try {
             connectInternal(new InetSocketAddress(address, port));
         } catch (SocketException x) {
-            // Yes, j.n.DatagramSocket really does this
+            throw new Error(x);
         }
     }
 
     @Override
     public void connect(SocketAddress remote) throws SocketException {
-        Objects.requireNonNull(remote, "Address can't be null");
-        connectInternal(remote);
+        if (remote == null)
+            throw new IllegalArgumentException("Address can't be null");
+        connectInternal(Net.asInetSocketAddress(remote));
     }
 
     @Override
@@ -157,80 +155,84 @@
 
     @Override
     public SocketAddress getLocalSocketAddress() {
-        return dc.localAddress();
+        try {
+            return dc.getLocalAddress();
+        } catch (ClosedChannelException e) {
+            return null;
+        } catch (Exception x) {
+            throw new Error(x);
+        }
     }
 
     @Override
     public void send(DatagramPacket p) throws IOException {
-        synchronized (dc.blockingLock()) {
-            if (!dc.isBlocking())
-                throw new IllegalBlockingModeException();
-            try {
-                synchronized (p) {
-                    ByteBuffer bb = ByteBuffer.wrap(p.getData(),
-                                                    p.getOffset(),
-                                                    p.getLength());
-                    if (dc.isConnected()) {
-                        if (p.getAddress() == null) {
-                            // Legacy DatagramSocket will send in this case
-                            // and set address and port of the packet
-                            InetSocketAddress isa = dc.remoteAddress();
-                            p.setPort(isa.getPort());
-                            p.setAddress(isa.getAddress());
-                            dc.write(bb);
-                        } else {
-                            // Target address may not match connected address
-                            dc.send(bb, p.getSocketAddress());
-                        }
-                    } else {
-                        // Not connected so address must be valid or throw
-                        dc.send(bb, p.getSocketAddress());
+        ByteBuffer bb = null;
+        try {
+            InetSocketAddress target;
+            synchronized (p) {
+                // copy bytes to temporary direct buffer
+                int len = p.getLength();
+                bb = Util.getTemporaryDirectBuffer(len);
+                bb.put(p.getData(), p.getOffset(), len);
+                bb.flip();
+
+                // target address
+                if (p.getAddress() == null) {
+                    InetSocketAddress remote = dc.remoteAddress();
+                    if (remote == null) {
+                        // not specified by DatagramSocket
+                        throw new IllegalArgumentException("Address not set");
                     }
+                    // set address/port to maintain compatibility with DatagramSocket
+                    p.setAddress(remote.getAddress());
+                    p.setPort(remote.getPort());
+                    target = remote;
+                } else {
+                    // throws IllegalArgumentException if port not set
+                    target = (InetSocketAddress) p.getSocketAddress();
                 }
-            } catch (IOException x) {
-                Net.translateException(x);
             }
-        }
-    }
-
-    private SocketAddress receive(ByteBuffer bb) throws IOException {
-        assert Thread.holdsLock(dc.blockingLock()) && dc.isBlocking();
-
-        long to = this.timeout;
-        if (to == 0) {
-            return dc.receive(bb);
-        } else {
-            for (;;) {
-                if (!dc.isOpen())
-                    throw new ClosedChannelException();
-                long st = System.currentTimeMillis();
-                if (dc.pollRead(to)) {
-                    return dc.receive(bb);
-                }
-                to -= System.currentTimeMillis() - st;
-                if (to <= 0)
-                    throw new SocketTimeoutException();
+            // send datagram
+            try {
+                dc.blockingSend(bb, target);
+            } catch (AlreadyConnectedException e) {
+                throw new IllegalArgumentException("Connected and packet address differ");
+            } catch (ClosedChannelException e) {
+                var exc = new SocketException("Socket closed");
+                exc.initCause(e);
+                throw exc;
+            }
+        } finally {
+            if (bb != null) {
+                Util.offerFirstTemporaryDirectBuffer(bb);
             }
         }
     }
 
     @Override
     public void receive(DatagramPacket p) throws IOException {
-        synchronized (dc.blockingLock()) {
-            if (!dc.isBlocking())
-                throw new IllegalBlockingModeException();
-            try {
-                synchronized (p) {
-                    ByteBuffer bb = ByteBuffer.wrap(p.getData(),
-                                                    p.getOffset(),
-                                                    p.getLength());
-                    SocketAddress sender = receive(bb);
-                    p.setSocketAddress(sender);
-                    p.setLength(bb.position() - p.getOffset());
-                }
-            } catch (IOException x) {
-                Net.translateException(x);
+        // get temporary direct buffer with a capacity of p.bufLength
+        int bufLength = DatagramPackets.getBufLength(p);
+        ByteBuffer bb = Util.getTemporaryDirectBuffer(bufLength);
+        try {
+            long nanos = MILLISECONDS.toNanos(timeout);
+            SocketAddress sender = dc.blockingReceive(bb, nanos);
+            bb.flip();
+            synchronized (p) {
+                // copy bytes to the DatagramPacket and set length
+                int len = Math.min(bb.limit(), DatagramPackets.getBufLength(p));
+                bb.get(p.getData(), p.getOffset(), len);
+                DatagramPackets.setLength(p, len);
+
+                // sender address
+                p.setSocketAddress(sender);
             }
+        } catch (ClosedChannelException e) {
+            var exc = new SocketException("Socket closed");
+            exc.initCause(e);
+            throw exc;
+        } finally {
+            Util.offerFirstTemporaryDirectBuffer(bb);
         }
     }
 
@@ -257,19 +259,16 @@
     public int getLocalPort() {
         if (isClosed())
             return -1;
-        try {
-            InetSocketAddress local = dc.localAddress();
-            if (local != null) {
-                return local.getPort();
-            }
-        } catch (Exception x) {
+        InetSocketAddress local = dc.localAddress();
+        if (local != null) {
+            return local.getPort();
         }
         return 0;
     }
 
     @Override
     public void setSoTimeout(int timeout) throws SocketException {
-        if (!dc.isOpen())
+        if (isClosed())
             throw new SocketException("Socket is closed");
         if (timeout < 0)
             throw new IllegalArgumentException("timeout < 0");
@@ -278,7 +277,7 @@
 
     @Override
     public int getSoTimeout() throws SocketException {
-        if (!dc.isOpen())
+        if (isClosed())
             throw new SocketException("Socket is closed");
         return timeout;
     }
@@ -353,7 +352,6 @@
     @Override
     public boolean getReuseAddress() throws SocketException {
         return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
-
     }
 
     @Override
@@ -411,50 +409,157 @@
         return dc.supportedOptions();
     }
 
-   /*
-    * A dummy implementation of DatagramSocketImpl that can be passed to the
-    * DatagramSocket constructor so that no native resources are allocated in
-    * super class.
-    */
-   private static final DatagramSocketImpl dummyDatagramSocket
-       = new DatagramSocketImpl()
-   {
-       protected void create() throws SocketException {}
+
+    /**
+     * DatagramSocketImpl implementation where all methods throw an error.
+     */
+    private static class DummyDatagramSocketImpl extends DatagramSocketImpl {
+        private static <T> T shouldNotGetHere() {
+            throw new InternalError("Should not get here");
+        }
+
+        @Override
+        protected void create() {
+            shouldNotGetHere();
+        }
+
+        @Override
+        protected void bind(int lport, InetAddress laddr) {
+            shouldNotGetHere();
+        }
+
+        @Override
+        protected void send(DatagramPacket p) {
+            shouldNotGetHere();
+        }
+
+        @Override
+        protected int peek(InetAddress address) {
+            return shouldNotGetHere();
+        }
+
+        @Override
+        protected int peekData(DatagramPacket p) {
+            return shouldNotGetHere();
+        }
 
-       protected void bind(int lport, InetAddress laddr) throws SocketException {}
+        @Override
+        protected void receive(DatagramPacket p) {
+            shouldNotGetHere();
+        }
+
+        @Deprecated
+        protected void setTTL(byte ttl) {
+            shouldNotGetHere();
+        }
 
-       protected void send(DatagramPacket p) throws IOException {}
+        @Deprecated
+        protected byte getTTL() {
+            return shouldNotGetHere();
+        }
 
-       protected int peek(InetAddress i) throws IOException { return 0; }
+        @Override
+        protected void setTimeToLive(int ttl) {
+            shouldNotGetHere();
+        }
 
-       protected int peekData(DatagramPacket p) throws IOException { return 0; }
+        @Override
+        protected int getTimeToLive() {
+            return shouldNotGetHere();
+        }
+
+        @Override
+        protected void join(InetAddress group) {
+            shouldNotGetHere();
+        }
 
-       protected void receive(DatagramPacket p) throws IOException {}
+        @Override
+        protected void leave(InetAddress inetaddr) {
+            shouldNotGetHere();
+        }
 
-       @Deprecated
-       protected void setTTL(byte ttl) throws IOException {}
+        @Override
+        protected void joinGroup(SocketAddress group, NetworkInterface netIf) {
+            shouldNotGetHere();
+        }
 
-       @Deprecated
-       protected byte getTTL() throws IOException { return 0; }
+        @Override
+        protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) {
+            shouldNotGetHere();
+        }
 
-       protected void setTimeToLive(int ttl) throws IOException {}
+        @Override
+        protected void close() {
+            shouldNotGetHere();
+        }
+
+        @Override
+        public Object getOption(int optID) {
+            return shouldNotGetHere();
+        }
 
-       protected int getTimeToLive() throws IOException { return 0;}
+        @Override
+        public void setOption(int optID, Object value) {
+            shouldNotGetHere();
+        }
+
+        @Override
+        protected <T> void setOption(SocketOption<T> name, T value) {
+            shouldNotGetHere();
+        }
 
-       protected void join(InetAddress inetaddr) throws IOException {}
+        @Override
+        protected <T> T getOption(SocketOption<T> name) {
+            return shouldNotGetHere();
+        }
 
-       protected void leave(InetAddress inetaddr) throws IOException {}
+        @Override
+        protected Set<SocketOption<?>> supportedOptions() {
+            return shouldNotGetHere();
+        }
+    }
 
-       protected void joinGroup(SocketAddress mcastaddr,
-                                 NetworkInterface netIf) throws IOException {}
-
-       protected void leaveGroup(SocketAddress mcastaddr,
-                                 NetworkInterface netIf) throws IOException {}
+    /**
+     * Defines static methods to get/set DatagramPacket fields and workaround
+     * DatagramPacket deficiencies.
+     */
+    private static class DatagramPackets {
+        private static final VarHandle LENGTH;
+        private static final VarHandle BUF_LENGTH;
+        static {
+            try {
+                PrivilegedAction<Lookup> pa = () -> {
+                    try {
+                        return MethodHandles.privateLookupIn(DatagramPacket.class, MethodHandles.lookup());
+                    } catch (Exception e) {
+                        throw new ExceptionInInitializerError(e);
+                    }
+                };
+                MethodHandles.Lookup l = AccessController.doPrivileged(pa);
+                LENGTH = l.findVarHandle(DatagramPacket.class, "length", int.class);
+                BUF_LENGTH = l.findVarHandle(DatagramPacket.class, "bufLength", int.class);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
 
-       protected void close() {}
-
-       public Object getOption(int optID) throws SocketException { return null;}
+        /**
+         * Sets the DatagramPacket.length field. DatagramPacket.setLength cannot be
+         * used at this time because it sets both the length and bufLength fields.
+         */
+        static void setLength(DatagramPacket p, int value) {
+            synchronized (p) {
+                LENGTH.set(p, value);
+            }
+        }
 
-       public void setOption(int optID, Object value) throws SocketException {}
-   };
-}
+        /**
+         * Returns the value of the DatagramPacket.bufLength field.
+         */
+        static int getBufLength(DatagramPacket p) {
+            synchronized (p) {
+                return (int) BUF_LENGTH.get(p);
+            }
+        }
+    }
+}
\ No newline at end of file
--- a/src/java.base/share/classes/sun/nio/ch/DummySocketImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/DummySocketImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -31,8 +31,6 @@
 import java.net.SocketAddress;
 import java.net.SocketImpl;
 import java.net.SocketOption;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import java.util.Set;
 
 /**
@@ -41,12 +39,10 @@
  */
 
 class DummySocketImpl extends SocketImpl {
-    private static final PrivilegedAction<SocketImpl> NEW = DummySocketImpl::new;
-
     private DummySocketImpl() { }
 
     static SocketImpl create() {
-        return AccessController.doPrivileged(NEW);
+        return new DummySocketImpl();
     }
 
     private static <T> T shouldNotGetHere() {
--- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -229,8 +229,8 @@
                         n = implAccept(fd, newfd, isaa);
                     }
                 } finally {
-                    // restore socket to blocking mode
-                    lockedConfigureBlocking(true);
+                    // restore socket to blocking mode (if channel is open)
+                    tryLockedConfigureBlocking(true);
                 }
             } finally {
                 end(true, n > 0);
@@ -257,7 +257,7 @@
     }
 
     /**
-     * Adjust the blocking mode while holding acceptLock.
+     * Adjust the blocking. acceptLock must already be held.
      */
     private void lockedConfigureBlocking(boolean block) throws IOException {
         assert acceptLock.isHeldByCurrentThread();
@@ -268,6 +268,25 @@
     }
 
     /**
+     * Adjusts the blocking mode if the channel is open. acceptLock must already
+     * be held.
+     *
+     * @return {@code true} if the blocking mode was adjusted, {@code false} if
+     *         the blocking mode was not adjusted because the channel is closed
+     */
+    private boolean tryLockedConfigureBlocking(boolean block) throws IOException {
+        assert acceptLock.isHeldByCurrentThread();
+        synchronized (stateLock) {
+            if (isOpen()) {
+                IOUtil.configureBlocking(fd, block);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
      * Closes the socket if there are no accept in progress and the channel is
      * not registered with a Selector.
      */
--- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -409,7 +409,7 @@
     }
 
     /**
-     * Adjust the blocking mode while holding the readLock or writeLock.
+     * Adjusts the blocking mode. readLock or writeLock must already be held.
      */
     private void lockedConfigureBlocking(boolean block) throws IOException {
         assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
@@ -420,6 +420,25 @@
     }
 
     /**
+     * Adjusts the blocking mode if the channel is open. readLock or writeLock
+     * must already be held.
+     *
+     * @return {@code true} if the blocking mode was adjusted, {@code false} if
+     *         the blocking mode was not adjusted because the channel is closed
+     */
+    private boolean tryLockedConfigureBlocking(boolean block) throws IOException {
+        assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
+        synchronized (stateLock) {
+            if (isOpen()) {
+                IOUtil.configureBlocking(fd, block);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
      * Returns the local address, or null if not bound
      */
     SocketAddress localAddress() {
@@ -852,8 +871,8 @@
                             int n = connectImpl(fd, sa);
                             connected = (n > 0) ? true : finishTimedConnect(nanos);
                         } finally {
-                            // restore socket to blocking mode
-                            lockedConfigureBlocking(true);
+                            // restore socket to blocking mode (if channel is open)
+                            tryLockedConfigureBlocking(true);
                         }
                     } finally {
                         endConnect(true, connected);
@@ -945,8 +964,8 @@
                     try {
                         n = timedRead(b, off, len, nanos);
                     } finally {
-                        // restore socket to blocking mode
-                        lockedConfigureBlocking(true);
+                        // restore socket to blocking mode (if channel is open)
+                        tryLockedConfigureBlocking(true);
                     }
                 } else {
                     // read, no timeout
--- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Mon Nov 04 11:25:55 2019 +0000
@@ -58,6 +58,7 @@
 import java.text.Collator;
 import java.text.MessageFormat;
 import java.util.*;
+import java.util.function.BiFunction;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.math.BigInteger;
@@ -4109,15 +4110,51 @@
     }
 
     /**
-     * Match a command (may be abbreviated) with a command set.
-     * @param s the command provided
+     * Match a command with a command set. The match can be exact, or
+     * partial, or case-insensitive.
+     *
+     * @param s the command provided by user
      * @param list the legal command set. If there is a null, commands after it
-     * are regarded experimental, which means they are supported but their
-     * existence should not be revealed to user.
+     *      are regarded experimental, which means they are supported but their
+     *      existence should not be revealed to user.
      * @return the position of a single match, or -1 if none matched
      * @throws Exception if s is ambiguous
      */
     private static int oneOf(String s, String... list) throws Exception {
+
+        // First, if there is an exact match, returns it.
+        int res = oneOfMatch((a,b) -> a.equals(b), s, list);
+        if (res >= 0) {
+            return res;
+        }
+
+        // Second, if there is one single camelCase or prefix match, returns it.
+        // This regex substitution removes all lowercase letters not at the
+        // beginning, so "keyCertSign" becomes "kCS".
+        res = oneOfMatch((a,b) -> a.equals(b.replaceAll("(?<!^)[a-z]", ""))
+                || b.startsWith(a), s, list);
+        if (res >= 0) {
+            return res;
+        }
+
+        // Finally, retry the 2nd step ignoring case
+        return oneOfMatch((a,b) -> a.equalsIgnoreCase(b.replaceAll("(?<!^)[a-z]", ""))
+                || b.toUpperCase(Locale.ROOT).startsWith(a.toUpperCase(Locale.ROOT)),
+                s, list);
+    }
+
+    /**
+     * Match a command with a command set.
+     *
+     * @param matcher a BiFunction which returns {@code true} if the 1st
+     *               argument (user input) matches the 2nd one (full command)
+     * @param s the command provided by user
+     * @param list the legal command set
+     * @return the position of a single match, or -1 if none matched
+     * @throws Exception if s is ambiguous
+     */
+    private static int oneOfMatch(BiFunction<String,String,Boolean> matcher,
+            String s, String... list) throws Exception {
         int[] match = new int[list.length];
         int nmatch = 0;
         int experiment = Integer.MAX_VALUE;
@@ -4127,25 +4164,8 @@
                 experiment = i;
                 continue;
             }
-            if (one.toLowerCase(Locale.ENGLISH)
-                    .startsWith(s.toLowerCase(Locale.ENGLISH))) {
+            if (matcher.apply(s, one)) {
                 match[nmatch++] = i;
-            } else {
-                StringBuilder sb = new StringBuilder();
-                boolean first = true;
-                for (char c: one.toCharArray()) {
-                    if (first) {
-                        sb.append(c);
-                        first = false;
-                    } else {
-                        if (!Character.isLowerCase(c)) {
-                            sb.append(c);
-                        }
-                    }
-                }
-                if (sb.toString().equalsIgnoreCase(s)) {
-                    match[nmatch++] = i;
-                }
             }
         }
         if (nmatch == 0) {
@@ -4159,7 +4179,7 @@
             }
             StringBuilder sb = new StringBuilder();
             MessageFormat form = new MessageFormat(rb.getString
-                ("command.{0}.is.ambiguous."));
+                    ("command.{0}.is.ambiguous."));
             Object[] source = {s};
             sb.append(form.format(source));
             sb.append("\n    ");
--- a/src/java.base/share/native/libjava/Thread.c	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/native/libjava/Thread.c	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, 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
@@ -50,9 +50,7 @@
     {"yield",            "()V",        (void *)&JVM_Yield},
     {"sleep",            "(J)V",       (void *)&JVM_Sleep},
     {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
-    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
     {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
-    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
     {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
     {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
     {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
@@ -69,3 +67,12 @@
 {
     (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
 }
+
+JNIEXPORT void JNICALL
+Java_java_lang_Thread_clearInterruptEvent(JNIEnv *env, jclass cls)
+{
+#if defined(_WIN32)
+    // Need to reset the interrupt event used by Process.waitFor
+    ResetEvent((HANDLE) JVM_GetThreadInterruptEvent());
+#endif
+}
--- a/src/java.base/share/native/libjava/jni_util.c	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/native/libjava/jni_util.c	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -791,13 +791,6 @@
     CHECK_NULL(String_value_ID);
 }
 
-// This alias is used for compatibility with 32 bit Windows
-JNIEXPORT jstring
-NewStringPlatform(JNIEnv *env, const char *str)
-{
-    return JNU_NewStringPlatform(env, str);
-}
-
 JNIEXPORT jstring JNICALL
 JNU_NewStringPlatform(JNIEnv *env, const char *str)
 {
--- a/src/java.base/share/native/libjava/jni_util.h	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/share/native/libjava/jni_util.h	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -93,9 +93,6 @@
 JNU_ThrowIOExceptionWithLastError(JNIEnv *env, const char *defaultDetail);
 
 /* Convert between Java strings and i18n C strings */
-JNIEXPORT jstring
-NewStringPlatform(JNIEnv *env, const char *str);
-
 JNIEXPORT const char *
 GetStringPlatformChars(JNIEnv *env, jstring jstr, jboolean *isCopy);
 
--- a/src/java.base/unix/classes/sun/nio/fs/UnixFileStore.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/unix/classes/sun/nio/fs/UnixFileStore.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -118,13 +118,31 @@
     @Override
     public long getTotalSpace() throws IOException {
         UnixFileStoreAttributes attrs = readAttributes();
-        return attrs.blockSize() * attrs.totalBlocks();
+        try {
+            return Math.multiplyExact(attrs.blockSize(), attrs.totalBlocks());
+        } catch (ArithmeticException ignore) {
+            return Long.MAX_VALUE;
+        }
     }
 
     @Override
     public long getUsableSpace() throws IOException {
-       UnixFileStoreAttributes attrs = readAttributes();
-       return attrs.blockSize() * attrs.availableBlocks();
+        UnixFileStoreAttributes attrs = readAttributes();
+        try {
+            return Math.multiplyExact(attrs.blockSize(), attrs.availableBlocks());
+        } catch (ArithmeticException ignore) {
+            return Long.MAX_VALUE;
+        }
+    }
+
+    @Override
+    public long getUnallocatedSpace() throws IOException {
+        UnixFileStoreAttributes attrs = readAttributes();
+        try {
+            return Math.multiplyExact(attrs.blockSize(), attrs.freeBlocks());
+        } catch (ArithmeticException ignore) {
+            return Long.MAX_VALUE;
+        }
     }
 
     @Override
@@ -134,12 +152,6 @@
     }
 
     @Override
-    public long getUnallocatedSpace() throws IOException {
-        UnixFileStoreAttributes attrs = readAttributes();
-        return attrs.blockSize() * attrs.freeBlocks();
-    }
-
-    @Override
     public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> view)
     {
         if (view == null)
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileStore.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileStore.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -145,21 +145,25 @@
 
     @Override
     public long getTotalSpace() throws IOException {
-        return readDiskFreeSpaceEx().totalNumberOfBytes();
+        long space = readDiskFreeSpaceEx().totalNumberOfBytes();
+        return space >= 0 ? space : Long.MAX_VALUE;
     }
 
     @Override
     public long getUsableSpace() throws IOException {
-        return readDiskFreeSpaceEx().freeBytesAvailable();
-    }
-
-    public long getBlockSize() throws IOException {
-        return readDiskFreeSpace().bytesPerSector();
+        long space = readDiskFreeSpaceEx().freeBytesAvailable();
+        return space >= 0 ? space : Long.MAX_VALUE;
     }
 
     @Override
     public long getUnallocatedSpace() throws IOException {
-        return readDiskFreeSpaceEx().freeBytesAvailable();
+        long space = readDiskFreeSpaceEx().freeBytesAvailable();
+        return space >= 0 ? space : Long.MAX_VALUE;
+    }
+
+    @Override
+    public long getBlockSize() throws IOException {
+        return readDiskFreeSpace().bytesPerSector();
     }
 
     @Override
--- a/src/java.base/windows/native/libjava/canonicalize_md.c	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/java.base/windows/native/libjava/canonicalize_md.c	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, 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
@@ -41,39 +41,9 @@
 
 #undef DEBUG_PATH        /* Define this to debug path code */
 
-#define isfilesep(c) ((c) == '/' || (c) == '\\')
-#define wisfilesep(c) ((c) == L'/' || (c) == L'\\')
-#define islb(c)      (IsDBCSLeadByte((BYTE)(c)))
-
-
 /* Copy bytes to dst, not going past dend; return dst + number of bytes copied,
    or NULL if dend would have been exceeded.  If first != '\0', copy that byte
    before copying bytes from src to send - 1. */
-
-static char *
-cp(char *dst, char *dend, char first, char *src, char *send)
-{
-    char *p = src, *q = dst;
-    if (first != '\0') {
-        if (q < dend) {
-            *q++ = first;
-        } else {
-            errno = ENAMETOOLONG;
-            return NULL;
-        }
-    }
-    if (send - p > dend - q) {
-        errno = ENAMETOOLONG;
-        return NULL;
-    }
-    while (p < send) {
-        *q++ = *p++;
-    }
-    return q;
-}
-
-/* Wide character version of cp */
-
 static WCHAR*
 wcp(WCHAR *dst, WCHAR *dend, WCHAR first, WCHAR *src, WCHAR *send)
 {
@@ -95,23 +65,8 @@
     return q;
 }
 
-
 /* Find first instance of '\\' at or following start.  Return the address of
    that byte or the address of the null terminator if '\\' is not found. */
-
-static char *
-nextsep(char *start)
-{
-    char *p = start;
-    int c;
-    while ((c = *p) && (c != '\\')) {
-        p += ((islb(c) && p[1]) ? 2 : 1);
-    }
-    return p;
-}
-
-/* Wide character version of nextsep */
-
 static WCHAR *
 wnextsep(WCHAR *start)
 {
@@ -123,21 +78,6 @@
 }
 
 /* Tell whether the given string contains any wildcard characters */
-
-static int
-wild(char *start)
-{
-    char *p = start;
-    int c;
-    while (c = *p) {
-        if ((c == '*') || (c == '?')) return 1;
-        p += ((islb(c) && p[1]) ? 2 : 1);
-    }
-    return 0;
-}
-
-/* Wide character version of wild */
-
 static int
 wwild(WCHAR *start)
 {
@@ -157,25 +97,6 @@
    Prohibited canonical paths: c:\..\x  c:\x.\d c:\...
 */
 static int
-dots(char *start)
-{
-    char *p = start;
-    while (*p) {
-        if ((p = strchr(p, '.')) == NULL) // find next occurrence of '.'
-            return 0; // no more dots
-        p++; // next char
-        while ((*p) == '.') // go to the end of dots
-            p++;
-        if (*p && (*p != '\\')) // path element does not end with a dot
-            p++; // go to the next char
-        else
-            return 1; // path element does end with a dot - prohibited
-    }
-    return 0; // no prohibited combinations of dots found
-}
-
-/* Wide character version of dots */
-static int
 wdots(WCHAR *start)
 {
     WCHAR *p = start;
@@ -203,7 +124,6 @@
    successfully after copying the rest of the original path to the result path.
    Other I/O errors cause an error return.
 */
-
 int
 lastErrorReportable()
 {
@@ -225,222 +145,10 @@
     return 1;
 }
 
-int wcanonicalize(WCHAR *orig_path, WCHAR *result, int size);
-
 /* Convert a pathname to canonical form.  The input orig_path is assumed to
    have been converted to native form already, via JVM_NativePath().  This is
    necessary because _fullpath() rejects duplicate separator characters on
    Win95, though it accepts them on NT. */
-
-int
-canonicalize(char *orig_path, char *result, int size)
-{
-    WIN32_FIND_DATA fd;
-    HANDLE h;
-    char path[1024];    /* Working copy of path */
-    char *src, *dst, *dend;
-    wchar_t *worig_path, *wresult;
-    size_t converted_chars = 0;
-
-    /* handle long path with length >= MAX_PATH */
-    if (strlen(orig_path) >= MAX_PATH) {
-        if ((worig_path = (WCHAR*)malloc(size * sizeof(WCHAR))) == NULL)
-            return -1;
-
-        if (mbstowcs_s(&converted_chars, worig_path, (size_t)size, orig_path, (size_t)(size - 1)) != 0) {
-            free(worig_path);
-            return -1;
-        }
-
-        if ((wresult = (WCHAR*)malloc(size * sizeof(WCHAR))) == NULL)
-            return -1;
-
-        if (wcanonicalize(worig_path, wresult, size) != 0) {
-            free(worig_path);
-            free(wresult);
-            return -1;
-        }
-
-        if (wcstombs_s(&converted_chars, result, (size_t)size, wresult, (size_t)(size - 1)) != 0) {
-            free(worig_path);
-            free(wresult);
-            return -1;
-        }
-
-        free(worig_path);
-        free(wresult);
-        return 0;
-    }
-
-    /* Reject paths that contain wildcards */
-    if (wild(orig_path)) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    /* Collapse instances of "foo\.." and ensure absoluteness.  Note that
-      contrary to the documentation, the _fullpath procedure does not require
-      the drive to be available.  It also does not reliably change all
-      occurrences of '/' to '\\' on Win95, so now JVM_NativePath does that. */
-    if (!_fullpath(path, orig_path, sizeof(path))) {
-        return -1;
-    }
-
-    /* Correction for Win95: _fullpath may leave a trailing "\\"
-      on a UNC pathname */
-    if ((path[0] == '\\') && (path[1] == '\\')) {
-        char *p = path + strlen(path);
-        if ((p[-1] == '\\') && !islb(p[-2])) {
-            p[-1] = '\0';
-        }
-    }
-
-    if (dots(path)) /* Check for prohibited combinations of dots */
-        return -1;
-
-    src = path;            /* Start scanning here */
-    dst = result;        /* Place results here */
-    dend = dst + size;        /* Don't go to or past here */
-
-    /* Copy prefix, assuming path is absolute */
-    if (isalpha(src[0]) && (src[1] == ':') && (src[2] == '\\')) {
-        /* Drive specifier */
-        *src = toupper(*src);    /* Canonicalize drive letter */
-        if (!(dst = cp(dst, dend, '\0', src, src + 2))) {
-            return -1;
-        }
-        src += 2;
-    } else if ((src[0] == '\\') && (src[1] == '\\')) {
-        /* UNC pathname */
-        char *p;
-        p = nextsep(src + 2);    /* Skip past host name */
-        if (!*p) {
-            /* A UNC pathname must begin with "\\\\host\\share",
-            so reject this path as invalid if there is no share name */
-            errno = EINVAL;
-            return -1;
-        }
-        p = nextsep(p + 1);    /* Skip past share name */
-        if (!(dst = cp(dst, dend, '\0', src, p))) {
-            return -1;
-        }
-        src = p;
-    } else {
-        /* Invalid path */
-        errno = EINVAL;
-        return -1;
-    }
-
-    /* Windows 95/98/Me bug - FindFirstFile fails on network mounted drives */
-    /* for root pathes like "E:\" . If the path has this form, we should  */
-    /* simply return it, it is already canonicalized. */
-    if (strlen(path) == 3 && path[1] == ':' && path[2] == '\\') {
-        /* At this point we have already copied the drive specifier ("z:")*/
-        /* so we need to copy "\" and the null character. */
-        result[2] = '\\';
-        result[3] = '\0';
-        return 0;
-    }
-
-    /* At this point we have copied either a drive specifier ("z:") or a UNC
-    prefix ("\\\\host\\share") to the result buffer, and src points to the
-    first byte of the remainder of the path.  We now scan through the rest
-    of the path, looking up each prefix in order to find the true name of
-    the last element of each prefix, thereby computing the full true name of
-    the original path. */
-    while (*src) {
-        char *p = nextsep(src + 1);    /* Find next separator */
-        char c = *p;
-        assert(*src == '\\');        /* Invariant */
-        *p = '\0';            /* Temporarily clear separator */
-        h = FindFirstFile(path, &fd);    /* Look up prefix */
-        *p = c;                /* Restore separator */
-        if (h != INVALID_HANDLE_VALUE) {
-            /* Lookup succeeded; append true name to result and continue */
-            FindClose(h);
-            if (!(dst = cp(dst, dend, '\\',
-                fd.cFileName,
-                fd.cFileName + strlen(fd.cFileName)))) {
-                return -1;
-            }
-            src = p;
-            continue;
-        } else {
-            if (!lastErrorReportable()) {
-                if (!(dst = cp(dst, dend, '\0', src, src + strlen(src)))) {
-                    return -1;
-                }
-                break;
-            } else {
-                return -1;
-            }
-        }
-    }
-
-    if (dst >= dend) {
-        errno = ENAMETOOLONG;
-        return -1;
-    }
-    *dst = '\0';
-    return 0;
-
-}
-
-
-/* Convert a pathname to canonical form.  The input prefix is assumed
-   to be in canonical form already, and the trailing filename must not
-   contain any wildcard, dot/double dot, or other "tricky" characters
-   that are rejected by the canonicalize() routine above.  This
-   routine is present to allow the canonicalization prefix cache to be
-   used while still returning canonical names with the correct
-   capitalization. */
-
-int
-canonicalizeWithPrefix(char* canonicalPrefix, char* pathWithCanonicalPrefix, char *result, int size)
-{
-    WIN32_FIND_DATA fd;
-    HANDLE h;
-    char *src, *dst, *dend;
-
-    src = pathWithCanonicalPrefix;
-    dst = result;        /* Place results here */
-    dend = dst + size;   /* Don't go to or past here */
-
-    h = FindFirstFile(pathWithCanonicalPrefix, &fd);    /* Look up file */
-    if (h != INVALID_HANDLE_VALUE) {
-        /* Lookup succeeded; concatenate true name to prefix */
-        FindClose(h);
-        if (!(dst = cp(dst, dend, '\0',
-                       canonicalPrefix,
-                       canonicalPrefix + strlen(canonicalPrefix)))) {
-            return -1;
-        }
-        if (!(dst = cp(dst, dend, '\\',
-                       fd.cFileName,
-                       fd.cFileName + strlen(fd.cFileName)))) {
-            return -1;
-        }
-    } else {
-        if (!lastErrorReportable()) {
-            if (!(dst = cp(dst, dend, '\0', src, src + strlen(src)))) {
-                return -1;
-            }
-        } else {
-            return -1;
-        }
-    }
-
-    if (dst >= dend) {
-        errno = ENAMETOOLONG;
-        return -1;
-    }
-    *dst = '\0';
-    return 0;
-}
-
-
-/* Wide character version of canonicalize. Size is a wide-character size. */
-
 int
 wcanonicalize(WCHAR *orig_path, WCHAR *result, int size)
 {
@@ -559,9 +267,13 @@
     return -1;
 }
 
-
-/* Wide character version of canonicalizeWithPrefix. */
-
+/* Convert a pathname to canonical form.  The input prefix is assumed
+   to be in canonical form already, and the trailing filename must not
+   contain any wildcard, dot/double dot, or other "tricky" characters
+   that are rejected by the canonicalize() routine above.  This
+   routine is present to allow the canonicalization prefix cache to be
+   used while still returning canonical names with the correct
+   capitalization. */
 int
 wcanonicalizeWithPrefix(WCHAR *canonicalPrefix, WCHAR *pathWithCanonicalPrefix, WCHAR *result, int size)
 {
@@ -613,6 +325,46 @@
     return 0;
 }
 
+/* Non-Wide character version of canonicalize.
+   Converts to whchar and delegates to wcanonicalize. */
+int
+canonicalize(char* orig_path, char* result, int size) {
+    wchar_t* wpath = NULL;
+    wchar_t* wresult = NULL;
+    size_t conv;
+    size_t path_len = strlen(orig_path);
+    int ret = -1;
+
+    if ((wpath = (wchar_t*) malloc(sizeof(wchar_t) * (path_len + 1))) == NULL) {
+        goto finish;
+    }
+
+    if (mbstowcs_s(&conv, wpath, path_len + 1, orig_path, path_len) != 0) {
+        goto finish;
+    }
+
+    if ((wresult = (wchar_t*) malloc(sizeof(wchar_t) * size)) == NULL) {
+        goto finish;
+    }
+
+    if (wcanonicalize(wpath, wresult, size) != 0) {
+        goto finish;
+    }
+
+    if (wcstombs_s(&conv, result, (size_t) size, wresult, (size_t) (size - 1)) != 0) {
+        goto finish;
+    }
+
+    // Change return value to success.
+    ret = 0;
+
+finish:
+    free(wresult);
+    free(wpath);
+
+    return ret;
+}
+
 
 /* The appropriate location of getPrefixed() should be io_util_md.c, but
    java.lang.instrument package has hardwired canonicalize_md.c into their
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java	Mon Nov 04 11:25:55 2019 +0000
@@ -951,34 +951,20 @@
 
     private Env<AttrContext> attribStatToTree(JCTree stat, Env<AttrContext>env,
                                               JCTree tree, Map<JCClassDecl, JCClassDecl> copiedClasses) {
-        JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
-        Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
-        try {
-            Env<AttrContext> result = attr.attribStatToTree(stat, env, tree);
+        Env<AttrContext> result = attr.attribStatToTree(stat, env, tree);
 
-            enter.unenter(env.toplevel, stat);
-            fixLocalClassNames(copiedClasses, env);
-            return result;
-        } finally {
-            log.popDiagnosticHandler(diagHandler);
-            log.useSource(prev);
-        }
+        fixLocalClassNames(copiedClasses, env);
+
+        return result;
     }
 
     private Env<AttrContext> attribExprToTree(JCExpression expr, Env<AttrContext>env,
                                               JCTree tree, Map<JCClassDecl, JCClassDecl> copiedClasses) {
-        JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
-        Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
-        try {
-            Env<AttrContext> result = attr.attribExprToTree(expr, env, tree);
+        Env<AttrContext> result = attr.attribExprToTree(expr, env, tree);
 
-            enter.unenter(env.toplevel, expr);
-            fixLocalClassNames(copiedClasses, env);
-            return result;
-        } finally {
-            log.popDiagnosticHandler(diagHandler);
-            log.useSource(prev);
-        }
+        fixLocalClassNames(copiedClasses, env);
+
+        return result;
     }
 
     /* Change the flatnames of the local and anonymous classes in the Scope to
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -564,7 +564,7 @@
 
             //TODO: to further refine the analysis, try all rewriting combinations
             deferredAttr.attribSpeculative(treeToAnalyze, rewriting.env, attr.statInfo, new TreeRewriter(rewriting),
-                    t -> rewriting.diagHandler(), AttributionMode.ANALYZER, argumentAttr.withLocalCacheContext());
+                    () -> rewriting.diagHandler(), AttributionMode.ANALYZER, argumentAttr.withLocalCacheContext());
             rewriting.analyzer.process(rewriting.oldTree, rewriting.replacement, rewriting.erroneous);
         } catch (Throwable ex) {
             Assert.error("Analyzer error when processing: " +
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -1377,4 +1377,31 @@
     public void newRound() {
         blockCount = 1;
     }
+
+    public Queues setQueues(Queues nue) {
+        Queues stored = new Queues(q, validateQ, typesQ, afterTypesQ);
+        this.q = nue.q;
+        this.typesQ = nue.typesQ;
+        this.afterTypesQ = nue.afterTypesQ;
+        this.validateQ = nue.validateQ;
+        return stored;
+    }
+
+    static class Queues {
+        private final ListBuffer<Runnable> q;
+        private final ListBuffer<Runnable> validateQ;
+        private final ListBuffer<Runnable> typesQ;
+        private final ListBuffer<Runnable> afterTypesQ;
+
+        public Queues() {
+            this(new ListBuffer<Runnable>(), new ListBuffer<Runnable>(), new ListBuffer<Runnable>(), new ListBuffer<Runnable>());
+        }
+
+        public Queues(ListBuffer<Runnable> q, ListBuffer<Runnable> validateQ, ListBuffer<Runnable> typesQ, ListBuffer<Runnable> afterTypesQ) {
+            this.q = q;
+            this.validateQ = validateQ;
+            this.typesQ = typesQ;
+            this.afterTypesQ = afterTypesQ;
+        }
+    }
 }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Mon Nov 04 11:25:55 2019 +0000
@@ -74,11 +74,8 @@
 import static com.sun.tools.javac.code.Kinds.Kind.*;
 import static com.sun.tools.javac.code.TypeTag.*;
 import static com.sun.tools.javac.code.TypeTag.WILDCARD;
-import com.sun.tools.javac.comp.Analyzer.AnalyzerMode;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
-import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler;
-import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
 
 /** This is the main context-dependent analysis phase in GJC. It
  *  encompasses name resolution, type checking and constant folding as
@@ -394,12 +391,20 @@
     }
 
     public Env<AttrContext> attribExprToTree(JCTree expr, Env<AttrContext> env, JCTree tree) {
+        return attribToTree(expr, env, tree, unknownExprInfo);
+    }
+
+    public Env<AttrContext> attribStatToTree(JCTree stmt, Env<AttrContext> env, JCTree tree) {
+        return attribToTree(stmt, env, tree, statInfo);
+    }
+
+    private Env<AttrContext> attribToTree(JCTree root, Env<AttrContext> env, JCTree tree, ResultInfo resultInfo) {
         breakTree = tree;
         JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
-        EnumSet<AnalyzerMode> analyzerModes = EnumSet.copyOf(analyzer.analyzerModes);
         try {
-            analyzer.analyzerModes.clear();
-            attribExpr(expr, env);
+            deferredAttr.attribSpeculative(root, env, resultInfo,
+                    null, DeferredAttr.AttributionMode.ANALYZER,
+                    argumentAttr.withLocalCacheContext());
         } catch (BreakAttr b) {
             return b.env;
         } catch (AssertionError ae) {
@@ -411,30 +416,6 @@
         } finally {
             breakTree = null;
             log.useSource(prev);
-            analyzer.analyzerModes.addAll(analyzerModes);
-        }
-        return env;
-    }
-
-    public Env<AttrContext> attribStatToTree(JCTree stmt, Env<AttrContext> env, JCTree tree) {
-        breakTree = tree;
-        JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
-        EnumSet<AnalyzerMode> analyzerModes = EnumSet.copyOf(analyzer.analyzerModes);
-        try {
-            analyzer.analyzerModes.clear();
-            attribStat(stmt, env);
-        } catch (BreakAttr b) {
-            return b.env;
-        } catch (AssertionError ae) {
-            if (ae.getCause() instanceof BreakAttr) {
-                return ((BreakAttr)(ae.getCause())).env;
-            } else {
-                throw ae;
-            }
-        } finally {
-            breakTree = null;
-            log.useSource(prev);
-            analyzer.analyzerModes.addAll(analyzerModes);
         }
         return env;
     }
@@ -1273,7 +1254,7 @@
     }
 
     public void visitBlock(JCBlock tree) {
-        if (env.info.scope.owner.kind == TYP) {
+        if (env.info.scope.owner.kind == TYP || env.info.scope.owner.kind == ERR) {
             // Block is a static or instance initializer;
             // let the owner of the environment be a freshly
             // created BLOCK-method.
@@ -1533,8 +1514,8 @@
                     attribCase.accept(c, caseEnv);
                 } finally {
                     caseEnv.info.scope.leave();
-                    addVars(c.stats, switchEnv.info.scope);
                 }
+                addVars(c.stats, switchEnv.info.scope);
             }
         } finally {
             switchEnv.info.scope.leave();
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	Mon Nov 04 11:25:55 2019 +0000
@@ -47,6 +47,7 @@
 import com.sun.tools.javac.tree.JCTree.*;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
 import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler;
+import com.sun.tools.javac.util.Log.DiagnosticHandler;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -57,13 +58,14 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.WeakHashMap;
-import java.util.function.Function;
+import java.util.function.Supplier;
 
 import com.sun.source.tree.MemberReferenceTree;
 import com.sun.tools.javac.code.Type;
 import com.sun.tools.javac.tree.JCTree.JCMemberReference.OverloadKind;
 
 import static com.sun.tools.javac.code.TypeTag.*;
+import com.sun.tools.javac.comp.Annotate.Queues;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;
 
 /**
@@ -80,6 +82,7 @@
 public class DeferredAttr extends JCTree.Visitor {
     protected static final Context.Key<DeferredAttr> deferredAttrKey = new Context.Key<>();
 
+    final Annotate annotate;
     final Attr attr;
     final ArgumentAttr argumentAttr;
     final Check chk;
@@ -107,6 +110,7 @@
 
     protected DeferredAttr(Context context) {
         context.put(deferredAttrKey, this);
+        annotate = Annotate.instance(context);
         attr = Attr.instance(context);
         argumentAttr = ArgumentAttr.instance(context);
         chk = Check.instance(context);
@@ -483,31 +487,44 @@
      */
     JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
         return attribSpeculative(tree, env, resultInfo, treeCopier,
-                newTree->new DeferredDiagnosticHandler(log), AttributionMode.SPECULATIVE, null);
+                null, AttributionMode.SPECULATIVE, null);
     }
 
     JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo, LocalCacheContext localCache) {
         return attribSpeculative(tree, env, resultInfo, treeCopier,
-                newTree->new DeferredDiagnosticHandler(log), AttributionMode.SPECULATIVE, localCache);
+                null, AttributionMode.SPECULATIVE, localCache);
     }
 
     <Z> JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo, TreeCopier<Z> deferredCopier,
-                                 Function<JCTree, DeferredDiagnosticHandler> diagHandlerCreator, AttributionMode attributionMode,
+                                 Supplier<DiagnosticHandler> diagHandlerCreator, AttributionMode attributionMode,
                                  LocalCacheContext localCache) {
         final JCTree newTree = deferredCopier.copy(tree);
-        Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner)));
+        return attribSpeculative(newTree, env, resultInfo, diagHandlerCreator, attributionMode, localCache);
+    }
+
+    /**
+     * Attribute the given tree, mostly reverting side-effects applied to shared
+     * compiler state. Exceptions include the ArgumentAttr.argumentTypeCache,
+     * changes to which may be preserved if localCache is null.
+     */
+    <Z> JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo,
+                              Supplier<DiagnosticHandler> diagHandlerCreator, AttributionMode attributionMode,
+                              LocalCacheContext localCache) {
+        Env<AttrContext> speculativeEnv = env.dup(tree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner)));
         speculativeEnv.info.attributionMode = attributionMode;
-        Log.DeferredDiagnosticHandler deferredDiagnosticHandler = diagHandlerCreator.apply(newTree);
+        Log.DiagnosticHandler deferredDiagnosticHandler = diagHandlerCreator != null ? diagHandlerCreator.get() : new DeferredDiagnosticHandler(log);
         DeferredCompletionFailureHandler.Handler prevCFHandler = dcfh.setHandler(dcfh.speculativeCodeHandler);
+        Queues prevQueues = annotate.setQueues(new Queues());
         int nwarnings = log.nwarnings;
         log.nwarnings = 0;
         try {
-            attr.attribTree(newTree, speculativeEnv, resultInfo);
-            return newTree;
+            attr.attribTree(tree, speculativeEnv, resultInfo);
+            return tree;
         } finally {
+            annotate.setQueues(prevQueues);
             dcfh.setHandler(prevCFHandler);
             log.nwarnings += nwarnings;
-            enter.unenter(env.toplevel, newTree);
+            enter.unenter(env.toplevel, tree);
             log.popDiagnosticHandler(deferredDiagnosticHandler);
             if (localCache != null) {
                 localCache.leave();
--- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PSSSignature.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PSSSignature.java	Mon Nov 04 11:25:55 2019 +0000
@@ -338,9 +338,6 @@
 
         int keySize = 0; // in bytes
         if (mechInfo != null) {
-            // check against available native info
-            int minKeySize = (int) mechInfo.ulMinKeySize;
-            int maxKeySize = (int) mechInfo.ulMaxKeySize;
             if (key instanceof P11Key) {
                 keySize = (((P11Key) key).length() + 7) >> 3;
             } else if (key instanceof RSAKey) {
@@ -348,13 +345,16 @@
             } else {
                 throw new InvalidKeyException("Unrecognized key type " + key);
             }
-            if ((minKeySize != -1) && (keySize < minKeySize)) {
+            // check against available native info which are in bits
+            if ((mechInfo.iMinKeySize != 0) &&
+                    (keySize < (mechInfo.iMinKeySize >> 3))) {
                 throw new InvalidKeyException(KEY_ALGO +
-                    " key must be at least " + minKeySize + " bytes");
+                    " key must be at least " + mechInfo.iMinKeySize + " bits");
             }
-            if ((maxKeySize != -1) && (keySize > maxKeySize)) {
+            if ((mechInfo.iMaxKeySize != Integer.MAX_VALUE) &&
+                    (keySize > (mechInfo.iMaxKeySize >> 3))) {
                 throw new InvalidKeyException(KEY_ALGO +
-                    " key must be at most " + maxKeySize + " bytes");
+                    " key must be at most " + mechInfo.iMaxKeySize + " bits");
             }
         }
         if (this.sigParams != null) {
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Flags.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Flags.java	Mon Nov 04 11:25:55 2019 +0000
@@ -35,7 +35,8 @@
   MANAGEMENT ("Management"),
   ERGONOMIC ("Ergonomic"),
   ATTACH_ON_DEMAND ("Attach on demand"),
-  INTERNAL ("Internal");
+  INTERNAL ("Internal"),
+  JIMAGE_RESOURCE ("JImage");
 
   private final String value;
 
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2019, 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
@@ -31,7 +31,6 @@
 // The OSThread class holds OS-specific thread information.  It is equivalent
 // to the sys_thread_t structure of the classic JVM implementation.
 public class OSThread extends VMObject {
-    private static JIntField interruptedField;
     private static Field threadIdField;
     private static CIntegerField threadStateField;
 
@@ -56,7 +55,6 @@
 
     private static synchronized void initialize(TypeDataBase db) {
         Type type = db.lookupType("OSThread");
-        interruptedField = type.getJIntField("_interrupted");
         threadIdField = type.getField("_thread_id");
         threadStateField = type.getCIntegerField("_state");
 
@@ -75,10 +73,6 @@
         super(addr);
     }
 
-    public boolean interrupted() {
-        return ((int)interruptedField.getValue(addr)) != 0;
-    }
-
     public int threadId() {
         return threadIdField.getJInt(addr);
     }
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java	Mon Nov 04 11:25:55 2019 +0000
@@ -114,6 +114,7 @@
   public static int    Flags_ERGONOMIC;
   public static int    Flags_ATTACH_ON_DEMAND;
   public static int    Flags_INTERNAL;
+  public static int    Flags_JIMAGE_RESOURCE;
   private static int   Flags_VALUE_ORIGIN_MASK;
   private static int   Flags_ORIG_COMMAND_LINE;
   /** This is only present in a non-core build */
@@ -200,6 +201,8 @@
             return "attach";
         } else if (origin == Flags_INTERNAL) {
             return "internal";
+        } else if (origin == Flags_JIMAGE_RESOURCE) {
+            return "jimage";
         } else {
             throw new IllegalStateException(
                 "Unknown flag origin " + origin + " is detected in " + name);
@@ -484,6 +487,7 @@
     Flags_ERGONOMIC = db.lookupIntConstant("JVMFlag::ERGONOMIC").intValue();
     Flags_ATTACH_ON_DEMAND = db.lookupIntConstant("JVMFlag::ATTACH_ON_DEMAND").intValue();
     Flags_INTERNAL = db.lookupIntConstant("JVMFlag::INTERNAL").intValue();
+    Flags_JIMAGE_RESOURCE = db.lookupIntConstant("JVMFlag::JIMAGE_RESOURCE").intValue();
     Flags_VALUE_ORIGIN_MASK = db.lookupIntConstant("JVMFlag::VALUE_ORIGIN_MASK").intValue();
     Flags_ORIG_COMMAND_LINE = db.lookupIntConstant("JVMFlag::ORIG_COMMAND_LINE").intValue();
     oopSize  = db.lookupIntConstant("oopSize").intValue();
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/BindingReader.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/BindingReader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.keymap;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/KeyMap.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/keymap/KeyMap.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.keymap;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Binding.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Binding.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Buffer.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Buffer.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Completer.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Completer.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletingParsedLine.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletingParsedLine.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Expander.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Expander.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Highlighter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Highlighter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/History.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/History.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,11 +4,12 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
 import java.io.IOException;
+import java.nio.file.Path;
 import java.time.Instant;
 import java.util.Iterator;
 import java.util.ListIterator;
@@ -42,6 +43,26 @@
     void save() throws IOException;
 
     /**
+     * Write history to the file. If incremental only the events that are new since the last incremental operation to
+     * the file are added.
+     * @throws IOException if a problem occurs
+     */
+    void write(Path file, boolean incremental) throws IOException;
+
+    /**
+     * Append history to the file. If incremental only the events that are new since the last incremental operation to
+     * the file are added.
+     * @throws IOException if a problem occurs
+     */
+    void append(Path file, boolean incremental) throws IOException;
+
+    /**
+     * Read history from the file. If incremental only the events that are not contained within the internal list are added.
+     * @throws IOException if a problem occurs
+     */
+    void read(Path file, boolean incremental) throws IOException;
+
+    /**
      * Purge history.
      * @throws IOException if a problem occurs
      */
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
@@ -95,7 +95,10 @@
     String CALLBACK_FINISH = "callback-finish";
     String CALLBACK_KEYMAP = "callback-keymap";
 
+    String ACCEPT_AND_INFER_NEXT_HISTORY = "accept-and-infer-next-history";
+    String ACCEPT_AND_HOLD = "accept-and-hold";
     String ACCEPT_LINE = "accept-line";
+    String ACCEPT_LINE_AND_DOWN_HISTORY = "accept-line-and-down-history";
     String ARGUMENT_BASE = "argument-base";
     String BACKWARD_CHAR = "backward-char";
     String BACKWARD_DELETE_CHAR = "backward-delete-char";
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
@@ -85,7 +85,7 @@
     public LineReaderBuilder parser(Parser parser) {
         if (parser != null) {
             try {
-                if (!Boolean.parseBoolean(LineReader.PROP_SUPPORT_PARSEDLINE)
+                if (!Boolean.getBoolean(LineReader.PROP_SUPPORT_PARSEDLINE)
                         && !(parser.parse("", 0) instanceof CompletingParsedLine)) {
                     Log.warn("The Parser of class " + parser.getClass().getName() + " does not support the CompletingParsedLine interface. " +
                             "Completion with escaped or quoted words won't work correctly.");
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Macro.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Macro.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/MaskingCallback.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/MaskingCallback.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ParsedLine.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ParsedLine.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
@@ -16,6 +16,10 @@
         return parse(line, cursor, ParseContext.UNSPECIFIED);
     }
 
+    default boolean isEscapeChar(char ch) {
+        return ch == '\\';
+    }
+
     enum ParseContext {
         UNSPECIFIED,
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Reference.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Reference.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/UserInterruptException.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/UserInterruptException.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Widget.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Widget.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/BufferImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/BufferImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultExpander.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultExpander.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl;
 
@@ -18,6 +18,13 @@
 
 public class DefaultParser implements Parser {
 
+    public enum Bracket {
+        ROUND,   // ()
+        CURLY,   // {}
+        SQUARE,  // []
+        ANGLE;   // <>
+    }
+
     private char[] quoteChars = {'\'', '"'};
 
     private char[] escapeChars = {'\\'};
@@ -26,6 +33,10 @@
 
     private boolean eofOnEscapedNewLine;
 
+    private char[] openingBrackets = null;
+
+    private char[] closingBrackets = null;
+
     //
     // Chainable setters
     //
@@ -45,6 +56,11 @@
         return this;
     }
 
+    public DefaultParser eofOnUnclosedBracket(Bracket... brackets){
+        setEofOnUnclosedBracket(brackets);
+        return this;
+    }
+
     public DefaultParser eofOnEscapedNewLine(boolean eofOnEscapedNewLine) {
         this.eofOnEscapedNewLine = eofOnEscapedNewLine;
         return this;
@@ -86,6 +102,39 @@
         return eofOnEscapedNewLine;
     }
 
+    public void setEofOnUnclosedBracket(Bracket... brackets){
+        if (brackets == null) {
+            openingBrackets = null;
+            closingBrackets = null;
+        } else {
+            Set<Bracket> bs = new HashSet<>(Arrays.asList(brackets));
+            openingBrackets = new char[bs.size()];
+            closingBrackets = new char[bs.size()];
+            int i = 0;
+            for (Bracket b : bs) {
+                switch (b) {
+                case ROUND:
+                    openingBrackets[i] = '(';
+                    closingBrackets[i] = ')';
+                    break;
+                case CURLY:
+                    openingBrackets[i] = '{';
+                    closingBrackets[i] = '}';
+                    break;
+                case SQUARE:
+                    openingBrackets[i] = '[';
+                    closingBrackets[i] = ']';
+                    break;
+                case ANGLE:
+                    openingBrackets[i] = '<';
+                    closingBrackets[i] = '>';
+                    break;
+                }
+                i++;
+            }
+        }
+    }
+
     public ParsedLine parse(final String line, final int cursor, ParseContext context) {
         List<String> words = new LinkedList<>();
         StringBuilder current = new StringBuilder();
@@ -95,6 +144,8 @@
         int rawWordCursor = -1;
         int rawWordLength = -1;
         int rawWordStart = 0;
+        BracketChecker bracketChecker = new BracketChecker();
+        boolean quotedWord = false;
 
         for (int i = 0; (line != null) && (i < line.length()); i++) {
             // once we reach the cursor, set the
@@ -110,36 +161,35 @@
             if (quoteStart < 0 && isQuoteChar(line, i)) {
                 // Start a quote block
                 quoteStart = i;
-            } else if (quoteStart >= 0) {
-                // In a quote block
-                if (line.charAt(quoteStart) == line.charAt(i) && !isEscaped(line, i)) {
-                    // End the block; arg could be empty, but that's fine
+                if (current.length()==0) {
+                    quotedWord = true;
+                } else {
+                    current.append(line.charAt(i));
+                }
+            } else if (quoteStart >= 0 && line.charAt(quoteStart) == line.charAt(i) && !isEscaped(line, i)) {
+                // End quote block
+                if (!quotedWord) {
+                    current.append(line.charAt(i));
+                } else if (rawWordCursor >= 0 && rawWordLength < 0) {
+                    rawWordLength = i - rawWordStart + 1;
+                }
+                quoteStart = -1;
+                quotedWord = false;
+            } else if (quoteStart < 0 && isDelimiter(line, i)) {
+                // Delimiter
+                if (current.length() > 0) {
                     words.add(current.toString());
-                    current.setLength(0);
-                    quoteStart = -1;
+                    current.setLength(0); // reset the arg
                     if (rawWordCursor >= 0 && rawWordLength < 0) {
-                        rawWordLength = i - rawWordStart + 1;
-                    }
-                } else {
-                    if (!isEscapeChar(line, i)) {
-                        // Take the next character
-                        current.append(line.charAt(i));
+                        rawWordLength = i - rawWordStart;
                     }
                 }
+                rawWordStart = i + 1;
             } else {
-                // Not in a quote block
-                if (isDelimiter(line, i)) {
-                    if (current.length() > 0) {
-                        words.add(current.toString());
-                        current.setLength(0); // reset the arg
-                        if (rawWordCursor >= 0 && rawWordLength < 0) {
-                            rawWordLength = i - rawWordStart;
-                        }
-                    }
-                    rawWordStart = i + 1;
-                } else {
-                    if (!isEscapeChar(line, i)) {
-                        current.append(line.charAt(i));
+                if (!isEscapeChar(line, i)) {
+                    current.append(line.charAt(i));
+                    if (quoteStart < 0) {
+                        bracketChecker.check(line, i);
                     }
                 }
             }
@@ -159,21 +209,29 @@
             rawWordLength = rawWordCursor;
         }
 
-        if (eofOnEscapedNewLine && isEscapeChar(line, line.length() - 1)) {
-            throw new EOFError(-1, -1, "Escaped new line", "newline");
-        }
-        if (eofOnUnclosedQuote && quoteStart >= 0 && context != ParseContext.COMPLETE) {
-            throw new EOFError(-1, -1, "Missing closing quote", line.charAt(quoteStart) == '\''
-                    ? "quote" : "dquote");
+        if (context != ParseContext.COMPLETE) {
+            if (eofOnEscapedNewLine && isEscapeChar(line, line.length() - 1)) {
+                throw new EOFError(-1, -1, "Escaped new line", "newline");
+            }
+            if (eofOnUnclosedQuote && quoteStart >= 0) {
+                throw new EOFError(-1, -1, "Missing closing quote", line.charAt(quoteStart) == '\''
+                        ? "quote" : "dquote");
+            }
+            if (bracketChecker.isOpeningBracketMissing()) {
+                throw new EOFError(-1, -1, "Missing opening bracket", "missing: " + bracketChecker.getMissingOpeningBracket());
+            }
+            if (bracketChecker.isClosingBracketMissing()) {
+                throw new EOFError(-1, -1, "Missing closing brackets", "add: " + bracketChecker.getMissingClosingBrackets());
+            }
         }
 
-        String openingQuote = quoteStart >= 0 ? line.substring(quoteStart, quoteStart + 1) : null;
+        String openingQuote = quotedWord ? line.substring(quoteStart, quoteStart + 1) : null;
         return new ArgumentList(line, words, wordIndex, wordCursor, cursor, openingQuote, rawWordCursor, rawWordLength);
     }
 
     /**
      * Returns true if the specified character is a whitespace parameter. Check to ensure that the character is not
-     * escaped by any of {@link #getQuoteChars}, and is not escaped by ant of the {@link #getEscapeChars}, and
+     * escaped by any of {@link #getQuoteChars}, and is not escaped by any of the {@link #getEscapeChars}, and
      * returns true from {@link #isDelimiterChar}.
      *
      * @param buffer    The complete command buffer
@@ -202,6 +260,18 @@
         return false;
     }
 
+    @Override
+    public boolean isEscapeChar(char ch) {
+        if (escapeChars != null) {
+            for (char e : escapeChars) {
+                if (e == ch) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * Check if this character is a valid escape char (i.e. one that has not been escaped)
      *
@@ -216,14 +286,8 @@
         if (pos < 0) {
             return false;
         }
-        if (escapeChars != null) {
-            for (char e : escapeChars) {
-                if (e == buffer.charAt(pos)) {
-                    return !isEscaped(buffer, pos);
-                }
-            }
-        }
-        return false;
+        char ch = buffer.charAt(pos);
+        return isEscapeChar(ch) && !isEscaped(buffer, pos);
     }
 
     /**
@@ -245,7 +309,7 @@
 
     /**
      * Returns true if the character at the specified position if a delimiter. This method will only be called if
-     * the character is not enclosed in any of the {@link #getQuoteChars}, and is not escaped by ant of the
+     * the character is not enclosed in any of the {@link #getQuoteChars}, and is not escaped by any of the
      * {@link #getEscapeChars}. To perform escaping manually, override {@link #isDelimiter} instead.
      *
      * @param buffer
@@ -280,6 +344,67 @@
         return false;
     }
 
+    private class BracketChecker {
+        private int missingOpeningBracket = -1;
+        private List<Integer> nested = new ArrayList<>();
+
+        public BracketChecker(){}
+
+        public void check(final CharSequence buffer, final int pos){
+            if (openingBrackets == null || pos < 0) {
+                return;
+            }
+            int bid = bracketId(openingBrackets, buffer, pos);
+            if (bid >= 0) {
+                nested.add(bid);
+            } else {
+                bid = bracketId(closingBrackets, buffer, pos);
+                if (bid >= 0) {
+                    if (!nested.isEmpty() && bid == nested.get(nested.size()-1)) {
+                        nested.remove(nested.size()-1);
+                    } else {
+                        missingOpeningBracket = bid;
+                    }
+                }
+            }
+        }
+
+        public boolean isOpeningBracketMissing(){
+            return missingOpeningBracket != -1;
+        }
+
+        public String getMissingOpeningBracket(){
+            if (!isOpeningBracketMissing()) {
+                return null;
+            }
+            return Character.toString(openingBrackets[missingOpeningBracket]);
+        }
+
+        public boolean isClosingBracketMissing(){
+            return !nested.isEmpty();
+        }
+
+        public String getMissingClosingBrackets(){
+            if (!isClosingBracketMissing()) {
+                return null;
+            }
+            StringBuilder out = new StringBuilder();
+            for (int i = nested.size() - 1; i > -1; i--) {
+                out.append(closingBrackets[nested.get(i)]);
+            }
+            return out.toString();
+        }
+
+        private int bracketId(final char[] brackets, final CharSequence buffer, final int pos){
+            for (int i=0; i < brackets.length; i++) {
+                if (buffer.charAt(pos) == brackets[i]) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+    }
+
     /**
      * The result of a delimited buffer.
      *
@@ -367,26 +492,50 @@
         public CharSequence escape(CharSequence candidate, boolean complete) {
             StringBuilder sb = new StringBuilder(candidate);
             Predicate<Integer> needToBeEscaped;
-            // Completion is protected by an opening quote:
-            // Delimiters (spaces) don't need to be escaped, nor do other quotes, but everything else does.
-            // Also, close the quote at the end
-            if (openingQuote != null) {
-                needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)) || String.valueOf(sb.charAt(i)).equals(openingQuote);
-            }
-            // No quote protection, need to escape everything: delimiter chars (spaces), quote chars
-            // and escapes themselves
-            else {
-                needToBeEscaped = i -> isDelimiterChar(sb, i) || isRawEscapeChar(sb.charAt(i)) || isRawQuoteChar(sb.charAt(i));
-            }
-            for (int i = 0; i < sb.length(); i++) {
-                if (needToBeEscaped.test(i)) {
-                    sb.insert(i++, escapeChars[0]);
+            String quote = openingQuote;
+            boolean middleQuotes = false;
+            if (openingQuote==null) {
+                for (int i=0; i < sb.length(); i++) {
+                    if (isQuoteChar(sb, i)) {
+                        middleQuotes = true;
+                        break;
+                    }
                 }
             }
-            if (openingQuote != null) {
-                sb.insert(0, openingQuote);
+            if (escapeChars != null) {
+                // Completion is protected by an opening quote:
+                // Delimiters (spaces) don't need to be escaped, nor do other quotes, but everything else does.
+                // Also, close the quote at the end
+                if (openingQuote != null) {
+                    needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)) || String.valueOf(sb.charAt(i)).equals(openingQuote);
+                }
+                // Completion is protected by middle quotes:
+                // Delimiters (spaces) don't need to be escaped, nor do quotes, but everything else does.
+                else if (middleQuotes) {
+                    needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i));
+                }
+                // No quote protection, need to escape everything: delimiter chars (spaces), quote chars
+                // and escapes themselves
+                else {
+                    needToBeEscaped = i -> isDelimiterChar(sb, i) || isRawEscapeChar(sb.charAt(i)) || isRawQuoteChar(sb.charAt(i));
+                }
+                for (int i = 0; i < sb.length(); i++) {
+                    if (needToBeEscaped.test(i)) {
+                        sb.insert(i++, escapeChars[0]);
+                    }
+                }
+            } else if (openingQuote == null && !middleQuotes) {
+                for (int i = 0; i < sb.length(); i++) {
+                    if (isDelimiterChar(sb, i)) {
+                        quote = "'";
+                        break;
+                    }
+                }
+            }
+            if (quote != null) {
+                sb.insert(0, quote);
                 if (complete) {
-                    sb.append(openingQuote);
+                    sb.append(quote);
                 }
             }
             return sb;
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/KillRing.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/KillRing.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2002-2018, the original author or authors.
+ * Copyright (c) 2002-2019, the original author or authors.
  *
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl;
 
@@ -17,10 +17,13 @@
 import java.util.*;
 import java.util.Map.Entry;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
 import jdk.internal.org.jline.keymap.BindingReader;
@@ -32,6 +35,7 @@
 import jdk.internal.org.jline.terminal.Attributes.ControlChar;
 import jdk.internal.org.jline.terminal.Terminal.Signal;
 import jdk.internal.org.jline.terminal.Terminal.SignalHandler;
+import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal;
 import jdk.internal.org.jline.utils.AttributedString;
 import jdk.internal.org.jline.utils.AttributedStringBuilder;
 import jdk.internal.org.jline.utils.AttributedStyle;
@@ -159,8 +163,8 @@
 
     protected final Size size = new Size();
 
-    protected AttributedString prompt;
-    protected AttributedString rightPrompt;
+    protected AttributedString prompt = AttributedString.EMPTY;
+    protected AttributedString rightPrompt = AttributedString.EMPTY;
 
     protected MaskingCallback maskingCallback;
 
@@ -210,6 +214,10 @@
     protected UndoTree<Buffer> undo = new UndoTree<>(this::setBuffer);
     protected boolean isUndo;
 
+    /**
+     * State lock
+     */
+    protected final ReentrantLock lock = new ReentrantLock();
     /*
      * Current internal state of the line reader
      */
@@ -239,6 +247,11 @@
 
     protected int smallTerminalOffset = 0;
 
+    /*
+     * accept-and-infer-next-history, accept-and-hold & accept-line-and-down-history
+     */
+    protected boolean nextCommandFromHistory = false;
+    protected int nextHistoryId = -1;
 
 
     public LineReaderImpl(Terminal terminal) throws IOException {
@@ -266,6 +279,7 @@
         builtinWidgets = builtinWidgets();
         widgets = new HashMap<>(builtinWidgets);
         bindingReader = new BindingReader(terminal.reader());
+        doDisplay();
     }
 
     public Terminal getTerminal() {
@@ -467,8 +481,7 @@
         SignalHandler previousWinchHandler = null;
         SignalHandler previousContHandler = null;
         Attributes originalAttributes = null;
-        boolean dumb = Terminal.TYPE_DUMB.equals(terminal.getType())
-                    || Terminal.TYPE_DUMB_COLOR.equals(terminal.getType());
+        boolean dumb = isTerminalDumb();
         try {
 
             this.maskingCallback = maskingCallback;
@@ -495,6 +508,17 @@
             if (buffer != null) {
                 buf.write(buffer);
             }
+            if (nextCommandFromHistory && nextHistoryId > 0) {
+                if (history.size() > nextHistoryId) {
+                    history.moveTo(nextHistoryId);
+                } else {
+                    history.moveTo(history.last());
+                }
+                buf.write(history.current());
+            } else {
+                nextHistoryId = -1;
+            }
+            nextCommandFromHistory = false;
             undo.clear();
             parsedLine = null;
             keyMap = MAIN;
@@ -503,7 +527,9 @@
                 history.attach(this);
             }
 
-            synchronized (this) {
+            try {
+                lock.lock();
+
                 this.reading = true;
 
                 previousIntrHandler = terminal.handle(Signal.INT, signal -> readLineThread.interrupt());
@@ -511,18 +537,7 @@
                 previousContHandler = terminal.handle(Signal.CONT, this::handleSignal);
                 originalAttributes = terminal.enterRawMode();
 
-                // Cache terminal size for the duration of the call to readLine()
-                // It will eventually be updated with WINCH signals
-                size.copy(terminal.getSize());
-
-                display = new Display(terminal, false);
-                if (size.getRows() == 0 || size.getColumns() == 0) {
-                    display.resize(1, Integer.MAX_VALUE);
-                } else {
-                    display.resize(size.getRows(), size.getColumns());
-                }
-                if (isSet(Option.DELAY_LINE_WRAP))
-                    display.setDelayLineWrap(true);
+                doDisplay();
 
                 // Move into application mode
                 if (!dumb) {
@@ -547,6 +562,8 @@
                 // Draw initial prompt
                 redrawLine();
                 redisplay();
+            } finally {
+                lock.unlock();
             }
 
             while (true) {
@@ -578,7 +595,8 @@
                     regionActive = RegionType.NONE;
                 }
 
-                synchronized (this) {
+                try {
+                    lock.lock();
                     // Get executable widget
                     Buffer copy = buf.copy();
                     Widget w = getWidget(o);
@@ -610,6 +628,8 @@
                     if (!dumb) {
                         redisplay();
                     }
+                } finally {
+                    lock.unlock();
                 }
             }
         } catch (IOError e) {
@@ -620,7 +640,9 @@
             }
         }
         finally {
-            synchronized (this) {
+            try {
+                lock.lock();
+
                 this.reading = false;
 
                 cleanup();
@@ -636,26 +658,54 @@
                 if (previousContHandler != null) {
                     terminal.handle(Signal.CONT, previousContHandler);
                 }
+            } finally {
+                lock.unlock();
             }
             startedReading.set(false);
         }
     }
 
+    private boolean isTerminalDumb(){
+        return Terminal.TYPE_DUMB.equals(terminal.getType())
+                || Terminal.TYPE_DUMB_COLOR.equals(terminal.getType());
+    }
+
+    private void doDisplay(){
+        // Cache terminal size for the duration of the call to readLine()
+        // It will eventually be updated with WINCH signals
+        size.copy(terminal.getBufferSize());
+
+        display = new Display(terminal, false);
+        if (size.getRows() == 0 || size.getColumns() == 0) {
+            display.resize(1, Integer.MAX_VALUE);
+        } else {
+            display.resize(size.getRows(), size.getColumns());
+        }
+        if (isSet(Option.DELAY_LINE_WRAP))
+            display.setDelayLineWrap(true);
+    }
+
     @Override
-    public synchronized void printAbove(String str) {
-        boolean reading = this.reading;
-        if (reading) {
-            display.update(Collections.emptyList(), 0);
-        }
-        if (str.endsWith("\n")) {
-            terminal.writer().print(str);
-        } else {
-            terminal.writer().println(str);
-        }
-        if (reading) {
-            redisplay(false);
-        }
-        terminal.flush();
+    public void printAbove(String str) {
+        try {
+            lock.lock();
+
+            boolean reading = this.reading;
+            if (reading) {
+                display.update(Collections.emptyList(), 0);
+            }
+            if (str.endsWith("\n") || str.endsWith("\n\033[m") || str.endsWith("\n\033[0m")) {
+                terminal.writer().print(str);
+            } else {
+                terminal.writer().println(str);
+            }
+            if (reading) {
+                redisplay(false);
+            }
+            terminal.flush();
+        } finally {
+            lock.unlock();
+        }
     }
 
     @Override
@@ -664,8 +714,13 @@
     }
 
     @Override
-    public synchronized boolean isReading() {
-        return reading;
+    public boolean isReading() {
+        try {
+            lock.lock();
+            return reading;
+        } finally {
+            lock.unlock();
+        }
     }
 
     /* Make sure we position the cursor on column 0 */
@@ -700,27 +755,32 @@
             sb.append(" ");
             sb.append(KeyMap.key(terminal, Capability.carriage_return));
         }
-        print(sb.toAnsi(terminal));
+        sb.print(terminal);
         return true;
     }
 
     @Override
-    public synchronized void callWidget(String name) {
-        if (!reading) {
-            throw new IllegalStateException("Widgets can only be called during a `readLine` call");
-        }
+    public void callWidget(String name) {
         try {
-            Widget w;
-            if (name.startsWith(".")) {
-                w = builtinWidgets.get(name.substring(1));
-            } else {
-                w = widgets.get(name);
-            }
-            if (w != null) {
-                w.apply();
-            }
-        } catch (Throwable t) {
-            Log.debug("Error executing widget '", name, "'", t);
+            lock.lock();
+            if (!reading) {
+                throw new IllegalStateException("Widgets can only be called during a `readLine` call");
+            }
+            try {
+                Widget w;
+                if (name.startsWith(".")) {
+                    w = builtinWidgets.get(name.substring(1));
+                } else {
+                    w = widgets.get(name);
+                }
+                if (w != null) {
+                    w.apply();
+                }
+            } catch (Throwable t) {
+                Log.debug("Error executing widget '", name, "'", t);
+            }
+        } finally {
+            lock.unlock();
         }
     }
 
@@ -760,13 +820,35 @@
      * @return the character, or -1 if an EOF is received.
      */
     public int readCharacter() {
-        return bindingReader.readCharacter();
+        if (lock.isHeldByCurrentThread()) {
+            try {
+                lock.unlock();
+                return bindingReader.readCharacter();
+            } finally {
+                lock.lock();
+            }
+        } else {
+            return bindingReader.readCharacter();
+        }
     }
 
     public int peekCharacter(long timeout) {
         return bindingReader.peekCharacter(timeout);
     }
 
+    protected <T> T doReadBinding(KeyMap<T> keys, KeyMap<T> local) {
+        if (lock.isHeldByCurrentThread()) {
+            try {
+                lock.unlock();
+                return bindingReader.readBinding(keys, local);
+            } finally {
+                lock.lock();
+            }
+        } else {
+            return bindingReader.readBinding(keys, local);
+        }
+    }
+
     /**
      * Read from the input stream and decode an operation from the key map.
      *
@@ -783,7 +865,7 @@
     }
 
     public Binding readBinding(KeyMap<Binding> keys, KeyMap<Binding> local) {
-        Binding o = bindingReader.readBinding(keys, local);
+        Binding o = doReadBinding(keys, local);
         /*
          * The kill ring keeps record of whether or not the
          * previous command was a yank or a kill. We reset
@@ -926,7 +1008,7 @@
                     if (ch != '\n') {
                         sb.append(ch);
                     }
-                } else if (ch == '\\') {
+                } else if (parser.isEscapeChar(ch)) {
                     escaped = true;
                 } else {
                     sb.append(ch);
@@ -948,13 +1030,18 @@
 
     protected void handleSignal(Signal signal) {
         if (signal == Signal.WINCH) {
-            size.copy(terminal.getSize());
+            Status status = Status.getStatus(terminal, false);
+            if (status != null) {
+                status.hardReset();
+            }
+            size.copy(terminal.getBufferSize());
             display.resize(size.getRows(), size.getColumns());
+            redrawLine();
             redisplay();
         }
         else if (signal == Signal.CONT) {
             terminal.enterRawMode();
-            size.copy(terminal.getSize());
+            size.copy(terminal.getBufferSize());
             display.resize(size.getRows(), size.getColumns());
             terminal.puts(Capability.keypad_xmit);
             redrawLine();
@@ -1903,7 +1990,7 @@
         while (true) {
             post = () -> new AttributedString(searchPrompt + searchBuffer.toString() + "_");
             redisplay();
-            Binding b = bindingReader.readBinding(keyMap);
+            Binding b = doReadBinding(keyMap, null);
             if (b instanceof Reference) {
                 String func = ((Reference) b).name();
                 switch (func) {
@@ -2300,7 +2387,7 @@
         } else {
             viMoveMode = mode;
             mark = -1;
-            Binding b = bindingReader.readBinding(getKeys(), keyMaps.get(VIOPP));
+            Binding b = doReadBinding(getKeys(), keyMaps.get(VIOPP));
             if (b == null || new Reference(SEND_BREAK).equals(b)) {
                 viMoveMode = ViMoveMode.NORMAL;
                 mark = oldMark;
@@ -2710,6 +2797,42 @@
         return acceptLine();
     }
 
+    protected boolean acceptAndHold() {
+        nextCommandFromHistory = false;
+        acceptLine();
+        if (!buf.toString().isEmpty()) {
+            nextHistoryId = Integer.MAX_VALUE;
+            nextCommandFromHistory = true;
+        }
+        return nextCommandFromHistory;
+    }
+
+    protected boolean acceptLineAndDownHistory() {
+        nextCommandFromHistory = false;
+        acceptLine();
+        if (nextHistoryId < 0) {
+            nextHistoryId = history.index();
+        }
+        if (history.size() > nextHistoryId + 1) {
+            nextHistoryId++;
+            nextCommandFromHistory = true;
+        }
+        return nextCommandFromHistory;
+    }
+
+    protected boolean acceptAndInferNextHistory() {
+        nextCommandFromHistory = false;
+        acceptLine();
+        if (!buf.toString().isEmpty()) {
+            nextHistoryId = searchBackwards(buf.toString(), history.last());
+            if (nextHistoryId >= 0 && history.size() > nextHistoryId + 1) {
+                nextHistoryId++;
+                nextCommandFromHistory = true;
+            }
+        }
+        return nextCommandFromHistory;
+    }
+
     protected boolean acceptLine() {
         parsedLine = null;
         if (!isSet(Option.DISABLE_EVENT_EXPANSION)) {
@@ -3343,255 +3466,317 @@
 
     protected Map<String, Widget> builtinWidgets() {
         Map<String, Widget> widgets = new HashMap<>();
-        widgets.put(ACCEPT_LINE, this::acceptLine);
-        widgets.put(ARGUMENT_BASE, this::argumentBase);
-        widgets.put(BACKWARD_CHAR, this::backwardChar);
-        widgets.put(BACKWARD_DELETE_CHAR, this::backwardDeleteChar);
-        widgets.put(BACKWARD_DELETE_WORD, this::backwardDeleteWord);
-        widgets.put(BACKWARD_KILL_LINE, this::backwardKillLine);
-        widgets.put(BACKWARD_KILL_WORD, this::backwardKillWord);
-        widgets.put(BACKWARD_WORD, this::backwardWord);
-        widgets.put(BEEP, this::beep);
-        widgets.put(BEGINNING_OF_BUFFER_OR_HISTORY, this::beginningOfBufferOrHistory);
-        widgets.put(BEGINNING_OF_HISTORY, this::beginningOfHistory);
-        widgets.put(BEGINNING_OF_LINE, this::beginningOfLine);
-        widgets.put(BEGINNING_OF_LINE_HIST, this::beginningOfLineHist);
-        widgets.put(CAPITALIZE_WORD, this::capitalizeWord);
-        widgets.put(CLEAR, this::clear);
-        widgets.put(CLEAR_SCREEN, this::clearScreen);
-        widgets.put(COMPLETE_PREFIX, this::completePrefix);
-        widgets.put(COMPLETE_WORD, this::completeWord);
-        widgets.put(COPY_PREV_WORD, this::copyPrevWord);
-        widgets.put(COPY_REGION_AS_KILL, this::copyRegionAsKill);
-        widgets.put(DELETE_CHAR, this::deleteChar);
-        widgets.put(DELETE_CHAR_OR_LIST, this::deleteCharOrList);
-        widgets.put(DELETE_WORD, this::deleteWord);
-        widgets.put(DIGIT_ARGUMENT, this::digitArgument);
-        widgets.put(DO_LOWERCASE_VERSION, this::doLowercaseVersion);
-        widgets.put(DOWN_CASE_WORD, this::downCaseWord);
-        widgets.put(DOWN_LINE, this::downLine);
-        widgets.put(DOWN_LINE_OR_HISTORY, this::downLineOrHistory);
-        widgets.put(DOWN_LINE_OR_SEARCH, this::downLineOrSearch);
-        widgets.put(DOWN_HISTORY, this::downHistory);
-        widgets.put(EMACS_EDITING_MODE, this::emacsEditingMode);
-        widgets.put(EMACS_BACKWARD_WORD, this::emacsBackwardWord);
-        widgets.put(EMACS_FORWARD_WORD, this::emacsForwardWord);
-        widgets.put(END_OF_BUFFER_OR_HISTORY, this::endOfBufferOrHistory);
-        widgets.put(END_OF_HISTORY, this::endOfHistory);
-        widgets.put(END_OF_LINE, this::endOfLine);
-        widgets.put(END_OF_LINE_HIST, this::endOfLineHist);
-        widgets.put(EXCHANGE_POINT_AND_MARK, this::exchangePointAndMark);
-        widgets.put(EXPAND_HISTORY, this::expandHistory);
-        widgets.put(EXPAND_OR_COMPLETE, this::expandOrComplete);
-        widgets.put(EXPAND_OR_COMPLETE_PREFIX, this::expandOrCompletePrefix);
-        widgets.put(EXPAND_WORD, this::expandWord);
-        widgets.put(FRESH_LINE, this::freshLine);
-        widgets.put(FORWARD_CHAR, this::forwardChar);
-        widgets.put(FORWARD_WORD, this::forwardWord);
-        widgets.put(HISTORY_INCREMENTAL_SEARCH_BACKWARD, this::historyIncrementalSearchBackward);
-        widgets.put(HISTORY_INCREMENTAL_SEARCH_FORWARD, this::historyIncrementalSearchForward);
-        widgets.put(HISTORY_SEARCH_BACKWARD, this::historySearchBackward);
-        widgets.put(HISTORY_SEARCH_FORWARD, this::historySearchForward);
-        widgets.put(INSERT_CLOSE_CURLY, this::insertCloseCurly);
-        widgets.put(INSERT_CLOSE_PAREN, this::insertCloseParen);
-        widgets.put(INSERT_CLOSE_SQUARE, this::insertCloseSquare);
-        widgets.put(INSERT_COMMENT, this::insertComment);
-        widgets.put(KILL_BUFFER, this::killBuffer);
-        widgets.put(KILL_LINE, this::killLine);
-        widgets.put(KILL_REGION, this::killRegion);
-        widgets.put(KILL_WHOLE_LINE, this::killWholeLine);
-        widgets.put(KILL_WORD, this::killWord);
-        widgets.put(LIST_CHOICES, this::listChoices);
-        widgets.put(MENU_COMPLETE, this::menuComplete);
-        widgets.put(MENU_EXPAND_OR_COMPLETE, this::menuExpandOrComplete);
-        widgets.put(NEG_ARGUMENT, this::negArgument);
-        widgets.put(OVERWRITE_MODE, this::overwriteMode);
-//        widgets.put(QUIT, this::quit);
-        widgets.put(QUOTED_INSERT, this::quotedInsert);
-        widgets.put(REDISPLAY, this::redisplay);
-        widgets.put(REDRAW_LINE, this::redrawLine);
-        widgets.put(REDO, this::redo);
-        widgets.put(SELF_INSERT, this::selfInsert);
-        widgets.put(SELF_INSERT_UNMETA, this::selfInsertUnmeta);
-        widgets.put(SEND_BREAK, this::sendBreak);
-        widgets.put(SET_MARK_COMMAND, this::setMarkCommand);
-        widgets.put(TRANSPOSE_CHARS, this::transposeChars);
-        widgets.put(TRANSPOSE_WORDS, this::transposeWords);
-        widgets.put(UNDEFINED_KEY, this::undefinedKey);
-        widgets.put(UNIVERSAL_ARGUMENT, this::universalArgument);
-        widgets.put(UNDO, this::undo);
-        widgets.put(UP_CASE_WORD, this::upCaseWord);
-        widgets.put(UP_HISTORY, this::upHistory);
-        widgets.put(UP_LINE, this::upLine);
-        widgets.put(UP_LINE_OR_HISTORY, this::upLineOrHistory);
-        widgets.put(UP_LINE_OR_SEARCH, this::upLineOrSearch);
-        widgets.put(VI_ADD_EOL, this::viAddEol);
-        widgets.put(VI_ADD_NEXT, this::viAddNext);
-        widgets.put(VI_BACKWARD_CHAR, this::viBackwardChar);
-        widgets.put(VI_BACKWARD_DELETE_CHAR, this::viBackwardDeleteChar);
-        widgets.put(VI_BACKWARD_BLANK_WORD, this::viBackwardBlankWord);
-        widgets.put(VI_BACKWARD_BLANK_WORD_END, this::viBackwardBlankWordEnd);
-        widgets.put(VI_BACKWARD_KILL_WORD, this::viBackwardKillWord);
-        widgets.put(VI_BACKWARD_WORD, this::viBackwardWord);
-        widgets.put(VI_BACKWARD_WORD_END, this::viBackwardWordEnd);
-        widgets.put(VI_BEGINNING_OF_LINE, this::viBeginningOfLine);
-        widgets.put(VI_CMD_MODE, this::viCmdMode);
-        widgets.put(VI_DIGIT_OR_BEGINNING_OF_LINE, this::viDigitOrBeginningOfLine);
-        widgets.put(VI_DOWN_LINE_OR_HISTORY, this::viDownLineOrHistory);
-        widgets.put(VI_CHANGE, this::viChange);
-        widgets.put(VI_CHANGE_EOL, this::viChangeEol);
-        widgets.put(VI_CHANGE_WHOLE_LINE, this::viChangeWholeLine);
-        widgets.put(VI_DELETE_CHAR, this::viDeleteChar);
-        widgets.put(VI_DELETE, this::viDelete);
-        widgets.put(VI_END_OF_LINE, this::viEndOfLine);
-        widgets.put(VI_KILL_EOL, this::viKillEol);
-        widgets.put(VI_FIRST_NON_BLANK, this::viFirstNonBlank);
-        widgets.put(VI_FIND_NEXT_CHAR, this::viFindNextChar);
-        widgets.put(VI_FIND_NEXT_CHAR_SKIP, this::viFindNextCharSkip);
-        widgets.put(VI_FIND_PREV_CHAR, this::viFindPrevChar);
-        widgets.put(VI_FIND_PREV_CHAR_SKIP, this::viFindPrevCharSkip);
-        widgets.put(VI_FORWARD_BLANK_WORD, this::viForwardBlankWord);
-        widgets.put(VI_FORWARD_BLANK_WORD_END, this::viForwardBlankWordEnd);
-        widgets.put(VI_FORWARD_CHAR, this::viForwardChar);
-        widgets.put(VI_FORWARD_WORD, this::viForwardWord);
-        widgets.put(VI_FORWARD_WORD, this::viForwardWord);
-        widgets.put(VI_FORWARD_WORD_END, this::viForwardWordEnd);
-        widgets.put(VI_HISTORY_SEARCH_BACKWARD, this::viHistorySearchBackward);
-        widgets.put(VI_HISTORY_SEARCH_FORWARD, this::viHistorySearchForward);
-        widgets.put(VI_INSERT, this::viInsert);
-        widgets.put(VI_INSERT_BOL, this::viInsertBol);
-        widgets.put(VI_INSERT_COMMENT, this::viInsertComment);
-        widgets.put(VI_JOIN, this::viJoin);
-        widgets.put(VI_KILL_LINE, this::viKillWholeLine);
-        widgets.put(VI_MATCH_BRACKET, this::viMatchBracket);
-        widgets.put(VI_OPEN_LINE_ABOVE, this::viOpenLineAbove);
-        widgets.put(VI_OPEN_LINE_BELOW, this::viOpenLineBelow);
-        widgets.put(VI_PUT_AFTER, this::viPutAfter);
-        widgets.put(VI_PUT_BEFORE, this::viPutBefore);
-        widgets.put(VI_REPEAT_FIND, this::viRepeatFind);
-        widgets.put(VI_REPEAT_SEARCH, this::viRepeatSearch);
-        widgets.put(VI_REPLACE_CHARS, this::viReplaceChars);
-        widgets.put(VI_REV_REPEAT_FIND, this::viRevRepeatFind);
-        widgets.put(VI_REV_REPEAT_SEARCH, this::viRevRepeatSearch);
-        widgets.put(VI_SWAP_CASE, this::viSwapCase);
-        widgets.put(VI_UP_LINE_OR_HISTORY, this::viUpLineOrHistory);
-        widgets.put(VI_YANK, this::viYankTo);
-        widgets.put(VI_YANK_WHOLE_LINE, this::viYankWholeLine);
-        widgets.put(VISUAL_LINE_MODE, this::visualLineMode);
-        widgets.put(VISUAL_MODE, this::visualMode);
-        widgets.put(WHAT_CURSOR_POSITION, this::whatCursorPosition);
-        widgets.put(YANK, this::yank);
-        widgets.put(YANK_POP, this::yankPop);
-        widgets.put(MOUSE, this::mouse);
-        widgets.put(BEGIN_PASTE, this::beginPaste);
-        widgets.put(FOCUS_IN, this::focusIn);
-        widgets.put(FOCUS_OUT, this::focusOut);
+        addBuiltinWidget(widgets, ACCEPT_AND_INFER_NEXT_HISTORY, this::acceptAndInferNextHistory);
+        addBuiltinWidget(widgets, ACCEPT_AND_HOLD, this::acceptAndHold);
+        addBuiltinWidget(widgets, ACCEPT_LINE, this::acceptLine);
+        addBuiltinWidget(widgets, ACCEPT_LINE_AND_DOWN_HISTORY, this::acceptLineAndDownHistory);
+        addBuiltinWidget(widgets, ARGUMENT_BASE, this::argumentBase);
+        addBuiltinWidget(widgets, BACKWARD_CHAR, this::backwardChar);
+        addBuiltinWidget(widgets, BACKWARD_DELETE_CHAR, this::backwardDeleteChar);
+        addBuiltinWidget(widgets, BACKWARD_DELETE_WORD, this::backwardDeleteWord);
+        addBuiltinWidget(widgets, BACKWARD_KILL_LINE, this::backwardKillLine);
+        addBuiltinWidget(widgets, BACKWARD_KILL_WORD, this::backwardKillWord);
+        addBuiltinWidget(widgets, BACKWARD_WORD, this::backwardWord);
+        addBuiltinWidget(widgets, BEEP, this::beep);
+        addBuiltinWidget(widgets, BEGINNING_OF_BUFFER_OR_HISTORY, this::beginningOfBufferOrHistory);
+        addBuiltinWidget(widgets, BEGINNING_OF_HISTORY, this::beginningOfHistory);
+        addBuiltinWidget(widgets, BEGINNING_OF_LINE, this::beginningOfLine);
+        addBuiltinWidget(widgets, BEGINNING_OF_LINE_HIST, this::beginningOfLineHist);
+        addBuiltinWidget(widgets, CAPITALIZE_WORD, this::capitalizeWord);
+        addBuiltinWidget(widgets, CLEAR, this::clear);
+        addBuiltinWidget(widgets, CLEAR_SCREEN, this::clearScreen);
+        addBuiltinWidget(widgets, COMPLETE_PREFIX, this::completePrefix);
+        addBuiltinWidget(widgets, COMPLETE_WORD, this::completeWord);
+        addBuiltinWidget(widgets, COPY_PREV_WORD, this::copyPrevWord);
+        addBuiltinWidget(widgets, COPY_REGION_AS_KILL, this::copyRegionAsKill);
+        addBuiltinWidget(widgets, DELETE_CHAR, this::deleteChar);
+        addBuiltinWidget(widgets, DELETE_CHAR_OR_LIST, this::deleteCharOrList);
+        addBuiltinWidget(widgets, DELETE_WORD, this::deleteWord);
+        addBuiltinWidget(widgets, DIGIT_ARGUMENT, this::digitArgument);
+        addBuiltinWidget(widgets, DO_LOWERCASE_VERSION, this::doLowercaseVersion);
+        addBuiltinWidget(widgets, DOWN_CASE_WORD, this::downCaseWord);
+        addBuiltinWidget(widgets, DOWN_LINE, this::downLine);
+        addBuiltinWidget(widgets, DOWN_LINE_OR_HISTORY, this::downLineOrHistory);
+        addBuiltinWidget(widgets, DOWN_LINE_OR_SEARCH, this::downLineOrSearch);
+        addBuiltinWidget(widgets, DOWN_HISTORY, this::downHistory);
+        addBuiltinWidget(widgets, EMACS_EDITING_MODE, this::emacsEditingMode);
+        addBuiltinWidget(widgets, EMACS_BACKWARD_WORD, this::emacsBackwardWord);
+        addBuiltinWidget(widgets, EMACS_FORWARD_WORD, this::emacsForwardWord);
+        addBuiltinWidget(widgets, END_OF_BUFFER_OR_HISTORY, this::endOfBufferOrHistory);
+        addBuiltinWidget(widgets, END_OF_HISTORY, this::endOfHistory);
+        addBuiltinWidget(widgets, END_OF_LINE, this::endOfLine);
+        addBuiltinWidget(widgets, END_OF_LINE_HIST, this::endOfLineHist);
+        addBuiltinWidget(widgets, EXCHANGE_POINT_AND_MARK, this::exchangePointAndMark);
+        addBuiltinWidget(widgets, EXPAND_HISTORY, this::expandHistory);
+        addBuiltinWidget(widgets, EXPAND_OR_COMPLETE, this::expandOrComplete);
+        addBuiltinWidget(widgets, EXPAND_OR_COMPLETE_PREFIX, this::expandOrCompletePrefix);
+        addBuiltinWidget(widgets, EXPAND_WORD, this::expandWord);
+        addBuiltinWidget(widgets, FRESH_LINE, this::freshLine);
+        addBuiltinWidget(widgets, FORWARD_CHAR, this::forwardChar);
+        addBuiltinWidget(widgets, FORWARD_WORD, this::forwardWord);
+        addBuiltinWidget(widgets, HISTORY_INCREMENTAL_SEARCH_BACKWARD, this::historyIncrementalSearchBackward);
+        addBuiltinWidget(widgets, HISTORY_INCREMENTAL_SEARCH_FORWARD, this::historyIncrementalSearchForward);
+        addBuiltinWidget(widgets, HISTORY_SEARCH_BACKWARD, this::historySearchBackward);
+        addBuiltinWidget(widgets, HISTORY_SEARCH_FORWARD, this::historySearchForward);
+        addBuiltinWidget(widgets, INSERT_CLOSE_CURLY, this::insertCloseCurly);
+        addBuiltinWidget(widgets, INSERT_CLOSE_PAREN, this::insertCloseParen);
+        addBuiltinWidget(widgets, INSERT_CLOSE_SQUARE, this::insertCloseSquare);
+        addBuiltinWidget(widgets, INSERT_COMMENT, this::insertComment);
+        addBuiltinWidget(widgets, KILL_BUFFER, this::killBuffer);
+        addBuiltinWidget(widgets, KILL_LINE, this::killLine);
+        addBuiltinWidget(widgets, KILL_REGION, this::killRegion);
+        addBuiltinWidget(widgets, KILL_WHOLE_LINE, this::killWholeLine);
+        addBuiltinWidget(widgets, KILL_WORD, this::killWord);
+        addBuiltinWidget(widgets, LIST_CHOICES, this::listChoices);
+        addBuiltinWidget(widgets, MENU_COMPLETE, this::menuComplete);
+        addBuiltinWidget(widgets, MENU_EXPAND_OR_COMPLETE, this::menuExpandOrComplete);
+        addBuiltinWidget(widgets, NEG_ARGUMENT, this::negArgument);
+        addBuiltinWidget(widgets, OVERWRITE_MODE, this::overwriteMode);
+//        addBuiltinWidget(widgets, QUIT, this::quit);
+        addBuiltinWidget(widgets, QUOTED_INSERT, this::quotedInsert);
+        addBuiltinWidget(widgets, REDISPLAY, this::redisplay);
+        addBuiltinWidget(widgets, REDRAW_LINE, this::redrawLine);
+        addBuiltinWidget(widgets, REDO, this::redo);
+        addBuiltinWidget(widgets, SELF_INSERT, this::selfInsert);
+        addBuiltinWidget(widgets, SELF_INSERT_UNMETA, this::selfInsertUnmeta);
+        addBuiltinWidget(widgets, SEND_BREAK, this::sendBreak);
+        addBuiltinWidget(widgets, SET_MARK_COMMAND, this::setMarkCommand);
+        addBuiltinWidget(widgets, TRANSPOSE_CHARS, this::transposeChars);
+        addBuiltinWidget(widgets, TRANSPOSE_WORDS, this::transposeWords);
+        addBuiltinWidget(widgets, UNDEFINED_KEY, this::undefinedKey);
+        addBuiltinWidget(widgets, UNIVERSAL_ARGUMENT, this::universalArgument);
+        addBuiltinWidget(widgets, UNDO, this::undo);
+        addBuiltinWidget(widgets, UP_CASE_WORD, this::upCaseWord);
+        addBuiltinWidget(widgets, UP_HISTORY, this::upHistory);
+        addBuiltinWidget(widgets, UP_LINE, this::upLine);
+        addBuiltinWidget(widgets, UP_LINE_OR_HISTORY, this::upLineOrHistory);
+        addBuiltinWidget(widgets, UP_LINE_OR_SEARCH, this::upLineOrSearch);
+        addBuiltinWidget(widgets, VI_ADD_EOL, this::viAddEol);
+        addBuiltinWidget(widgets, VI_ADD_NEXT, this::viAddNext);
+        addBuiltinWidget(widgets, VI_BACKWARD_CHAR, this::viBackwardChar);
+        addBuiltinWidget(widgets, VI_BACKWARD_DELETE_CHAR, this::viBackwardDeleteChar);
+        addBuiltinWidget(widgets, VI_BACKWARD_BLANK_WORD, this::viBackwardBlankWord);
+        addBuiltinWidget(widgets, VI_BACKWARD_BLANK_WORD_END, this::viBackwardBlankWordEnd);
+        addBuiltinWidget(widgets, VI_BACKWARD_KILL_WORD, this::viBackwardKillWord);
+        addBuiltinWidget(widgets, VI_BACKWARD_WORD, this::viBackwardWord);
+        addBuiltinWidget(widgets, VI_BACKWARD_WORD_END, this::viBackwardWordEnd);
+        addBuiltinWidget(widgets, VI_BEGINNING_OF_LINE, this::viBeginningOfLine);
+        addBuiltinWidget(widgets, VI_CMD_MODE, this::viCmdMode);
+        addBuiltinWidget(widgets, VI_DIGIT_OR_BEGINNING_OF_LINE, this::viDigitOrBeginningOfLine);
+        addBuiltinWidget(widgets, VI_DOWN_LINE_OR_HISTORY, this::viDownLineOrHistory);
+        addBuiltinWidget(widgets, VI_CHANGE, this::viChange);
+        addBuiltinWidget(widgets, VI_CHANGE_EOL, this::viChangeEol);
+        addBuiltinWidget(widgets, VI_CHANGE_WHOLE_LINE, this::viChangeWholeLine);
+        addBuiltinWidget(widgets, VI_DELETE_CHAR, this::viDeleteChar);
+        addBuiltinWidget(widgets, VI_DELETE, this::viDelete);
+        addBuiltinWidget(widgets, VI_END_OF_LINE, this::viEndOfLine);
+        addBuiltinWidget(widgets, VI_KILL_EOL, this::viKillEol);
+        addBuiltinWidget(widgets, VI_FIRST_NON_BLANK, this::viFirstNonBlank);
+        addBuiltinWidget(widgets, VI_FIND_NEXT_CHAR, this::viFindNextChar);
+        addBuiltinWidget(widgets, VI_FIND_NEXT_CHAR_SKIP, this::viFindNextCharSkip);
+        addBuiltinWidget(widgets, VI_FIND_PREV_CHAR, this::viFindPrevChar);
+        addBuiltinWidget(widgets, VI_FIND_PREV_CHAR_SKIP, this::viFindPrevCharSkip);
+        addBuiltinWidget(widgets, VI_FORWARD_BLANK_WORD, this::viForwardBlankWord);
+        addBuiltinWidget(widgets, VI_FORWARD_BLANK_WORD_END, this::viForwardBlankWordEnd);
+        addBuiltinWidget(widgets, VI_FORWARD_CHAR, this::viForwardChar);
+        addBuiltinWidget(widgets, VI_FORWARD_WORD, this::viForwardWord);
+        addBuiltinWidget(widgets, VI_FORWARD_WORD, this::viForwardWord);
+        addBuiltinWidget(widgets, VI_FORWARD_WORD_END, this::viForwardWordEnd);
+        addBuiltinWidget(widgets, VI_HISTORY_SEARCH_BACKWARD, this::viHistorySearchBackward);
+        addBuiltinWidget(widgets, VI_HISTORY_SEARCH_FORWARD, this::viHistorySearchForward);
+        addBuiltinWidget(widgets, VI_INSERT, this::viInsert);
+        addBuiltinWidget(widgets, VI_INSERT_BOL, this::viInsertBol);
+        addBuiltinWidget(widgets, VI_INSERT_COMMENT, this::viInsertComment);
+        addBuiltinWidget(widgets, VI_JOIN, this::viJoin);
+        addBuiltinWidget(widgets, VI_KILL_LINE, this::viKillWholeLine);
+        addBuiltinWidget(widgets, VI_MATCH_BRACKET, this::viMatchBracket);
+        addBuiltinWidget(widgets, VI_OPEN_LINE_ABOVE, this::viOpenLineAbove);
+        addBuiltinWidget(widgets, VI_OPEN_LINE_BELOW, this::viOpenLineBelow);
+        addBuiltinWidget(widgets, VI_PUT_AFTER, this::viPutAfter);
+        addBuiltinWidget(widgets, VI_PUT_BEFORE, this::viPutBefore);
+        addBuiltinWidget(widgets, VI_REPEAT_FIND, this::viRepeatFind);
+        addBuiltinWidget(widgets, VI_REPEAT_SEARCH, this::viRepeatSearch);
+        addBuiltinWidget(widgets, VI_REPLACE_CHARS, this::viReplaceChars);
+        addBuiltinWidget(widgets, VI_REV_REPEAT_FIND, this::viRevRepeatFind);
+        addBuiltinWidget(widgets, VI_REV_REPEAT_SEARCH, this::viRevRepeatSearch);
+        addBuiltinWidget(widgets, VI_SWAP_CASE, this::viSwapCase);
+        addBuiltinWidget(widgets, VI_UP_LINE_OR_HISTORY, this::viUpLineOrHistory);
+        addBuiltinWidget(widgets, VI_YANK, this::viYankTo);
+        addBuiltinWidget(widgets, VI_YANK_WHOLE_LINE, this::viYankWholeLine);
+        addBuiltinWidget(widgets, VISUAL_LINE_MODE, this::visualLineMode);
+        addBuiltinWidget(widgets, VISUAL_MODE, this::visualMode);
+        addBuiltinWidget(widgets, WHAT_CURSOR_POSITION, this::whatCursorPosition);
+        addBuiltinWidget(widgets, YANK, this::yank);
+        addBuiltinWidget(widgets, YANK_POP, this::yankPop);
+        addBuiltinWidget(widgets, MOUSE, this::mouse);
+        addBuiltinWidget(widgets, BEGIN_PASTE, this::beginPaste);
+        addBuiltinWidget(widgets, FOCUS_IN, this::focusIn);
+        addBuiltinWidget(widgets, FOCUS_OUT, this::focusOut);
         return widgets;
     }
 
+    private void addBuiltinWidget(Map<String, Widget> widgets, String name, Widget widget) {
+        widgets.put(name, namedWidget(name, widget));
+    }
+
+    private Widget namedWidget(String name, Widget widget) {
+        return new Widget() {
+            @Override
+            public String toString() {
+                return name;
+            }
+            @Override
+            public boolean apply() {
+                return widget.apply();
+            }
+        };
+    }
+
     public boolean redisplay() {
         redisplay(true);
         return true;
     }
 
-    protected synchronized void redisplay(boolean flush) {
-        if (skipRedisplay) {
-            skipRedisplay = false;
-            return;
-        }
-
-        Status status = Status.getStatus(terminal, false);
-        if (status != null) {
-            status.redraw();
-        }
-
-        if (size.getRows() > 0 && size.getRows() < MIN_ROWS) {
-            AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH);
-
-            sb.append(prompt);
-            concat(getHighlightedBuffer(buf.toString()).columnSplitLength(Integer.MAX_VALUE), sb);
-            AttributedString full = sb.toAttributedString();
-
-            sb.setLength(0);
-            sb.append(prompt);
-            String line = buf.upToCursor();
-            if(maskingCallback != null) {
-                line = maskingCallback.display(line);
-            }
-
-            concat(new AttributedString(line).columnSplitLength(Integer.MAX_VALUE), sb);
-            AttributedString toCursor = sb.toAttributedString();
-
-            int w = WCWidth.wcwidth('\u2026');
-            int width = size.getColumns();
-            int cursor = toCursor.columnLength();
-            int inc = width /2 + 1;
-            while (cursor <= smallTerminalOffset + w) {
-                smallTerminalOffset -= inc;
-            }
-            while (cursor >= smallTerminalOffset + width - w) {
-                smallTerminalOffset += inc;
-            }
-            if (smallTerminalOffset > 0) {
-                sb.setLength(0);
-                sb.append("\u2026");
-                sb.append(full.columnSubSequence(smallTerminalOffset + w, Integer.MAX_VALUE));
-                full = sb.toAttributedString();
-            }
-            int length = full.columnLength();
-            if (length >= smallTerminalOffset + width) {
+    protected void redisplay(boolean flush) {
+        try {
+            lock.lock();
+
+            if (skipRedisplay) {
+                skipRedisplay = false;
+                return;
+            }
+
+            Status status = Status.getStatus(terminal, false);
+            if (status != null) {
+                status.redraw();
+            }
+
+            if (size.getRows() > 0 && size.getRows() < MIN_ROWS) {
+                AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH);
+
+                sb.append(prompt);
+                concat(getHighlightedBuffer(buf.toString()).columnSplitLength(Integer.MAX_VALUE), sb);
+                AttributedString full = sb.toAttributedString();
+
                 sb.setLength(0);
-                sb.append(full.columnSubSequence(0, width - w));
-                sb.append("\u2026");
-                full = sb.toAttributedString();
-            }
-
-            display.update(Collections.singletonList(full), cursor - smallTerminalOffset, flush);
-            return;
-        }
-
-        List<AttributedString> secondaryPrompts = new ArrayList<>();
-        AttributedString full = getDisplayedBufferWithPrompts(secondaryPrompts);
-
-        List<AttributedString> newLines;
-        if (size.getColumns() <= 0) {
-            newLines = new ArrayList<>();
-            newLines.add(full);
-        } else {
-            newLines = full.columnSplitLength(size.getColumns(), true, display.delayLineWrap());
-        }
-
-        List<AttributedString> rightPromptLines;
-        if (rightPrompt.length() == 0 || size.getColumns() <= 0) {
-            rightPromptLines = new ArrayList<>();
-        } else {
-            rightPromptLines = rightPrompt.columnSplitLength(size.getColumns());
-        }
-        while (newLines.size() < rightPromptLines.size()) {
-            newLines.add(new AttributedString(""));
-        }
-        for (int i = 0; i < rightPromptLines.size(); i++) {
-            AttributedString line = rightPromptLines.get(i);
-            newLines.set(i, addRightPrompt(line, newLines.get(i)));
-        }
-
-        int cursorPos = -1;
-        if (size.getColumns() > 0) {
-            AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH);
-            sb.append(prompt);
-            String buffer = buf.upToCursor();
-            if (maskingCallback != null) {
-                buffer = maskingCallback.display(buffer);
-            }
-            sb.append(insertSecondaryPrompts(new AttributedString(buffer), secondaryPrompts, false));
-            List<AttributedString> promptLines = sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap());
-            if (!promptLines.isEmpty()) {
-                cursorPos = size.cursorPos(promptLines.size() - 1,
-                                           promptLines.get(promptLines.size() - 1).columnLength());
-            }
-        }
-
-        display.update(newLines, cursorPos, flush);
+                sb.append(prompt);
+                String line = buf.upToCursor();
+                if (maskingCallback != null) {
+                    line = maskingCallback.display(line);
+                }
+
+                concat(new AttributedString(line).columnSplitLength(Integer.MAX_VALUE), sb);
+                AttributedString toCursor = sb.toAttributedString();
+
+                int w = WCWidth.wcwidth('\u2026');
+                int width = size.getColumns();
+                int cursor = toCursor.columnLength();
+                int inc = width / 2 + 1;
+                while (cursor <= smallTerminalOffset + w) {
+                    smallTerminalOffset -= inc;
+                }
+                while (cursor >= smallTerminalOffset + width - w) {
+                    smallTerminalOffset += inc;
+                }
+                if (smallTerminalOffset > 0) {
+                    sb.setLength(0);
+                    sb.append("\u2026");
+                    sb.append(full.columnSubSequence(smallTerminalOffset + w, Integer.MAX_VALUE));
+                    full = sb.toAttributedString();
+                }
+                int length = full.columnLength();
+                if (length >= smallTerminalOffset + width) {
+                    sb.setLength(0);
+                    sb.append(full.columnSubSequence(0, width - w));
+                    sb.append("\u2026");
+                    full = sb.toAttributedString();
+                }
+
+                display.update(Collections.singletonList(full), cursor - smallTerminalOffset, flush);
+                return;
+            }
+
+            List<AttributedString> secondaryPrompts = new ArrayList<>();
+            AttributedString full = getDisplayedBufferWithPrompts(secondaryPrompts);
+
+            List<AttributedString> newLines;
+            if (size.getColumns() <= 0) {
+                newLines = new ArrayList<>();
+                newLines.add(full);
+            } else {
+                newLines = full.columnSplitLength(size.getColumns(), true, display.delayLineWrap());
+            }
+
+            List<AttributedString> rightPromptLines;
+            if (rightPrompt.length() == 0 || size.getColumns() <= 0) {
+                rightPromptLines = new ArrayList<>();
+            } else {
+                rightPromptLines = rightPrompt.columnSplitLength(size.getColumns());
+            }
+            while (newLines.size() < rightPromptLines.size()) {
+                newLines.add(new AttributedString(""));
+            }
+            for (int i = 0; i < rightPromptLines.size(); i++) {
+                AttributedString line = rightPromptLines.get(i);
+                newLines.set(i, addRightPrompt(line, newLines.get(i)));
+            }
+
+            int cursorPos = -1;
+            int cursorNewLinesId = -1;
+            int cursorColPos = -1;
+            if (size.getColumns() > 0) {
+                AttributedStringBuilder sb = new AttributedStringBuilder().tabs(TAB_WIDTH);
+                sb.append(prompt);
+                String buffer = buf.upToCursor();
+                if (maskingCallback != null) {
+                    buffer = maskingCallback.display(buffer);
+                }
+                sb.append(insertSecondaryPrompts(new AttributedString(buffer), secondaryPrompts, false));
+                List<AttributedString> promptLines = sb.columnSplitLength(size.getColumns(), false, display.delayLineWrap());
+                if (!promptLines.isEmpty()) {
+                    cursorNewLinesId = promptLines.size() - 1;
+                    cursorColPos = promptLines.get(promptLines.size() - 1).columnLength();
+                    cursorPos = size.cursorPos(cursorNewLinesId, cursorColPos);
+                }
+            }
+
+            List<AttributedString> newLinesToDisplay = new ArrayList<>();
+            int displaySize = size.getRows() - (status != null ? status.size() : 0);
+            if (newLines.size() > displaySize && !isTerminalDumb()) {
+                StringBuilder sb = new StringBuilder(">....");
+                // blanks are needed when displaying command completion candidate list
+                for (int i = sb.toString().length(); i < size.getColumns(); i++) {
+                    sb.append(" ");
+                }
+                AttributedString partialCommandInfo = new AttributedString(sb.toString());
+                int lineId = newLines.size() - displaySize + 1;
+                int endId = displaySize;
+                int startId = 1;
+                if (lineId  > cursorNewLinesId) {
+                    lineId = cursorNewLinesId;
+                    endId = displaySize - 1;
+                    startId = 0;
+                } else {
+                    newLinesToDisplay.add(partialCommandInfo);
+                }
+                int cursorRowPos = 0;
+                for (int i = startId; i < endId; i++) {
+                    if (cursorNewLinesId == lineId) {
+                        cursorRowPos = i;
+                    }
+                    newLinesToDisplay.add(newLines.get(lineId++));
+                }
+                if (startId == 0) {
+                    newLinesToDisplay.add(partialCommandInfo);
+                }
+                cursorPos = size.cursorPos(cursorRowPos, cursorColPos);
+            } else {
+                newLinesToDisplay = newLines;
+            }
+            display.update(newLinesToDisplay, cursorPos, flush);
+        } finally {
+            lock.unlock();
+        }
     }
 
     private void concat(List<AttributedString> lines, AttributedStringBuilder sb) {
@@ -3656,26 +3841,26 @@
                 decode: while (true) {
                     ch = pattern.charAt(i++);
                     switch (ch) {
-                       case '{':
-                       case '}':
-                           String str = sb.toString();
-                           AttributedString astr;
-                           if (!isHidden) {
-                               astr = AttributedString.fromAnsi(str);
-                               cols += astr.columnLength();
-                           } else {
-                               astr = new AttributedString(str, AttributedStyle.HIDDEN);
-                           }
-                           if (padPartIndex == parts.size()) {
-                               padPartString = sb;
-                               if (i < plen) {
-                                   sb = new StringBuilder();
-                               }
-                           } else {
-                               sb.setLength(0);
-                           }
-                           parts.add(astr);
-                           isHidden = ch == '{';
+                        case '{':
+                        case '}':
+                            String str = sb.toString();
+                            AttributedString astr;
+                            if (!isHidden) {
+                                astr = AttributedString.fromAnsi(str);
+                                cols += astr.columnLength();
+                            } else {
+                                astr = new AttributedString(str, AttributedStyle.HIDDEN);
+                            }
+                            if (padPartIndex == parts.size()) {
+                                padPartString = sb;
+                                if (i < plen) {
+                                    sb = new StringBuilder();
+                                }
+                            } else {
+                                sb.setLength(0);
+                            }
+                            parts.add(astr);
+                            isHidden = ch == '{';
                             break decode;
                         case '%':
                             sb.append(ch);
@@ -4048,113 +4233,117 @@
         if (matching.isEmpty()) {
             return false;
         }
-
-        // If we only need to display the list, do it now
-        if (lst == CompletionType.List) {
+        size.copy(terminal.getSize());
+        try {
+            // If we only need to display the list, do it now
+            if (lst == CompletionType.List) {
+                List<Candidate> possible = matching.entrySet().stream()
+                        .flatMap(e -> e.getValue().stream())
+                        .collect(Collectors.toList());
+                doList(possible, line.word(), false, line::escape);
+                return !possible.isEmpty();
+            }
+
+            // Check if there's a single possible match
+            Candidate completion = null;
+            // If there's a single possible completion
+            if (matching.size() == 1) {
+                completion = matching.values().stream().flatMap(Collection::stream)
+                        .findFirst().orElse(null);
+            }
+            // Or if RECOGNIZE_EXACT is set, try to find an exact match
+            else if (isSet(Option.RECOGNIZE_EXACT)) {
+                completion = matching.values().stream().flatMap(Collection::stream)
+                        .filter(Candidate::complete)
+                        .filter(c -> exact.test(c.value()))
+                        .findFirst().orElse(null);
+            }
+            // Complete and exit
+            if (completion != null && !completion.value().isEmpty()) {
+                if (prefix) {
+                    buf.backspace(line.rawWordCursor());
+                } else {
+                    buf.move(line.rawWordLength() - line.rawWordCursor());
+                    buf.backspace(line.rawWordLength());
+                }
+                buf.write(line.escape(completion.value(), completion.complete()));
+                if (completion.complete()) {
+                    if (buf.currChar() != ' ') {
+                        buf.write(" ");
+                    } else {
+                        buf.move(1);
+                    }
+                }
+                if (completion.suffix() != null) {
+                    redisplay();
+                    Binding op = readBinding(getKeys());
+                    if (op != null) {
+                        String chars = getString(REMOVE_SUFFIX_CHARS, DEFAULT_REMOVE_SUFFIX_CHARS);
+                        String ref = op instanceof Reference ? ((Reference) op).name() : null;
+                        if (SELF_INSERT.equals(ref) && chars.indexOf(getLastBinding().charAt(0)) >= 0
+                                || ACCEPT_LINE.equals(ref)) {
+                            buf.backspace(completion.suffix().length());
+                            if (getLastBinding().charAt(0) != ' ') {
+                                buf.write(' ');
+                            }
+                        }
+                        pushBackBinding(true);
+                    }
+                }
+                return true;
+            }
+
             List<Candidate> possible = matching.entrySet().stream()
                     .flatMap(e -> e.getValue().stream())
                     .collect(Collectors.toList());
-            doList(possible, line.word(), false, line::escape);
-            return !possible.isEmpty();
-        }
-
-        // Check if there's a single possible match
-        Candidate completion = null;
-        // If there's a single possible completion
-        if (matching.size() == 1) {
-            completion = matching.values().stream().flatMap(Collection::stream)
-                    .findFirst().orElse(null);
-        }
-        // Or if RECOGNIZE_EXACT is set, try to find an exact match
-        else if (isSet(Option.RECOGNIZE_EXACT)) {
-            completion = matching.values().stream().flatMap(Collection::stream)
-                    .filter(Candidate::complete)
-                    .filter(c -> exact.test(c.value()))
-                    .findFirst().orElse(null);
-        }
-        // Complete and exit
-        if (completion != null && !completion.value().isEmpty()) {
+
+            if (useMenu) {
+                buf.move(line.word().length() - line.wordCursor());
+                buf.backspace(line.word().length());
+                doMenu(possible, line.word(), line::escape);
+                return true;
+            }
+
+            // Find current word and move to end
+            String current;
             if (prefix) {
-                buf.backspace(line.rawWordCursor());
+                current = line.word().substring(0, line.wordCursor());
             } else {
+                current = line.word();
                 buf.move(line.rawWordLength() - line.rawWordCursor());
+            }
+            // Now, we need to find the unambiguous completion
+            // TODO: need to find common suffix
+            String commonPrefix = null;
+            for (String key : matching.keySet()) {
+                commonPrefix = commonPrefix == null ? key : getCommonStart(commonPrefix, key, caseInsensitive);
+            }
+            boolean hasUnambiguous = commonPrefix.startsWith(current) && !commonPrefix.equals(current);
+
+            if (hasUnambiguous) {
                 buf.backspace(line.rawWordLength());
-            }
-            buf.write(line.escape(completion.value(), completion.complete()));
-            if (completion.complete()) {
-                if (buf.currChar() != ' ') {
-                    buf.write(" ");
-                } else {
-                    buf.move(1);
+                buf.write(line.escape(commonPrefix, false));
+                current = commonPrefix;
+                if ((!isSet(Option.AUTO_LIST) && isSet(Option.AUTO_MENU))
+                        || (isSet(Option.AUTO_LIST) && isSet(Option.LIST_AMBIGUOUS))) {
+                    if (!nextBindingIsComplete()) {
+                        return true;
+                    }
                 }
             }
-            if (completion.suffix() != null) {
-                redisplay();
-                Binding op = readBinding(getKeys());
-                if (op != null) {
-                    String chars = getString(REMOVE_SUFFIX_CHARS, DEFAULT_REMOVE_SUFFIX_CHARS);
-                    String ref = op instanceof Reference ? ((Reference) op).name() : null;
-                    if (SELF_INSERT.equals(ref) && chars.indexOf(getLastBinding().charAt(0)) >= 0
-                            || ACCEPT_LINE.equals(ref)) {
-                        buf.backspace(completion.suffix().length());
-                        if (getLastBinding().charAt(0) != ' ') {
-                            buf.write(' ');
-                        }
-                    }
-                    pushBackBinding(true);
+            if (isSet(Option.AUTO_LIST)) {
+                if (!doList(possible, current, true, line::escape)) {
+                    return true;
                 }
             }
+            if (isSet(Option.AUTO_MENU)) {
+                buf.backspace(current.length());
+                doMenu(possible, line.word(), line::escape);
+            }
             return true;
-        }
-
-        List<Candidate> possible = matching.entrySet().stream()
-                .flatMap(e -> e.getValue().stream())
-                .collect(Collectors.toList());
-
-        if (useMenu) {
-            buf.move(line.word().length() - line.wordCursor());
-            buf.backspace(line.word().length());
-            doMenu(possible, line.word(), line::escape);
-            return true;
-        }
-
-        // Find current word and move to end
-        String current;
-        if (prefix) {
-            current = line.word().substring(0, line.wordCursor());
-        } else {
-            current = line.word();
-            buf.move(line.rawWordLength() - line.rawWordCursor());
-        }
-        // Now, we need to find the unambiguous completion
-        // TODO: need to find common suffix
-        String commonPrefix = null;
-        for (String key : matching.keySet()) {
-            commonPrefix = commonPrefix == null ? key : getCommonStart(commonPrefix, key, caseInsensitive);
-        }
-        boolean hasUnambiguous = commonPrefix.startsWith(current) && !commonPrefix.equals(current);
-
-        if (hasUnambiguous) {
-            buf.backspace(line.rawWordLength());
-            buf.write(line.escape(commonPrefix, false));
-            current = commonPrefix;
-            if ((!isSet(Option.AUTO_LIST) && isSet(Option.AUTO_MENU))
-                    || (isSet(Option.AUTO_LIST) && isSet(Option.LIST_AMBIGUOUS))) {
-                if (!nextBindingIsComplete()) {
-                    return true;
-                }
-            }
-        }
-        if (isSet(Option.AUTO_LIST)) {
-            if (!doList(possible, current, true, line::escape)) {
-                return true;
-            }
-        }
-        if (isSet(Option.AUTO_MENU)) {
-            buf.backspace(current.length());
-            doMenu(possible, line.word(), line::escape);
-        }
-        return true;
+        } finally {
+            size.copy(terminal.getBufferSize());
+        }
     }
 
     private CompletingParsedLine wrap(ParsedLine line) {
@@ -4534,7 +4723,7 @@
         if (listMax > 0 && possible.size() >= listMax
                 || lines >= size.getRows() - promptLines) {
             // prompt
-            post = () -> new AttributedString(getAppName() + ": do you wish to see to see all " + possible.size()
+            post = () -> new AttributedString(getAppName() + ": do you wish to see all " + possible.size()
                     + " possibilities (" + lines + " lines)?");
             redisplay(true);
             int c = readCharacter();
@@ -4586,7 +4775,7 @@
             }
             redisplay();
             // TODO: use a different keyMap ?
-            Binding b = bindingReader.readBinding(getKeys());
+            Binding b = doReadBinding(getKeys(), null);
             if (b instanceof Reference) {
                 String name = ((Reference) b).name();
                 if (BACKWARD_DELETE_CHAR.equals(name) || VI_BACKWARD_DELETE_CHAR.equals(name)) {
@@ -4731,7 +4920,7 @@
 
     @SuppressWarnings("unchecked")
     protected void toColumns(Object items, int width, int maxWidth, AttributedStringBuilder sb, Candidate selection, String completed, boolean rowsFirst, int[] out) {
-        if (maxWidth <= 0) {
+        if (maxWidth <= 0 || width <= 0) {
             return;
         }
         // This is a group
@@ -4985,7 +5174,9 @@
                 while (end < buf.length() && buf.atChar(end) != '\n') {
                     end++;
                 }
-                end++;
+                if (end < buf.length()) {
+                    end++;
+                }
             }
         }
         String killed = buf.substring(start, end);
@@ -5188,7 +5379,7 @@
         keyMap.bind(END_PASTE, BRACKETED_PASTE_END);
         StringBuilder sb = new StringBuilder();
         while (true) {
-            Object b = bindingReader.readBinding(keyMap);
+            Object b = doReadBinding(keyMap, null);
             if (b == END_PASTE) {
                 break;
             }
@@ -5227,6 +5418,11 @@
      */
     public boolean clearScreen() {
         if (terminal.puts(Capability.clear_screen)) {
+            // ConEMU extended fonts support
+            if (AbstractWindowsTerminal.TYPE_WINDOWS_CONEMU.equals(terminal.getType())
+                    && !Boolean.getBoolean("org.jline.terminal.conemu.disable-activate")) {
+                terminal.writer().write("\u001b[9999E");
+            }
             Status status = Status.getStatus(terminal, false);
             if (status != null) {
                 status.reset();
@@ -5358,6 +5554,7 @@
 
     public KeyMap<Binding> emacs() {
         KeyMap<Binding> emacs = new KeyMap<>();
+        bindKeys(emacs);
         bind(emacs, SET_MARK_COMMAND,                       ctrl('@'));
         bind(emacs, BEGINNING_OF_LINE,                      ctrl('A'));
         bind(emacs, BACKWARD_CHAR,                          ctrl('B'));
@@ -5372,6 +5569,7 @@
         bind(emacs, CLEAR_SCREEN,                           ctrl('L'));
         bind(emacs, ACCEPT_LINE,                            ctrl('M'));
         bind(emacs, DOWN_LINE_OR_HISTORY,                   ctrl('N'));
+        bind(emacs, ACCEPT_LINE_AND_DOWN_HISTORY,           ctrl('O'));
         bind(emacs, UP_LINE_OR_HISTORY,                     ctrl('P'));
         bind(emacs, HISTORY_INCREMENTAL_SEARCH_BACKWARD,    ctrl('R'));
         bind(emacs, HISTORY_INCREMENTAL_SEARCH_FORWARD,     ctrl('S'));
@@ -5415,6 +5613,7 @@
         bind(emacs, END_OF_HISTORY,                         alt('>'));
         bind(emacs, LIST_CHOICES,                           alt('?'));
         bind(emacs, DO_LOWERCASE_VERSION,                   range("^[A-^[Z"));
+        bind(emacs, ACCEPT_AND_HOLD,                        alt('a'));
         bind(emacs, BACKWARD_WORD,                          alt('b'));
         bind(emacs, CAPITALIZE_WORD,                        alt('c'));
         bind(emacs, KILL_WORD,                              alt('d'));
@@ -5439,6 +5638,7 @@
 
     public KeyMap<Binding> viInsertion() {
         KeyMap<Binding> viins = new KeyMap<>();
+        bindKeys(viins);
         bind(viins, SELF_INSERT,                            range("^@-^_"));
         bind(viins, LIST_CHOICES,                           ctrl('D'));
         bind(viins, SEND_BREAK,                             ctrl('G'));
@@ -5638,6 +5838,14 @@
         return KeyMap.key(terminal, capability);
     }
 
+    private void bindKeys(KeyMap<Binding> emacs) {
+        Widget beep = namedWidget("beep", this::beep);
+        Stream.of(Capability.values())
+                .filter(c -> c.name().startsWith("key_"))
+                .map(this::key)
+                .forEach(k -> bind(emacs, beep, k));
+    }
+
     private void bindArrowKeys(KeyMap<Binding> map) {
         bind(map, UP_LINE_OR_SEARCH,    key(Capability.key_up));
         bind(map, DOWN_LINE_OR_SEARCH,  key(Capability.key_down));
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/SimpleMaskingCallback.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/SimpleMaskingCallback.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/UndoTree.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/UndoTree.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/AggregateCompleter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/AggregateCompleter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl.completer;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/ArgumentCompleter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/ArgumentCompleter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl.completer;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/EnumCompleter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/EnumCompleter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl.completer;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/FileNameCompleter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/FileNameCompleter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl.completer;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/NullCompleter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/NullCompleter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl.completer;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl.completer;
 
@@ -43,6 +43,11 @@
         }
     }
 
+    public StringsCompleter(Candidate ... candidates) {
+        assert candidates != null;
+        this.candidates.addAll(Arrays.asList(candidates));
+    }
+
     public void complete(LineReader reader, final ParsedLine commandLine, final List<Candidate> candidates) {
         assert commandLine != null;
         assert candidates != null;
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/package-info.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/package-info.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 /**
  * JLine 3.
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,12 +4,13 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.reader.impl.history;
 
 import java.io.*;
 import java.nio.file.*;
+import java.time.DateTimeException;
 import java.time.Instant;
 import java.util.*;
 
@@ -36,8 +37,7 @@
 
     private LineReader reader;
 
-    private int lastLoaded = 0;
-    private int nbEntriesInFile = 0;
+    private Map<String, HistoryFileData> historyFiles = new HashMap<>();
     private int offset = 0;
     private int index = 0;
 
@@ -68,7 +68,7 @@
             try {
                 load();
             }
-            catch (IOException e) {
+            catch (IllegalArgumentException | IOException e) {
                 Log.warn("Failed to load history", e);
             }
         }
@@ -84,12 +84,11 @@
                     try (BufferedReader reader = Files.newBufferedReader(path)) {
                         internalClear();
                         reader.lines().forEach(line -> addHistoryLine(path, line));
-                        lastLoaded = items.size();
-                        nbEntriesInFile = lastLoaded;
+                        setHistoryFileData(path, new HistoryFileData(items.size(), items.size()));
                         maybeResize();
                     }
                 }
-            } catch (IOException e) {
+            } catch (IllegalArgumentException | IOException e) {
                 Log.debug("Failed to load history; clearing", e);
                 internalClear();
                 throw e;
@@ -97,20 +96,100 @@
         }
     }
 
+    @Override
+    public void read(Path file, boolean incremental) throws IOException {
+        Path path = file != null ? file : getPath();
+        if (path != null) {
+            try {
+                if (Files.exists(path)) {
+                    Log.trace("Reading history from: ", path);
+                    try (BufferedReader reader = Files.newBufferedReader(path)) {
+                        reader.lines().forEach(line -> addHistoryLine(path, line, incremental));
+                        setHistoryFileData(path, new HistoryFileData(items.size(), items.size()));
+                        maybeResize();
+                    }
+                }
+            } catch (IllegalArgumentException | IOException e) {
+                Log.debug("Failed to read history; clearing", e);
+                internalClear();
+                throw e;
+            }
+        }
+    }
+
+    private String doHistoryFileDataKey (Path path){
+        return path != null ? path.toAbsolutePath().toString() : null;
+    }
+
+    private HistoryFileData getHistoryFileData(Path path) {
+        String key = doHistoryFileDataKey(path);
+        if (!historyFiles.containsKey(key)){
+            historyFiles.put(key, new HistoryFileData());
+        }
+        return historyFiles.get(key);
+    }
+
+    private void setHistoryFileData(Path path, HistoryFileData historyFileData) {
+        historyFiles.put(doHistoryFileDataKey(path), historyFileData);
+    }
+
+    private boolean isLineReaderHistory (Path path) throws IOException {
+        Path lrp = getPath();
+        if (lrp == null) {
+            if (path != null) {
+                return false;
+            } else {
+                return true;
+            }
+        }
+        return Files.isSameFile(lrp, path);
+    }
+
+    private void setLastLoaded(Path path, int lastloaded){
+        getHistoryFileData(path).setLastLoaded(lastloaded);
+    }
+
+    private void setEntriesInFile(Path path, int entriesInFile){
+        getHistoryFileData(path).setEntriesInFile(entriesInFile);
+    }
+
+    private void incEntriesInFile(Path path, int amount){
+        getHistoryFileData(path).incEntriesInFile(amount);
+    }
+
+    private int getLastLoaded(Path path){
+        return getHistoryFileData(path).getLastLoaded();
+    }
+
+    private int getEntriesInFile(Path path){
+        return getHistoryFileData(path).getEntriesInFile();
+    }
+
     protected void addHistoryLine(Path path, String line) {
+        addHistoryLine(path, line, false);
+    }
+
+    protected void addHistoryLine(Path path, String line, boolean checkDuplicates) {
         if (reader.isSet(LineReader.Option.HISTORY_TIMESTAMPED)) {
             int idx = line.indexOf(':');
+            final String badHistoryFileSyntax = "Bad history file syntax! " +
+                "The history file `" + path + "` may be an older history: " +
+                "please remove it or use a different history file.";
             if (idx < 0) {
-                throw new IllegalArgumentException("Bad history file syntax! " +
-                        "The history file `" + path + "` may be an older history: " +
-                        "please remove it or use a different history file.");
+                throw new IllegalArgumentException(badHistoryFileSyntax);
             }
-            Instant time = Instant.ofEpochMilli(Long.parseLong(line.substring(0, idx)));
+            Instant time;
+            try {
+                time = Instant.ofEpochMilli(Long.parseLong(line.substring(0, idx)));
+            } catch (DateTimeException | NumberFormatException e) {
+                throw new IllegalArgumentException(badHistoryFileSyntax);
+            }
+
             String unescaped = unescape(line.substring(idx + 1));
-            internalAdd(time, unescaped);
+            internalAdd(time, unescaped, checkDuplicates);
         }
         else {
-            internalAdd(Instant.now(), unescape(line));
+            internalAdd(Instant.now(), unescape(line), checkDuplicates);
         }
     }
 
@@ -125,28 +204,45 @@
     }
 
     @Override
+    public void write(Path file, boolean incremental) throws IOException {
+        Path path = file != null ? file : getPath();
+        if (path != null && Files.exists(path)) {
+            path.toFile().delete();
+        }
+        internalWrite(path, incremental ? getLastLoaded(path) : 0);
+    }
+
+    @Override
+    public void append(Path file, boolean incremental) throws IOException {
+        internalWrite(file != null ? file : getPath(),
+                      incremental ? getLastLoaded(file) : 0);
+    }
+
+    @Override
     public void save() throws IOException {
-        Path path = getPath();
+        internalWrite(getPath(), getLastLoaded(getPath()));
+    }
+
+    private void internalWrite(Path path, int from) throws IOException {
         if (path != null) {
             Log.trace("Saving history to: ", path);
             Files.createDirectories(path.toAbsolutePath().getParent());
             // Append new items to the history file
             try (BufferedWriter writer = Files.newBufferedWriter(path.toAbsolutePath(),
               StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) {
-                for (Entry entry : items.subList(lastLoaded, items.size())) {
+                for (Entry entry : items.subList(from, items.size())) {
                     if (isPersistable(entry)) {
                         writer.append(format(entry));
                     }
                 }
             }
-            nbEntriesInFile += items.size() - lastLoaded;
-            // If we are over 25% max size, trim history file
+            incEntriesInFile(path, items.size() - from);
             int max = getInt(reader, LineReader.HISTORY_FILE_SIZE, DEFAULT_HISTORY_FILE_SIZE);
-            if (nbEntriesInFile > max + max / 4) {
+            if (getEntriesInFile(path) > max + max / 4) {
                 trimHistory(path, max);
             }
         }
-        lastLoaded = items.size();
+        setLastLoaded(path, items.size());
     }
 
     protected void trimHistory(Path path, int max) throws IOException {
@@ -172,11 +268,14 @@
         }
         Files.move(temp, path, StandardCopyOption.REPLACE_EXISTING);
         // Keep items in memory
-        internalClear();
-        offset = allItems.get(0).index();
-        items.addAll(allItems);
-        lastLoaded = items.size();
-        nbEntriesInFile = items.size();
+        if (isLineReaderHistory(path)) {
+            internalClear();
+            offset = allItems.get(0).index();
+            items.addAll(allItems);
+            setHistoryFileData(path, new HistoryFileData(items.size(), items.size()));
+        } else {
+            setEntriesInFile(path, allItems.size());
+        }
         maybeResize();
     }
 
@@ -194,8 +293,7 @@
     private void internalClear() {
         offset = 0;
         index = 0;
-        lastLoaded = 0;
-        nbEntriesInFile = 0;
+        historyFiles = new HashMap<>();
         items.clear();
     }
 
@@ -302,7 +400,18 @@
     }
 
     protected void internalAdd(Instant time, String line) {
+        internalAdd(time, line, false);
+    }
+
+    protected void internalAdd(Instant time, String line, boolean checkDuplicates) {
         Entry entry = new EntryImpl(offset + items.size(), time, line);
+        if (checkDuplicates) {
+            for (Entry e: items) {
+                if (e.line().trim().equals(line.trim())) {
+                    return;
+                }
+            }
+        }
         items.add(entry);
         maybeResize();
     }
@@ -310,7 +419,9 @@
     private void maybeResize() {
         while (size() > getInt(reader, LineReader.HISTORY_SIZE, DEFAULT_HISTORY_SIZE)) {
             items.removeFirst();
-            lastLoaded--;
+            for (HistoryFileData hfd: historyFiles.values()) {
+                hfd.decLastLoaded();
+            }
             offset++;
         }
         index = size();
@@ -503,5 +614,46 @@
         return sb.toString();
     }
 
+    private class HistoryFileData {
+        private int lastLoaded = 0;
+        private int entriesInFile = 0;
+
+        public HistoryFileData() {
+        }
+
+        public HistoryFileData(int lastLoaded, int entriesInFile) {
+            this.lastLoaded = lastLoaded;
+            this.entriesInFile = entriesInFile;
+        }
+
+        public int getLastLoaded() {
+            return lastLoaded;
+        }
+
+        public void setLastLoaded(int lastLoaded) {
+            this.lastLoaded = lastLoaded;
+        }
+
+        public void decLastLoaded() {
+            lastLoaded = lastLoaded - 1;
+            if (lastLoaded < 0) {
+                lastLoaded = 0;
+            }
+        }
+
+        public int getEntriesInFile() {
+            return entriesInFile;
+        }
+
+        public void setEntriesInFile(int entriesInFile) {
+            this.entriesInFile = entriesInFile;
+        }
+
+        public void incEntriesInFile(int amount) {
+            entriesInFile = entriesInFile + amount;
+        }
+
+    }
+
 }
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/package-info.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/package-info.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 /**
  * JLine 3.
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/package-info.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/package-info.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 /**
  * JLine 3.
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Attributes.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Attributes.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Cursor.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Cursor.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/MouseEvent.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/MouseEvent.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Size.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Size.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal;
 
@@ -111,7 +111,7 @@
      *
      * @return The output stream
      *
-     * @see #writer();
+     * @see #writer()
      */
     OutputStream output();
 
@@ -183,6 +183,11 @@
 
     void setAttributes(Attributes attr);
 
+    /**
+     * Retrieve the size of the visible window
+     * @return the visible terminal size
+     * @see #getBufferSize()
+     */
     Size getSize();
 
     void setSize(Size size);
@@ -195,6 +200,22 @@
         return getSize().getRows();
     }
 
+    /**
+     * Retrieve the size of the window buffer.
+     * Some terminals can be configured to have a buffer size
+     * larger than the visible window size and provide scroll bars.
+     * In such cases, this method should attempt to return the size
+     * of the whole buffer.  The <code>getBufferSize()</code> method
+     * can be used to avoid wrapping when using the terminal in a line
+     * editing mode, while the {@link #getSize()} method should be
+     * used when using full screen mode.
+     * @return the terminal buffer size
+     * @see #getSize()
+     */
+    default Size getBufferSize() {
+        return getSize();
+    }
+
     void flush();
 
     //
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2002-2018, the original author or authors.
+ * Copyright (c) 2002-2019, the original author or authors.
  *
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal;
 
@@ -33,9 +33,6 @@
 import jdk.internal.org.jline.utils.Log;
 import jdk.internal.org.jline.utils.OSUtils;
 
-import static jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal.TYPE_WINDOWS;
-import static jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal.TYPE_WINDOWS_256_COLOR;
-
 /**
  * Builder class to create terminals.
  */
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPosixTerminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPosixTerminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPty.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractPty.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,3 +1,11 @@
+/*
+ * Copyright (c) 2002-2019, the original author or authors.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
 package jdk.internal.org.jline.terminal.impl;
 
 import jdk.internal.org.jline.terminal.Attributes;
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsConsoleWriter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsConsoleWriter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2002-2018, the original author or authors.
+ * Copyright (c) 2002-2019, the original author or authors.
  *
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
@@ -21,12 +21,10 @@
 import jdk.internal.org.jline.utils.Signals;
 import jdk.internal.org.jline.utils.WriterOutputStream;
 
-import java.io.IOError;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.io.Writer;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
@@ -50,6 +48,7 @@
 
     public static final String TYPE_WINDOWS = "windows";
     public static final String TYPE_WINDOWS_256_COLOR = "windows-256color";
+    public static final String TYPE_WINDOWS_CONEMU = "windows-conemu";
     public static final String TYPE_WINDOWS_VTP = "windows-vtp";
 
     public static final int ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
@@ -110,7 +109,7 @@
         closer = this::close;
         ShutdownHooks.add(closer);
         // ConEMU extended fonts support
-        if (TYPE_WINDOWS_256_COLOR.equals(getType())
+        if (TYPE_WINDOWS_CONEMU.equals(getType())
                 && !Boolean.getBoolean("org.jline.terminal.conemu.disable-activate")) {
             writer.write("\u001b[9999E");
             writer.flush();
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/CursorSupport.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/CursorSupport.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/DumbTerminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/DumbTerminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExecPty.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExecPty.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2002-2019, the original author or authors.
+ * Copyright (c) 2002-2016, the original author or authors.
  *
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
@@ -137,15 +137,12 @@
         }
         String undef = System.getProperty("os.name").toLowerCase().startsWith("hp") ? "^-" : "undef";
         for (ControlChar cchar : ControlChar.values()) {
-            if (attr.getControlChar(cchar) != current.getControlChar(cchar)) {
+            int v = attr.getControlChar(cchar);
+            if (v >= 0 && v != current.getControlChar(cchar)) {
                 String str = "";
-                int v = attr.getControlChar(cchar);
-                if (v == -1) { // Skip if ControlChar is <UNDEF>
-                    continue;
-                }
                 commands.add(cchar.name().toLowerCase().substring(1));
                 if (cchar == ControlChar.VMIN || cchar == ControlChar.VTIME) {
-                    commands.add(Integer.toBinaryString(v));
+                    commands.add(Integer.toString(v));
                 }
                 else if (v == 0) {
                     commands.add(undef);
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExternalTerminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/ExternalTerminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/LineDisciplineTerminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/LineDisciplineTerminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/MouseSupport.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/MouseSupport.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/NativeSignalHandler.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/NativeSignalHandler.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixPtyTerminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixPtyTerminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixSysTerminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixSysTerminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/package-info.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/package-info.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 /**
  * JLine 3.
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JansiSupport.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JansiSupport.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,3 +1,11 @@
+/*
+ * Copyright (c) 2002-2019, the original author or authors.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
 package jdk.internal.org.jline.terminal.spi;
 
 import jdk.internal.org.jline.terminal.Attributes;
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JnaSupport.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JnaSupport.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,3 +1,11 @@
+/*
+ * Copyright (c) 2002-2019, the original author or authors.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
 package jdk.internal.org.jline.terminal.spi;
 
 import jdk.internal.org.jline.terminal.Attributes;
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/Pty.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/Pty.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.spi;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2002-2016, the original author or authors.
+ * Copyright (c) 2002-2019, the original author or authors.
  *
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
@@ -38,6 +38,14 @@
     // cache the value here as we can't afford to get it each time
     static final boolean DISABLE_ALTERNATE_CHARSET = Boolean.getBoolean(PROP_DISABLE_ALTERNATE_CHARSET);
 
+    public void print(Terminal terminal) {
+        terminal.writer().print(toAnsi(terminal));
+    }
+
+    public void println(Terminal terminal) {
+        terminal.writer().println(toAnsi(terminal));
+    }
+
     public String toAnsi() {
         return toAnsi(null);
     }
@@ -54,7 +62,8 @@
             if (max_colors != null) {
                 colors = max_colors;
             }
-            force256colors = AbstractWindowsTerminal.TYPE_WINDOWS_256_COLOR.equals(terminal.getType());
+            force256colors = AbstractWindowsTerminal.TYPE_WINDOWS_256_COLOR.equals(terminal.getType())
+                || AbstractWindowsTerminal.TYPE_WINDOWS_CONEMU.equals(terminal.getType());
             if (!DISABLE_ALTERNATE_CHARSET) {
                 alternateIn = Curses.tputs(terminal.getStringCapability(Capability.enter_alt_charset_mode));
                 alternateOut = Curses.tputs(terminal.getStringCapability(Capability.exit_alt_charset_mode));
@@ -293,7 +302,7 @@
             if (col + w > start) {
                 break;
             }
-            begin++;
+            begin += Character.charCount(cp);
             col += w;
         }
         int end = begin;
@@ -305,7 +314,7 @@
             if (col + w > stop) {
                 break;
             }
-            end++;
+            end += Character.charCount(cp);
             col += w;
         }
         return subSequence(begin, end);
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,12 +4,13 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
 import java.security.InvalidParameterException;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -98,6 +99,10 @@
     }
 
     public static AttributedString fromAnsi(String ansi, int tabs) {
+        return fromAnsi(ansi, Arrays.asList(tabs));
+    }
+
+    public static AttributedString fromAnsi(String ansi, List<Integer> tabs) {
         if (ansi == null) {
             return null;
         }
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,11 +4,13 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.regex.Matcher;
@@ -24,7 +26,7 @@
     private char[] buffer;
     private int[] style;
     private int length;
-    private int tabs = 0;
+    private TabStops tabs = new TabStops(0);
     private int lastLineLength = 0;
     private AttributedStyle current = AttributedStyle.DEFAULT;
 
@@ -151,7 +153,7 @@
         for (int i = start; i < end; i++) {
             char c = str.charAt(i);
             int s = str.styleCodeAt(i) & ~current.getMask() | current.getStyle();
-            if (tabs > 0 && c == '\t') {
+            if (tabs.defined() && c == '\t') {
                 insertTab(new AttributedStyle(s, 0));
             } else {
                 ensureCapacity(length + 1);
@@ -332,7 +334,7 @@
                     // This is not a SGR code, so ignore
                     ansiState = 0;
                 }
-            } else if (c == '\t' && tabs > 0) {
+            } else if (c == '\t' && tabs.defined()) {
                 insertTab(current);
             } else {
                 ensureCapacity(length + 1);
@@ -350,7 +352,7 @@
     }
 
     protected void insertTab(AttributedStyle s) {
-        int nb = tabs - lastLineLength % tabs;
+        int nb = tabs.spaces(lastLineLength);
         ensureCapacity(length + nb);
         for (int i = 0; i < nb; i++) {
             buffer[length] = ' ';
@@ -373,13 +375,17 @@
      * @return this
      */
     public AttributedStringBuilder tabs(int tabsize) {
+        if (tabsize < 0) {
+            throw new IllegalArgumentException("Tab size must be non negative");
+        }
+        return tabs(Arrays.asList(tabsize));
+    }
+
+    public AttributedStringBuilder tabs(List<Integer> tabs) {
         if (length > 0) {
             throw new IllegalStateException("Cannot change tab size after appending text");
         }
-        if (tabsize < 0) {
-            throw new IllegalArgumentException("Tab size must be non negative");
-        }
-        this.tabs = tabsize;
+        this.tabs = new TabStops(tabs);
         return this;
     }
 
@@ -393,4 +399,60 @@
         return this;
     }
 
+    public AttributedStringBuilder styleMatches(Pattern pattern, List<AttributedStyle> styles) {
+        Matcher matcher = pattern.matcher(this);
+        while (matcher.find()) {
+            for (int group = 0; group < matcher.groupCount(); group++) {
+                AttributedStyle s = styles.get(group);
+                for (int i = matcher.start(group + 1); i < matcher.end(group + 1); i++) {
+                    style[i] = (style[i] & ~s.getMask()) | s.getStyle();
+                }
+            }
+        }
+        return this;
+    }
+
+    private class TabStops {
+        private List<Integer> tabs = new ArrayList<>();
+        private int lastStop = 0;
+        private int lastSize = 0;
+
+        public TabStops(int tabs) {
+            this.lastSize = tabs;
+        }
+
+        public TabStops(List<Integer> tabs) {
+            this.tabs = tabs;
+            int p = 0;
+            for (int s: tabs) {
+                if (s <= p) {
+                    continue;
+                }
+                lastStop = s;
+                lastSize = s - p;
+                p = s;
+            }
+        }
+
+        boolean defined() {
+            return lastSize > 0;
+        }
+
+        int spaces(int lastLineLength) {
+            int out = 0;
+            if (lastLineLength >= lastStop) {
+                out = lastSize - (lastLineLength - lastStop) % lastSize;
+            } else {
+                for (int s: tabs) {
+                    if (s > lastLineLength) {
+                        out = s - lastLineLength;
+                        break;
+                    }
+                }
+            }
+            return out;
+        }
+
+    }
+
 }
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ClosedException.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ClosedException.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
@@ -74,8 +74,16 @@
             switch (ch) {
                 case '\\':
                     ch = str.charAt(index++);
-                    if (ch >= '0' && ch <= '9') {
-                        throw new UnsupportedOperationException(); // todo
+                    if (ch >= '0' && ch <= '7') {
+                        int val = ch - '0';
+                        for (int i = 0; i < 2; i++) {
+                            ch = str.charAt(index++);
+                            if (ch < '0' || ch > '7') {
+                                throw new IllegalStateException();
+                            }
+                            val = val * 8 + (ch - '0');
+                        }
+                        out.append((char) val);
                     } else {
                         switch (ch) {
                             case 'e':
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/DiffHelper.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/DiffHelper.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
@@ -492,7 +492,7 @@
     }
 
     void rawPrint(AttributedString str) {
-        terminal.writer().write(str.toAnsi(terminal));
+        str.print(terminal);
     }
 
     public int wcwidth(String str) {
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ExecHelper.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ExecHelper.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2002-2016, the original author or authors.
+ * Copyright (c) 2002-2019, the original author or authors.
  *
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
@@ -578,6 +578,8 @@
                     int iVal;
                     if (val.startsWith("0x")) {
                         iVal = Integer.parseInt(val.substring(2), 16);
+                    } else if (val.startsWith("0")) {
+                        iVal = Integer.parseInt(val.substring(1), 8);
                     } else {
                         iVal = Integer.parseInt(val);
                     }
@@ -614,7 +616,7 @@
 
     static {
         for (String s : Arrays.asList("dumb", "ansi", "xterm", "xterm-256color",
-                                      "windows", "windows-256color", "windows-vtp",
+                                      "windows", "windows-256color", "windows-conemu", "windows-vtp",
                                       "screen", "screen-256color")) {
             setDefaultInfoCmp(s, () -> loadDefaultInfoCmp(s));
         }
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InputStreamReader.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InputStreamReader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Levenshtein.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Levenshtein.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlocking.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlocking.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStream.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStreamImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingInputStreamImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReader.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReaderImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingReaderImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/OSUtils.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/OSUtils.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ShutdownHooks.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ShutdownHooks.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,10 +4,11 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Proxy;
 import java.util.Objects;
 
@@ -85,13 +86,16 @@
 
     private static Object doRegister(String name, Object handler) throws Exception {
         Log.trace(() -> "Registering signal " + name + " with handler " + toString(handler));
-        if ("QUIT".equals(name) || "INFO".equals(name) && "9".equals(System.getProperty("java.specification.version"))) {
+        Class<?> signalClass = Class.forName("sun.misc.Signal");
+        Constructor<?> constructor = signalClass.getConstructor(String.class);
+        Object signal;
+        try {
+            signal = constructor.newInstance(name);
+        } catch (IllegalArgumentException e) {
             Log.trace(() -> "Ignoring unsupported signal " + name);
             return null;
         }
-        Class<?> signalClass = Class.forName("sun.misc.Signal");
         Class<?> signalHandlerClass = Class.forName("sun.misc.SignalHandler");
-        Object signal = signalClass.getConstructor(String.class).newInstance(name);
         return signalClass.getMethod("handle", signalClass, signalHandlerClass)
                 .invoke(null, signal, handler);
     }
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Status.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Status.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
@@ -24,9 +24,11 @@
     protected final AbstractTerminal terminal;
     protected final boolean supported;
     protected List<AttributedString> oldLines = Collections.emptyList();
+    protected List<AttributedString> linesToRestore = Collections.emptyList();
     protected int rows;
     protected int columns;
     protected boolean force;
+    protected boolean suspended = false;
 
     public static Status getStatus(Terminal terminal) {
         return getStatus(terminal, true);
@@ -61,15 +63,34 @@
         this.force = true;
     }
 
+    public void hardReset() {
+        if (suspended) {
+            return;
+        }
+        List<AttributedString> lines = new ArrayList<>(oldLines);
+        update(null);
+        update(lines);
+    }
+
     public void redraw() {
+        if (suspended) {
+            return;
+        }
         update(oldLines);
     }
 
     public void update(List<AttributedString> lines) {
+        if (!supported) {
+            return;
+        }
         if (lines == null) {
             lines = Collections.emptyList();
         }
-        if (!supported || (oldLines.equals(lines) && !force)) {
+        if (suspended) {
+            linesToRestore = new ArrayList<>(lines);
+            return;
+        }
+        if (oldLines.equals(lines) && !force) {
             return;
         }
         int nb = lines.size() - oldLines.size();
@@ -82,10 +103,11 @@
             }
         }
         terminal.puts(Capability.save_cursor);
+        terminal.puts(Capability.cursor_address, rows - lines.size(), 0);
         terminal.puts(Capability.clr_eos);
         for (int i = 0; i < lines.size(); i++) {
             terminal.puts(Capability.cursor_address, rows - lines.size() + i, 0);
-            terminal.writer().write(lines.get(i).columnSubSequence(0, columns).toAnsi(terminal));
+            lines.get(i).columnSubSequence(0, columns).print(terminal);
         }
         terminal.puts(Capability.change_scroll_region, 0, rows - 1 - lines.size());
         terminal.puts(Capability.restore_cursor);
@@ -93,4 +115,27 @@
         oldLines = new ArrayList<>(lines);
         force = false;
     }
+
+    public void suspend() {
+        if (suspended) {
+            return;
+        }
+        linesToRestore = new ArrayList<>(oldLines);
+        update(null);
+        suspended = true;
+    }
+
+    public void restore() {
+        if (!suspended) {
+            return;
+        }
+        suspended = false;
+        update(linesToRestore);
+        linesToRestore = Collections.emptyList();
+    }
+
+    public int size() {
+        return oldLines.size();
+    }
+
 }
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WCWidth.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WCWidth.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WriterOutputStream.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WriterOutputStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.utils;
 
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/capabilities.txt	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/capabilities.txt	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
 # This software is distributable under the BSD license. See the terms of the
 # BSD license in the documentation provided with this software.
 #
-# http://www.opensource.org/licenses/bsd-license.php
+# https://opensource.org/licenses/BSD-3-Clause
 #
 
 auto_left_margin, bw, bw
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/colors.txt	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/colors.txt	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
 # This software is distributable under the BSD license. See the terms of the
 # BSD license in the documentation provided with this software.
 #
-# http://www.opensource.org/licenses/bsd-license.php
+# https://opensource.org/licenses/BSD-3-Clause
 #
 
 black
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/dumb-colors.caps	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,4 @@
+dumb-color|80-column dumb tty with 256 coors,
+        am,
+        colors#256, cols#80,
+        bel=^G, cr=^M, cud1=^J, ind=^J,
--- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/package-info.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/package-info.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 /**
  * JLine 3.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/windows-conemu.caps	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,27 @@
+windows-conemu|conemu windows terminal,
+	am, mc5i, mir, msgr,
+	colors#256, cols#80, it#8, lines#24, ncv#3, pairs#64,
+	bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, clear=\E[H\E[J,
+	cr=^M, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB, cud1=\E[B,
+	cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH,
+	cuu=\E[%p1%dA, cuu1=\E[A,
+	il=\E[%p1%dL, il1=\E[L,
+	dl=\E[%p1%dM, dl1=\E[M,
+	ech=\E[%p1%dX,
+	el=\E[K, ed=\E[2K,
+	el1=\E[1K, home=\E[H, hpa=\E[%i%p1%dG,
+	ind=^J,
+	invis=\E[8m, kbs=^H, kcbt=\E[Z,
+	kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,
+	khome=\E[H,
+	op=\E[39;49m,
+	rev=\E[7m,
+	rmacs=\E[10m, rmpch=\E[10m, rmso=\E[m, rmul=\E[m,
+	setab=\E[4%p1%dm, setaf=\E[3%p1%dm,
+	sgr=\E[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m,
+	sgr0=\E[0;10m,
+	smso=\E[7m,
+	smul=\E[4m,
+	kdch1=\E[3~, kich1=\E[2~, kend=\E[4~, knp=\E[6~, kpp=\E[5~,
+	kf1=\EOP, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\E[15~, kf6=\E[17~,
+	kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~,
--- a/src/jdk.internal.le/share/legal/jline.md	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/share/legal/jline.md	Mon Nov 04 11:25:55 2019 +0000
@@ -1,4 +1,4 @@
-## JLine v3.9.0
+## JLine v3.12.1
 
 ### JLine License
 <pre>
--- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaSupportImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaSupportImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,3 +1,11 @@
+/*
+ * Copyright (c) 2002-2018, the original author or authors.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
 package jdk.internal.org.jline.terminal.impl.jna;
 
 import jdk.internal.org.jline.terminal.Attributes;
--- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinConsoleWriter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinConsoleWriter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl.jna.win;
 
--- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2002-2018, the original author or authors.
+ * Copyright (c) 2002-2019, the original author or authors.
  *
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl.jna.win;
 
@@ -36,7 +36,7 @@
         Writer writer;
         if (ansiPassThrough) {
             if (type == null) {
-                type = OSUtils.IS_CONEMU ? TYPE_WINDOWS_256_COLOR : TYPE_WINDOWS;
+                type = OSUtils.IS_CONEMU ? TYPE_WINDOWS_CONEMU : TYPE_WINDOWS;
             }
             writer = new JnaWinConsoleWriter(consoleOut);
         } else {
@@ -51,7 +51,7 @@
             } catch (LastErrorException e) {
                 if (OSUtils.IS_CONEMU) {
                     if (type == null) {
-                        type = TYPE_WINDOWS_256_COLOR;
+                        type = TYPE_WINDOWS_CONEMU;
                     }
                     writer = new JnaWinConsoleWriter(consoleOut);
                 } else {
@@ -95,6 +95,12 @@
     public Size getSize() {
         Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
         Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
+        return new Size(info.windowWidth(), info.windowHeight());
+    }
+
+    public Size getBufferSize() {
+        Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
+        Kernel32.INSTANCE.GetConsoleScreenBufferInfo(consoleOut, info);
         return new Size(info.dwSize.X, info.dwSize.Y);
     }
 
--- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/Kernel32.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl.jna.win;
 
@@ -26,7 +26,7 @@
 interface Kernel32 {//extends StdCallLibrary {
 
     Kernel32 INSTANCE = new Kernel32Impl();
-//    Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class, W32APIOptions.UNICODE_OPTIONS);
+//    Kernel32 INSTANCE = Native.load("kernel32", Kernel32.class, W32APIOptions.UNICODE_OPTIONS);
 
 //    Pointer INVALID_HANDLE_VALUE = Pointer.createConstant(-1L);
 
--- a/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/WindowsAnsiWriter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/WindowsAnsiWriter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,7 +4,7 @@
  * This software is distributable under the BSD license. See the terms of the
  * BSD license in the documentation provided with this software.
  *
- * http://www.opensource.org/licenses/bsd-license.php
+ * https://opensource.org/licenses/BSD-3-Clause
  */
 package jdk.internal.org.jline.terminal.impl.jna.win;
 
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/VirtualObject.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.code/src/jdk/vm/ci/code/VirtualObject.java	Mon Nov 04 11:25:55 2019 +0000
@@ -22,11 +22,11 @@
  */
 package jdk.vm.ci.code;
 
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.Set;
 
+import jdk.vm.ci.common.JVMCIError;
 import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.JavaValue;
 import jdk.vm.ci.meta.ResolvedJavaField;
@@ -74,8 +74,8 @@
      * @param id a unique id that identifies the object within the debug information for one
      *            position in the compiled code.
      * @param isAutoBox a flag that tells the runtime that the object may be a boxed primitive and
-     *            that it possibly needs to be obtained for the box cache instead of creating
-     *            a new instance.
+     *            that it possibly needs to be obtained for the box cache instead of creating a new
+     *            instance.
      * @return a new {@link VirtualObject} instance.
      */
     public static VirtualObject get(ResolvedJavaType type, int id, boolean isAutoBox) {
@@ -108,14 +108,32 @@
                         }
                     } else {
                         ResolvedJavaField[] fields = vo.type.getInstanceFields(true);
-                        assert fields.length == vo.values.length : vo.type + ", fields=" + Arrays.toString(fields) + ", values=" + Arrays.toString(vo.values);
-                        for (int i = 0; i < vo.values.length; i++) {
+                        int fieldIndex = 0;
+                        for (int i = 0; i < vo.values.length; i++, fieldIndex++) {
                             if (i != 0) {
                                 buf.append(',');
                             }
-                            buf.append(fields[i].getName()).append('=');
+                            if (fieldIndex >= fields.length) {
+                                buf.append("<missing field>");
+                            } else {
+                                ResolvedJavaField field = fields[fieldIndex];
+                                buf.append(field.getName());
+                                if (vo.slotKinds[i].getSlotCount() == 2 && field.getType().getJavaKind().getSlotCount() == 1) {
+                                    if (fieldIndex + 1 >= fields.length) {
+                                        buf.append("/<missing field>");
+                                    } else {
+                                        ResolvedJavaField field2 = fields[++fieldIndex];
+                                        buf.append('/').append(field2.getName());
+                                    }
+                                }
+                            }
+                            buf.append('=');
                             appendValue(buf, vo.values[i], visited);
                         }
+                        // Extra fields
+                        for (; fieldIndex < fields.length; fieldIndex++) {
+                            buf.append(fields[fieldIndex].getName()).append("=<missing value>");
+                        }
                     }
                 }
                 buf.append('}');
@@ -126,6 +144,55 @@
         return buf;
     }
 
+    public interface LayoutVerifier {
+        int getOffset(ResolvedJavaField field);
+
+        default JavaKind getStorageKind(ResolvedJavaField field) {
+            return field.getType().getJavaKind();
+        }
+    }
+
+    public void verifyLayout(LayoutVerifier verifier) {
+        if (!type.isArray()) {
+            ResolvedJavaField[] fields = type.getInstanceFields(true);
+            int fieldIndex = 0;
+            for (int i = 0; i < values.length; i++, fieldIndex++) {
+                JavaKind slotKind = slotKinds[i];
+                if (fieldIndex >= fields.length) {
+                    throw new JVMCIError("Not enough fields for the values provided for %s", toString());
+                } else {
+                    ResolvedJavaField field = fields[fieldIndex];
+                    JavaKind fieldKind = verifier.getStorageKind(field);
+                    if (slotKind.getSlotCount() == 2 && fieldKind == JavaKind.Int) {
+                        int offset = verifier.getOffset(field);
+                        if (offset % 8 != 0) {
+                            throw new JVMCIError("Double word value stored across two ints must be aligned %s", toString());
+                        }
+
+                        if (fieldIndex + 1 >= fields.length) {
+                            throw new JVMCIError("Missing second field for double word value stored in two ints %s", toString());
+                        }
+                        ResolvedJavaField field2 = fields[fieldIndex + 1];
+                        if (field2.getType().getJavaKind() != JavaKind.Int) {
+                            throw new JVMCIError("Second field for double word value stored in two ints must be int but got %s in %s", field2.getType().getJavaKind(), toString());
+                        }
+                        int offset2 = verifier.getOffset(field2);
+                        if (offset + 4 != offset2) {
+                            throw new JVMCIError("Double word value stored across two ints must be sequential %s", toString());
+                        }
+                        fieldIndex++;
+                    } else if (fieldKind.getStackKind() != slotKind.getStackKind()) {
+                        throw new JVMCIError("Expected value of kind %s but got %s for field %s in %s", fieldKind, slotKind, field, toString());
+                    }
+                }
+            }
+            // Extra fields
+            if (fieldIndex < fields.length) {
+                throw new JVMCIError("Not enough values provided for fields in %s", this);
+            }
+        }
+    }
+
     @Override
     public String toString() {
         Set<VirtualObject> visited = Collections.newSetFromMap(new IdentityHashMap<VirtualObject, Boolean>());
@@ -170,16 +237,18 @@
      * the box is in the cache range and try to return a cached object.
      */
     public boolean isAutoBox() {
-      return isAutoBox;
+        return isAutoBox;
     }
 
     /**
      * Sets the value of the box flag.
-     * @param isAutoBox a flag that tells the runtime that the object may be a boxed primitive and that
-     *            it possibly needs to be obtained for the box cache instead of creating a new instance.
+     *
+     * @param isAutoBox a flag that tells the runtime that the object may be a boxed primitive and
+     *            that it possibly needs to be obtained for the box cache instead of creating a new
+     *            instance.
      */
     public void setIsAutoBox(boolean isAutoBox) {
-      this.isAutoBox = isAutoBox;
+        this.isAutoBox = isAutoBox;
     }
 
     /**
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/Cleaner.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/Cleaner.java	Mon Nov 04 11:25:55 2019 +0000
@@ -32,7 +32,7 @@
  * A cleaner tracks a referent object and includes some {@linkplain #doCleanup() cleanup code} that
  * is run some time after the referent object has become weakly-reachable.
  *
- * This is like {@link sun.misc.Cleaner} but with weak semantics instead of phantom. Objects
+ * This is like {@link java.lang.ref.Cleaner} but with weak semantics instead of phantom. Objects
  * referenced by this might be referenced by {@link ResolvedJavaType} which is kept alive by a
  * {@link WeakReference} so we need equivalent reference strength.
  */
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	Mon Nov 04 11:25:55 2019 +0000
@@ -56,7 +56,7 @@
     private static native void registerNatives();
 
     /**
-     * These values mirror the equivalent values from {@link Unsafe} but are approriate for the JVM
+     * These values mirror the equivalent values from {@code Unsafe} but are appropriate for the JVM
      * being compiled against.
      */
     // Checkstyle: stop
@@ -514,10 +514,10 @@
 
     /**
      * Reads an object pointer within a VM data structure. That is, any {@link VMField} whose
-     * {@link VMField#type type} is {@code "oop"} (e.g.,
-     * {@code Klass::_java_mirror}, {@code JavaThread::_threadObj}).
+     * {@link VMField#type type} is {@code "oop"} (e.g., {@code Klass::_java_mirror},
+     * {@code JavaThread::_threadObj}).
      *
-     * Note that {@link Unsafe#getObject(Object, long)} cannot be used for this since it does a
+     * Note that {@code Unsafe.getObject(Object, long)} cannot be used for this since it does a
      * {@code narrowOop} read if the VM is using compressed oops whereas oops within VM data
      * structures are (currently) always uncompressed.
      *
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledCode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompiledCode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -25,10 +25,12 @@
 import jdk.vm.ci.code.BytecodeFrame;
 import jdk.vm.ci.code.CompiledCode;
 import jdk.vm.ci.code.StackSlot;
+import jdk.vm.ci.code.VirtualObject;
 import jdk.vm.ci.code.site.DataPatch;
 import jdk.vm.ci.code.site.Infopoint;
 import jdk.vm.ci.code.site.Site;
 import jdk.vm.ci.meta.Assumptions.Assumption;
+import jdk.vm.ci.meta.ResolvedJavaField;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
 
 /**
@@ -156,9 +158,23 @@
                 if (info.debugInfo != null) {
                     BytecodeFrame frame = info.debugInfo.frame();
                     assert frame == null || frame.validateFormat();
+                    if (info.debugInfo.getVirtualObjectMapping() != null) {
+                        for (VirtualObject v : info.debugInfo.getVirtualObjectMapping()) {
+                            verifyVirtualObject(v);
+                        }
+                    }
                 }
             }
         }
         return true;
     }
+
+    public static void verifyVirtualObject(VirtualObject v) {
+        v.verifyLayout(new VirtualObject.LayoutVerifier() {
+            @Override
+            public int getOffset(ResolvedJavaField field) {
+                return field.getOffset();
+            }
+        });
+    }
 }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java	Mon Nov 04 11:25:55 2019 +0000
@@ -31,25 +31,19 @@
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.io.Serializable;
-
 import java.lang.invoke.CallSite;
 import java.lang.invoke.ConstantCallSite;
 import java.lang.invoke.MethodHandle;
-import java.lang.module.ModuleDescriptor.Requires;
 import java.lang.ref.WeakReference;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.ServiceLoader;
 import java.util.function.Predicate;
 
-import jdk.internal.misc.Unsafe;
-
 import jdk.vm.ci.code.Architecture;
 import jdk.vm.ci.code.CompilationRequestResult;
 import jdk.vm.ci.code.CompiledCode;
@@ -187,9 +181,15 @@
                     // initialized.
                     JVMCI.getRuntime();
                 }
-                // Make sure all the primitive box caches are populated (required to properly materialize boxed primitives
+                // Make sure all the primitive box caches are populated (required to properly
+                // materialize boxed primitives
                 // during deoptimization).
-                Object[] boxCaches = { Boolean.valueOf(false), Byte.valueOf((byte)0), Short.valueOf((short) 0), Character.valueOf((char) 0), Integer.valueOf(0), Long.valueOf(0) };
+                Boolean.valueOf(false);
+                Byte.valueOf((byte) 0);
+                Short.valueOf((short) 0);
+                Character.valueOf((char) 0);
+                Integer.valueOf(0);
+                Long.valueOf(0);
             }
         }
         return result;
@@ -338,7 +338,7 @@
 
     private static HotSpotJVMCIBackendFactory findFactory(String architecture) {
         Iterable<HotSpotJVMCIBackendFactory> factories = getHotSpotJVMCIBackendFactories();
-assert factories != null : "sanity";
+        assert factories != null : "sanity";
         for (HotSpotJVMCIBackendFactory factory : factories) {
             if (factory.getArchitecture().equalsIgnoreCase(architecture)) {
                 return factory;
@@ -391,33 +391,35 @@
     @NativeImageReinitialize private volatile ClassValue<WeakReferenceHolder<HotSpotResolvedJavaType>> resolvedJavaType;
 
     /**
-     * To avoid calling ClassValue.remove to refresh the weak reference, which
-     * under certain circumstances can lead to an infinite loop, we use a
-     * permanent holder with a mutable field that we refresh.
+     * To avoid calling ClassValue.remove to refresh the weak reference, which under certain
+     * circumstances can lead to an infinite loop, we use a permanent holder with a mutable field
+     * that we refresh.
      */
     private static class WeakReferenceHolder<T> {
         private volatile WeakReference<T> ref;
+
         WeakReferenceHolder(T value) {
             set(value);
         }
+
         void set(T value) {
-            ref = new WeakReference<T>(value);
+            ref = new WeakReference<>(value);
         }
+
         T get() {
             return ref.get();
         }
-    };
+    }
 
     @NativeImageReinitialize private HashMap<Long, WeakReference<ResolvedJavaType>> resolvedJavaTypes;
 
     /**
-     * Stores the value set by {@link #excludeFromJVMCICompilation(Module...)} so that it can
-     * be read from the VM.
+     * Stores the value set by {@link #excludeFromJVMCICompilation(Module...)} so that it can be
+     * read from the VM.
      */
     @SuppressWarnings("unused")//
     @NativeImageReinitialize private Module[] excludeFromJVMCICompilation;
 
-
     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 
     private volatile List<HotSpotVMEventListener> vmEventListeners;
@@ -508,7 +510,7 @@
         if (resolvedJavaType == null) {
             synchronized (this) {
                 if (resolvedJavaType == null) {
-                    resolvedJavaType = new ClassValue<WeakReferenceHolder<HotSpotResolvedJavaType>>() {
+                    resolvedJavaType = new ClassValue<>() {
                         @Override
                         protected WeakReferenceHolder<HotSpotResolvedJavaType> computeValue(Class<?> type) {
                             return new WeakReferenceHolder<>(createClass(type));
@@ -522,8 +524,7 @@
         HotSpotResolvedJavaType javaType = ref.get();
         if (javaType == null) {
             /*
-             * If the referent has become null, create a new value and
-             * update cached weak reference.
+             * If the referent has become null, create a new value and update cached weak reference.
              */
             javaType = createClass(javaClass);
             ref.set(javaType);
@@ -591,7 +592,7 @@
      *            compiler.
      */
     public Predicate<ResolvedJavaType> getIntrinsificationTrustPredicate(Class<?>... compilerLeafClasses) {
-        return new Predicate<ResolvedJavaType>() {
+        return new Predicate<>() {
             @Override
             public boolean test(ResolvedJavaType type) {
                 if (type instanceof HotSpotResolvedObjectTypeImpl) {
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -563,6 +563,10 @@
          * a deopt instead since they can't really be used if they aren't linked yet.
          */
         if (!declaredHolder.isAssignableFrom(this) || this.isArray() || this.equals(declaredHolder) || !isLinked() || isInterface()) {
+            if (hmethod.canBeStaticallyBound()) {
+                // No assumptions are required.
+                return new AssumptionResult<>(hmethod);
+            }
             ResolvedJavaMethod result = hmethod.uniqueConcreteMethod(declaredHolder);
             if (result != null) {
                 return new AssumptionResult<>(result, new ConcreteMethod(method, declaredHolder, result));
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.services/src/jdk/vm/ci/services/JVMCIServiceLocator.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.services/src/jdk/vm/ci/services/JVMCIServiceLocator.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -23,7 +23,6 @@
 package jdk.vm.ci.services;
 
 import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE;
-import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -81,7 +80,7 @@
         result = ServiceLoader.load(JVMCIServiceLocator.class, ClassLoader.getSystemClassLoader());
         if (IS_BUILDING_NATIVE_IMAGE) {
             ArrayList<JVMCIServiceLocator> l = new ArrayList<>();
-            for (JVMCIServiceLocator locator: result) {
+            for (JVMCIServiceLocator locator : result) {
                 l.add(locator);
             }
             l.trimToSize();
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.services/src/jdk/vm/ci/services/Services.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.services/src/jdk/vm/ci/services/Services.java	Mon Nov 04 11:25:55 2019 +0000
@@ -33,7 +33,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.ServiceLoader;
 import java.util.Set;
 
@@ -45,9 +44,6 @@
  */
 public final class Services {
 
-    // This class must be compilable and executable on JDK 8 since it's used in annotation
-    // processors while building JDK 9 so use of API added in JDK 9 is made via reflection.
-
     /**
      * Guards code that should be run when building an JVMCI shared library but should be excluded
      * from (being compiled into) the library. Such code must be directly guarded by an {@code if}
@@ -73,8 +69,12 @@
     private Services() {
     }
 
-    private static volatile Map<String, String> savedProperties = VM.getSavedProperties();
-    static final boolean JVMCI_ENABLED = Boolean.parseBoolean(savedProperties.get("jdk.internal.vm.ci.enabled"));
+    /**
+     * In a native image, this field is initialized by {@link #initializeSavedProperties(byte[])}.
+     */
+    private static volatile Map<String, String> savedProperties;
+
+    static final boolean JVMCI_ENABLED = Boolean.parseBoolean(VM.getSavedProperties().get("jdk.internal.vm.ci.enabled"));
 
     /**
      * Checks that JVMCI is enabled in the VM and throws an error if it isn't.
@@ -90,9 +90,22 @@
      */
     public static Map<String, String> getSavedProperties() {
         checkJVMCIEnabled();
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            sm.checkPermission(new JVMCIPermission());
+        if (IS_IN_NATIVE_IMAGE) {
+            if (savedProperties == null) {
+                throw new InternalError("Saved properties not initialized");
+            }
+        } else {
+            if (savedProperties == null) {
+                synchronized (Services.class) {
+                    if (savedProperties == null) {
+                        SecurityManager sm = System.getSecurityManager();
+                        if (sm != null) {
+                            sm.checkPermission(new JVMCIPermission());
+                        }
+                        savedProperties = VM.getSavedProperties();
+                    }
+                }
+            }
         }
         return savedProperties;
     }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.services/src/jdk/vm/ci/services/SuppressFBWarnings.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.services/src/jdk/vm/ci/services/SuppressFBWarnings.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,9 +4,7 @@
  *
  * 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
+ * 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/EconomicMap.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/EconomicMap.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/EconomicSet.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/EconomicSet.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/Equivalence.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/Equivalence.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/MapCursor.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/MapCursor.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/Pair.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/Pair.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/UnmodifiableEconomicMap.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/UnmodifiableEconomicMap.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/UnmodifiableEconomicSet.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/UnmodifiableEconomicSet.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/UnmodifiableMapCursor.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/UnmodifiableMapCursor.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/package-info.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.collections/src/jdk/internal/vm/compiler/collections/package-info.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/ComparableWord.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/ComparableWord.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/LocationIdentity.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/LocationIdentity.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/Pointer.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/Pointer.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/PointerBase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/PointerBase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/SignedWord.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/SignedWord.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/UnsignedWord.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/UnsignedWord.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/WordBase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/WordBase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/WordFactory.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/WordFactory.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/package-info.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/jdk.internal.vm.compiler.word/src/jdk/internal/vm/compiler/word/package-info.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/MethodSubstitution.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/MethodSubstitution.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -35,9 +35,7 @@
 /**
  * Denotes a method whose body is used by a compiler as the substitute (or intrinsification) of
  * another method. The exact mechanism used to do the substitution is compiler dependent but every
- * compiler should require substitute methods to be annotated with {@link MethodSubstitution}. In
- * addition, a compiler is recommended to implement {@link MethodSubstitutionRegistry} to advertise
- * the mechanism by which it supports registration of method substitutes.
+ * compiler should require substitute methods to be annotated with {@link MethodSubstitution}.
  *
  * A compiler may support partial intrinsification where only a part of a method is implemented by
  * the compiler. The unsupported path is expressed by a call to either the original or substitute
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/MethodSubstitutionRegistry.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2013, 2018, 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.api.replacements;
-
-import java.lang.reflect.Type;
-
-/**
- * A registry for {@link MethodSubstitution}s.
- */
-public interface MethodSubstitutionRegistry {
-
-    /**
-     * Gets the type representing the receiver (i.e., {@code this}) argument in a non-static method.
-     */
-    Class<?> getReceiverType();
-
-    /**
-     * Registers a substitution method.
-     *
-     * @param substituteDeclaringClass the class declaring the substitute method
-     * @param name the name of both the original and substitute method
-     * @param argumentTypes the argument types of the method. Element 0 of this array must be
-     *            {@link #getReceiverType()} iff the method is non-static. Upon returning, element 0
-     *            will have been rewritten to {@code declaringClass}.
-     */
-    default void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Type... argumentTypes) {
-        registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes);
-    }
-
-    /**
-     * Registers a substitution method.
-     *
-     * @param substituteDeclaringClass the class declaring the substitute method
-     * @param name the name of both the original method
-     * @param substituteName the name of the substitute method
-     * @param argumentTypes the argument types of the method. Element 0 of this array must be
-     *            {@link #getReceiverType()} iff the method is non-static. Upon returning, element 0
-     *            will have been rewritten to {@code declaringClass}.
-     */
-    void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.test/src/org/graalvm/compiler/api/test/ExportingClassLoader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, 2019, 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.api.test;
+
+/**
+ * A class loader that exports all packages in the module defining the class loader to all classes
+ * in the unnamed module associated with the loader.
+ */
+public class ExportingClassLoader extends ClassLoader {
+    public ExportingClassLoader() {
+        ModuleSupport.exportAllPackagesTo(getClass(), this);
+    }
+
+    public ExportingClassLoader(ClassLoader parent) {
+        super(parent);
+        ModuleSupport.exportAllPackagesTo(getClass(), this);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.test/src/org/graalvm/compiler/api/test/ModuleSupport.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2019, 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.api.test;
+
+import java.io.IOException;
+import java.lang.module.ModuleDescriptor.Requires;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.graalvm.compiler.debug.DebugOptions;
+
+import jdk.internal.module.Modules;
+
+public class ModuleSupport {
+
+    public static void exportPackageTo(Class<?> moduleMember, String packageName, Class<?> requestor) {
+        Module moduleToExport = moduleMember.getModule();
+        Module requestorModule = requestor.getModule();
+        if (moduleToExport != requestorModule) {
+            Modules.addExports(moduleToExport, packageName, requestorModule);
+        }
+    }
+
+    public static void exportAllPackagesTo(Class<?> moduleMember, Class<?> requestor) {
+        Module moduleToExport = moduleMember.getModule();
+        Module requestorModule = requestor.getModule();
+        if (moduleToExport != requestorModule) {
+            for (String packageName : moduleToExport.getPackages()) {
+                Modules.addExports(moduleToExport, packageName, requestorModule);
+            }
+        }
+    }
+
+    public static void exportAllPackagesTo(Class<?> moduleMember, ClassLoader cl) {
+        Module moduleToExport = moduleMember.getModule();
+        Module unnamedModule = cl.getUnnamedModule();
+        for (String packageName : moduleToExport.getPackages()) {
+            Modules.addExports(moduleToExport, packageName, unnamedModule);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    public static void exportAndOpenAllPackagesToUnnamed(String name) {
+        Module module = ModuleLayer.boot().findModule(name).orElseThrow();
+        Set<String> packages = module.getPackages();
+        for (String pkg : packages) {
+            Modules.addExportsToAllUnnamed(module, pkg);
+            Modules.addOpensToAllUnnamed(module, pkg);
+        }
+    }
+
+    public static List<String> getJRTGraalClassNames() throws IOException {
+        List<String> classNames = new ArrayList<>();
+        FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap());
+        Module graalModule = DebugOptions.class.getModule();
+        Set<String> graalModuleSet = new HashSet<>();
+        graalModuleSet.add(graalModule.getName());
+        for (Module module : graalModule.getLayer().modules()) {
+            if (requires(module, graalModule)) {
+                graalModuleSet.add(module.getName());
+            }
+        }
+
+        Path top = fs.getPath("/modules/");
+        Files.find(top, Integer.MAX_VALUE,
+                        (path, attrs) -> attrs.isRegularFile()).forEach(p -> {
+                            int nameCount = p.getNameCount();
+                            if (nameCount > 2) {
+                                String base = p.getName(nameCount - 1).toString();
+                                if (base.endsWith(".class") && !base.equals("module-info.class")) {
+                                    String module = p.getName(1).toString();
+                                    if (graalModuleSet.contains(module)) {
+                                        // Strip module prefix and convert to dotted
+                                        // form
+                                        String className = p.subpath(2, nameCount).toString().replace('/', '.');
+                                        // Strip ".class" suffix
+                                        className = className.replace('/', '.').substring(0, className.length() - ".class".length());
+                                        classNames.add(className);
+                                    }
+                                }
+                            }
+                        });
+        return classNames;
+    }
+
+    private static boolean requires(Module module, Module graalModule) {
+        ModuleLayer graalLayer = graalModule.getLayer();
+        for (Requires r : module.getDescriptor().requires()) {
+            if (r.name().equals(graalModule.getName())) {
+                return true;
+            }
+            Module dep = graalLayer.findModule(r.name()).get();
+            if (requires(dep, graalModule)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64Assembler.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64Assembler.java	Mon Nov 04 11:25:55 2019 +0000
@@ -471,6 +471,7 @@
     private static final int AddSubExtendedOp = 0x0B200000;
 
     private static final int MulOp = 0x1B000000;
+    private static final int SignedMulLongOp = 0x9B200000;
     private static final int DataProcessing1SourceOp = 0x5AC00000;
     private static final int DataProcessing2SourceOp = 0x1AC00000;
 
@@ -2313,7 +2314,7 @@
     }
 
     /**
-     * unsigned multiply high. dst = (src1 * src2)[127:64]
+     * Unsigned multiply high. dst = (src1 * src2)[127:64]
      *
      * @param dst general purpose register. May not be null or the stackpointer.
      * @param src1 general purpose register. May not be null or the stackpointer.
@@ -2327,7 +2328,7 @@
     }
 
     /**
-     * unsigned multiply add-long. xDst = xSrc3 + (wSrc1 * wSrc2)
+     * Unsigned multiply add-long. xDst = xSrc3 + (wSrc1 * wSrc2)
      *
      * @param dst general purpose register. May not be null or the stackpointer.
      * @param src1 general purpose register. May not be null or the stackpointer.
@@ -2343,7 +2344,7 @@
     }
 
     /**
-     * signed multiply add-long. xDst = xSrc3 + (wSrc1 * wSrc2)
+     * Signed multiply-add long. xDst = xSrc3 + (wSrc1 * wSrc2)
      *
      * @param dst general purpose register. May not be null or the stackpointer.
      * @param src1 general purpose register. May not be null or the stackpointer.
@@ -2351,11 +2352,19 @@
      * @param src3 general purpose register. May not be null or the stackpointer.
      */
     public void smaddl(Register dst, Register src1, Register src2, Register src3) {
-        assert !dst.equals(sp);
-        assert !src1.equals(sp);
-        assert !src2.equals(sp);
-        assert !src3.equals(sp);
-        emitInt(0b10011011001 << 21 | dst.encoding | rs1(src1) | rs2(src2) | rs3(src3));
+        smullInstruction(MADD, dst, src1, src2, src3);
+    }
+
+    /**
+     * Signed multiply-sub long. xDst = xSrc3 - (wSrc1 * wSrc2)
+     *
+     * @param dst general purpose register. May not be null or the stackpointer.
+     * @param src1 general purpose register. May not be null or the stackpointer.
+     * @param src2 general purpose register. May not be null or the stackpointer.
+     * @param src3 general purpose register. May not be null or the stackpointer.
+     */
+    public void smsubl(Register dst, Register src1, Register src2, Register src3) {
+        smullInstruction(MSUB, dst, src1, src2, src3);
     }
 
     private void mulInstruction(Instruction instr, Register dst, Register src1, Register src2, Register src3, InstructionType type) {
@@ -2366,6 +2375,14 @@
         emitInt(type.encoding | instr.encoding | MulOp | rd(dst) | rs1(src1) | rs2(src2) | rs3(src3));
     }
 
+    private void smullInstruction(Instruction instr, Register dst, Register src1, Register src2, Register src3) {
+        assert !dst.equals(sp);
+        assert !src1.equals(sp);
+        assert !src2.equals(sp);
+        assert !src3.equals(sp);
+        emitInt(instr.encoding | SignedMulLongOp | rd(dst) | rs1(src1) | rs2(src2) | rs3(src3));
+    }
+
     /**
      * Signed divide. dst = src1 / src2.
      *
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64MacroAssembler.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64MacroAssembler.java	Mon Nov 04 11:25:55 2019 +0000
@@ -817,7 +817,7 @@
     }
 
     /**
-     * unsigned multiply high. dst = (src1 * src2) >> size
+     * Unsigned multiply high. dst = (src1 * src2) >> size
      *
      * @param size register size. Has to be 32 or 64.
      * @param dst general purpose register. May not be null or the stackpointer.
@@ -838,7 +838,7 @@
     }
 
     /**
-     * signed multiply high. dst = (src1 * src2) >> size
+     * Signed multiply high. dst = (src1 * src2) >> size
      *
      * @param size register size. Has to be 32 or 64.
      * @param dst general purpose register. May not be null or the stackpointer.
@@ -859,6 +859,60 @@
     }
 
     /**
+     * Signed multiply long. xDst = wSrc1 * wSrc2
+     *
+     * @param size destination register size. Has to be 64.
+     * @param dst 64-bit general purpose register. May not be null or the stackpointer.
+     * @param src1 32-bit general purpose register. May not be null or the stackpointer.
+     * @param src2 32-bit general purpose register. May not be null or the stackpointer.
+     */
+    public void smull(int size, Register dst, Register src1, Register src2) {
+        this.smaddl(size, dst, src1, src2, zr);
+    }
+
+    /**
+     * Signed multiply-negate long. xDst = -(wSrc1 * wSrc2)
+     *
+     * @param size destination register size. Has to be 64.
+     * @param dst 64-bit general purpose register. May not be null or the stackpointer.
+     * @param src1 32-bit general purpose register. May not be null or the stackpointer.
+     * @param src2 32-bit general purpose register. May not be null or the stackpointer.
+     */
+    public void smnegl(int size, Register dst, Register src1, Register src2) {
+        this.smsubl(size, dst, src1, src2, zr);
+    }
+
+    /**
+     * Signed multiply-add long. xDst = xSrc3 + (wSrc1 * wSrc2)
+     *
+     * @param size destination register size. Has to be 64.
+     * @param dst 64-bit general purpose register. May not be null or the stackpointer.
+     * @param src1 32-bit general purpose register. May not be null or the stackpointer.
+     * @param src2 32-bit general purpose register. May not be null or the stackpointer.
+     * @param src3 64-bit general purpose register. May not be null or the stackpointer.
+     */
+    public void smaddl(int size, Register dst, Register src1, Register src2, Register src3) {
+        assert (!dst.equals(sp) && !src1.equals(sp) && !src2.equals(sp) && !src3.equals(sp));
+        assert size == 64;
+        super.smaddl(dst, src1, src2, src3);
+    }
+
+    /**
+     * Signed multiply-sub long. xDst = xSrc3 - (wSrc1 * wSrc2)
+     *
+     * @param size destination register size. Has to be 64.
+     * @param dst 64-bit general purpose register. May not be null or the stackpointer.
+     * @param src1 32-bit general purpose register. May not be null or the stackpointer.
+     * @param src2 32-bit general purpose register. May not be null or the stackpointer.
+     * @param src3 64-bit general purpose register. May not be null or the stackpointer.
+     */
+    public void smsubl(int size, Register dst, Register src1, Register src2, Register src3) {
+        assert (!dst.equals(sp) && !src1.equals(sp) && !src2.equals(sp) && !src3.equals(sp));
+        assert size == 64;
+        super.smsubl(dst, src1, src2, src3);
+    }
+
+    /**
      * dst = src1 % src2. Signed.
      *
      * @param size register size. Has to be 32 or 64.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64.test/src/org/graalvm/compiler/core/aarch64/test/AArch64MultiplyLongTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Arm Limited. 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.aarch64.test;
+
+import org.junit.Test;
+
+public class AArch64MultiplyLongTest extends AArch64MatchRuleTest {
+
+    public long signedMulLong(int a, int b) {
+        return a * (long) b;
+    }
+
+    public long signedMulLongFromShort(short a, short b) {
+        return (long) a * b;
+    }
+
+    public long signedMulLongFromChar(char a, char b) {
+        return a * (long) b;
+    }
+
+    public long signedMulLongFromByte(byte a, byte b) {
+        return (long) a * b;
+    }
+
+    @Test
+    public void testSignedMulLong() {
+        test("signedMulLong", 0x12345678, 0x87654321);
+        checkLIR("signedMulLong", op -> op.name().equals("SMULL"), 1);
+        test("signedMulLongFromShort", (short) 32767, (short) -32768);
+        checkLIR("signedMulLongFromShort", op -> op.name().equals("SMULL"), 1);
+        test("signedMulLongFromChar", (char) 59999, (char) 65535);
+        checkLIR("signedMulLongFromChar", op -> op.name().equals("SMULL"), 1);
+        test("signedMulLongFromByte", (byte) 10, (byte) -256);
+        checkLIR("signedMulLongFromByte", op -> op.name().equals("SMULL"), 1);
+    }
+
+    public long signedMNegLong1(int a, int b) {
+        return -(a * (long) b);
+    }
+
+    public long signedMNegLong2(int a, int b) {
+        return a * (-(long) b);
+    }
+
+    @Test
+    public void testSignedMNegLong() {
+        test("signedMNegLong1", 0x89abcdef, 0xfedcba98);
+        checkLIR("signedMNegLong1", op -> op.name().equals("SMNEGL"), 1);
+        test("signedMNegLong2", 0x89abcdef, 0xfedcba98);
+        checkLIR("signedMNegLong2", op -> op.name().equals("SMNEGL"), 1);
+    }
+
+    public long signedMAddLong1(int a, int b, long c) {
+        return c + a * (long) b;
+    }
+
+    public long signedMAddLong2(int a, int b, long c) {
+        return a * (long) b + c;
+    }
+
+    @Test
+    public void testSignedMAddLong() {
+        test("signedMAddLong1", 0x22228888, 0xaaaacccc, 0x123456789abcdL);
+        checkLIR("signedMAddLong1", op -> op.name().equals("SMADDL"), 1);
+        test("signedMAddLong2", 0x22228888, 0xaaaacccc, 0x123456789abcdL);
+        checkLIR("signedMAddLong2", op -> op.name().equals("SMADDL"), 1);
+    }
+
+    public long signedMSubLong(int a, int b, long c) {
+        return c - a * (long) b;
+    }
+
+    @Test
+    public void testSignedMSubLong() {
+        test("signedMSubLong", 0x99995555, 0xeeeebbbb, 0x3456789abcdefL);
+        checkLIR("signedMSubLong", op -> op.name().equals("SMSUBL"), 1);
+    }
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64ArithmeticLIRGenerator.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64ArithmeticLIRGenerator.java	Mon Nov 04 11:25:55 2019 +0000
@@ -220,23 +220,34 @@
         return result;
     }
 
-    public Value emitMAdd(Value a, Value b, Value c) {
-        return emitMultiplyAddSub(AArch64ArithmeticOp.ADD, a, b, c);
+    Value emitIntegerMAdd(Value a, Value b, Value c, boolean isI2L) {
+        return emitMultiplyAddSub(isI2L ? AArch64ArithmeticOp.SMADDL : AArch64ArithmeticOp.MADD, a, b, c);
     }
 
-    public Value emitMSub(Value a, Value b, Value c) {
-        return emitMultiplyAddSub(AArch64ArithmeticOp.SUB, a, b, c);
+    Value emitIntegerMSub(Value a, Value b, Value c, boolean isI2L) {
+        return emitMultiplyAddSub(isI2L ? AArch64ArithmeticOp.SMSUBL : AArch64ArithmeticOp.MSUB, a, b, c);
     }
 
     private Value emitMultiplyAddSub(AArch64ArithmeticOp op, Value a, Value b, Value c) {
-        assert a.getPlatformKind() == b.getPlatformKind() && b.getPlatformKind() == c.getPlatformKind();
-        if (op == AArch64ArithmeticOp.ADD || op == AArch64ArithmeticOp.SUB) {
-            assert isNumericInteger(a.getPlatformKind());
-        } else if (op == AArch64ArithmeticOp.FADD) {
-            assert a.getPlatformKind() == AArch64Kind.SINGLE || a.getPlatformKind() == AArch64Kind.DOUBLE;
+        assert a.getPlatformKind() == b.getPlatformKind();
+        Variable result;
+        if (op == AArch64ArithmeticOp.SMADDL || op == AArch64ArithmeticOp.SMSUBL) {
+            // For signed multiply int and then add/sub long.
+            assert a.getPlatformKind() != c.getPlatformKind();
+            result = getLIRGen().newVariable(LIRKind.combine(c));
+        } else {
+            assert a.getPlatformKind() == c.getPlatformKind();
+            if (op == AArch64ArithmeticOp.FADD) {
+                // For floating-point Math.fma intrinsic.
+                assert a.getPlatformKind() == AArch64Kind.SINGLE || a.getPlatformKind() == AArch64Kind.DOUBLE;
+            } else {
+                // For int/long multiply add or sub.
+                assert op == AArch64ArithmeticOp.MADD || op == AArch64ArithmeticOp.MSUB;
+                assert isNumericInteger(a.getPlatformKind());
+            }
+            result = getLIRGen().newVariable(LIRKind.combine(a, b, c));
         }
 
-        Variable result = getLIRGen().newVariable(LIRKind.combine(a, b, c));
         AllocatableValue x = moveSp(asAllocatable(a));
         AllocatableValue y = moveSp(asAllocatable(b));
         AllocatableValue z = moveSp(asAllocatable(c));
@@ -451,7 +462,7 @@
 
     @Override
     public Value emitFusedMultiplyAdd(Value a, Value b, Value c) {
-        return emitMultiplyAddSub(AArch64ArithmeticOp.FADD, a, b, c);
+        return emitMultiplyAddSub(AArch64ArithmeticOp.FMADD, a, b, c);
     }
 
     @Override
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java	Mon Nov 04 11:25:55 2019 +0000
@@ -57,6 +57,7 @@
 import org.graalvm.compiler.nodes.calc.BinaryNode;
 import org.graalvm.compiler.nodes.calc.IntegerLessThanNode;
 import org.graalvm.compiler.nodes.calc.LeftShiftNode;
+import org.graalvm.compiler.nodes.calc.MulNode;
 import org.graalvm.compiler.nodes.calc.NotNode;
 import org.graalvm.compiler.nodes.calc.OrNode;
 import org.graalvm.compiler.nodes.calc.RightShiftNode;
@@ -224,6 +225,33 @@
         return emitBinaryShift(op, a, shift, isShiftNot);
     }
 
+    @MatchRule("(Add=binary (Mul (SignExtend a) (SignExtend b)) c)")
+    @MatchRule("(Sub=binary c (Mul (SignExtend a) (SignExtend b)))")
+    public ComplexMatchResult signedMultiplyAddSubLong(BinaryNode binary, ValueNode a, ValueNode b, ValueNode c) {
+        assert a.getStackKind() == JavaKind.Int && b.getStackKind() == JavaKind.Int && c.getStackKind() == JavaKind.Long;
+        if (binary instanceof AddNode) {
+            return builder -> getArithmeticLIRGenerator().emitIntegerMAdd(operand(a), operand(b), operand(c), true);
+        }
+        return builder -> getArithmeticLIRGenerator().emitIntegerMSub(operand(a), operand(b), operand(c), true);
+    }
+
+    @MatchRule("(Negate (Mul=mul (SignExtend a) (SignExtend b)))")
+    @MatchRule("(Mul=mul (Negate (SignExtend a)) (SignExtend b))")
+    public ComplexMatchResult signedMultiplyNegLong(MulNode mul, ValueNode a, ValueNode b) {
+        assert a.getStackKind() == JavaKind.Int && b.getStackKind() == JavaKind.Int;
+        LIRKind resultKind = LIRKind.fromJavaKind(gen.target().arch, mul.getStackKind());
+        return builder -> getArithmeticLIRGenerator().emitBinary(
+                        resultKind, AArch64ArithmeticOp.SMNEGL, true, operand(a), operand(b));
+    }
+
+    @MatchRule("(Mul=mul (SignExtend a) (SignExtend b))")
+    public ComplexMatchResult signedMultiplyLong(MulNode mul, ValueNode a, ValueNode b) {
+        assert a.getStackKind() == JavaKind.Int && b.getStackKind() == JavaKind.Int;
+        LIRKind resultKind = LIRKind.fromJavaKind(gen.target().arch, mul.getStackKind());
+        return builder -> getArithmeticLIRGenerator().emitBinary(
+                        resultKind, AArch64ArithmeticOp.SMULL, true, operand(a), operand(b));
+    }
+
     @MatchRule("(Mul (Negate a) b)")
     @MatchRule("(Negate (Mul a b))")
     public ComplexMatchResult multiplyNegate(ValueNode a, ValueNode b) {
@@ -244,9 +272,9 @@
         }
 
         if (binary instanceof AddNode) {
-            return builder -> getArithmeticLIRGenerator().emitMAdd(operand(a), operand(b), operand(c));
+            return builder -> getArithmeticLIRGenerator().emitIntegerMAdd(operand(a), operand(b), operand(c), false);
         }
-        return builder -> getArithmeticLIRGenerator().emitMSub(operand(a), operand(b), operand(c));
+        return builder -> getArithmeticLIRGenerator().emitIntegerMSub(operand(a), operand(b), operand(c), false);
     }
 
     /**
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java	Mon Nov 04 11:25:55 2019 +0000
@@ -359,6 +359,7 @@
 
     @MatchRule("(If (IntegerTest Read=access value))")
     @MatchRule("(If (IntegerTest FloatingRead=access value))")
+    @MatchRule("(If (IntegerTest VolatileRead=access value))")
     public ComplexMatchResult integerTestBranchMemory(IfNode root, LIRLowerableAccess access, ValueNode value) {
         return emitIntegerTestBranchMemory(root, value, access);
     }
@@ -369,14 +370,21 @@
     @MatchRule("(If (IntegerEquals=compare value FloatingRead=access))")
     @MatchRule("(If (IntegerLessThan=compare value FloatingRead=access))")
     @MatchRule("(If (IntegerBelow=compare value FloatingRead=access))")
+    @MatchRule("(If (IntegerEquals=compare value VolatileRead=access))")
+    @MatchRule("(If (IntegerLessThan=compare value VolatileRead=access))")
+    @MatchRule("(If (IntegerBelow=compare value VolatileRead=access))")
     @MatchRule("(If (FloatEquals=compare value Read=access))")
     @MatchRule("(If (FloatEquals=compare value FloatingRead=access))")
+    @MatchRule("(If (FloatEquals=compare value VolatileRead=access))")
     @MatchRule("(If (FloatLessThan=compare value Read=access))")
     @MatchRule("(If (FloatLessThan=compare value FloatingRead=access))")
+    @MatchRule("(If (FloatLessThan=compare value VolatileRead=access))")
     @MatchRule("(If (PointerEquals=compare value Read=access))")
     @MatchRule("(If (PointerEquals=compare value FloatingRead=access))")
+    @MatchRule("(If (PointerEquals=compare value VolatileRead=access))")
     @MatchRule("(If (ObjectEquals=compare value Read=access))")
     @MatchRule("(If (ObjectEquals=compare value FloatingRead=access))")
+    @MatchRule("(If (ObjectEquals=compare value VolatileRead=access))")
     public ComplexMatchResult ifCompareMemory(IfNode root, CompareNode compare, ValueNode value, LIRLowerableAccess access) {
         return emitCompareBranchMemory(root, compare, value, access);
     }
@@ -478,6 +486,7 @@
 
     @MatchRule("(Add value Read=access)")
     @MatchRule("(Add value FloatingRead=access)")
+    @MatchRule("(Add value VolatileRead=access)")
     public ComplexMatchResult addMemory(ValueNode value, LIRLowerableAccess access) {
         OperandSize size = getMemorySize(access);
         if (size.isXmmType()) {
@@ -493,6 +502,7 @@
 
     @MatchRule("(Sub value Read=access)")
     @MatchRule("(Sub value FloatingRead=access)")
+    @MatchRule("(Sub value VolatileRead=access)")
     public ComplexMatchResult subMemory(ValueNode value, LIRLowerableAccess access) {
         OperandSize size = getMemorySize(access);
         if (size.isXmmType()) {
@@ -508,6 +518,7 @@
 
     @MatchRule("(Mul value Read=access)")
     @MatchRule("(Mul value FloatingRead=access)")
+    @MatchRule("(Mul value VolatileRead=access)")
     public ComplexMatchResult mulMemory(ValueNode value, LIRLowerableAccess access) {
         OperandSize size = getMemorySize(access);
         if (size.isXmmType()) {
@@ -523,6 +534,7 @@
 
     @MatchRule("(And value Read=access)")
     @MatchRule("(And value FloatingRead=access)")
+    @MatchRule("(And value VolatileRead=access)")
     public ComplexMatchResult andMemory(ValueNode value, LIRLowerableAccess access) {
         OperandSize size = getMemorySize(access);
         if (size.isXmmType()) {
@@ -534,6 +546,7 @@
 
     @MatchRule("(Or value Read=access)")
     @MatchRule("(Or value FloatingRead=access)")
+    @MatchRule("(Or value VolatileRead=access)")
     public ComplexMatchResult orMemory(ValueNode value, LIRLowerableAccess access) {
         OperandSize size = getMemorySize(access);
         if (size.isXmmType()) {
@@ -545,6 +558,7 @@
 
     @MatchRule("(Xor value Read=access)")
     @MatchRule("(Xor value FloatingRead=access)")
+    @MatchRule("(Xor value VolatileRead=access)")
     public ComplexMatchResult xorMemory(ValueNode value, LIRLowerableAccess access) {
         OperandSize size = getMemorySize(access);
         if (size.isXmmType()) {
@@ -565,12 +579,14 @@
 
     @MatchRule("(SignExtend Read=access)")
     @MatchRule("(SignExtend FloatingRead=access)")
+    @MatchRule("(SignExtend VolatileRead=access)")
     public ComplexMatchResult signExtend(SignExtendNode root, LIRLowerableAccess access) {
         return emitSignExtendMemory(access, root.getInputBits(), root.getResultBits(), null);
     }
 
     @MatchRule("(ZeroExtend Read=access)")
     @MatchRule("(ZeroExtend FloatingRead=access)")
+    @MatchRule("(ZeroExtend VolatileRead=access)")
     public ComplexMatchResult zeroExtend(ZeroExtendNode root, LIRLowerableAccess access) {
         AMD64Kind memoryKind = getMemoryKind(access);
         return builder -> getArithmeticLIRGenerator().emitZeroExtendMemory(memoryKind, root.getResultBits(), (AMD64AddressValue) operand(access.getAddress()), getState(access));
@@ -578,6 +594,7 @@
 
     @MatchRule("(Narrow Read=access)")
     @MatchRule("(Narrow FloatingRead=access)")
+    @MatchRule("(Narrow VolatileRead=access)")
     public ComplexMatchResult narrowRead(NarrowNode root, LIRLowerableAccess access) {
         return new ComplexMatchResult() {
             @Override
@@ -595,6 +612,7 @@
 
     @MatchRule("(SignExtend (Narrow=narrow Read=access))")
     @MatchRule("(SignExtend (Narrow=narrow FloatingRead=access))")
+    @MatchRule("(SignExtend (Narrow=narrow VolatileRead=access))")
     public ComplexMatchResult signExtendNarrowRead(SignExtendNode root, NarrowNode narrow, LIRLowerableAccess access) {
         LIRKind kind = getLIRGeneratorTool().getLIRKind(narrow.stamp(NodeView.DEFAULT));
         return emitSignExtendMemory(access, narrow.getResultBits(), root.getResultBits(), kind);
@@ -602,6 +620,7 @@
 
     @MatchRule("(FloatConvert Read=access)")
     @MatchRule("(FloatConvert FloatingRead=access)")
+    @MatchRule("(FloatConvert VolatileRead=access)")
     public ComplexMatchResult floatConvert(FloatConvertNode root, LIRLowerableAccess access) {
         switch (root.getFloatConvert()) {
             case D2F:
@@ -631,6 +650,7 @@
 
     @MatchRule("(Reinterpret Read=access)")
     @MatchRule("(Reinterpret FloatingRead=access)")
+    @MatchRule("(Reinterpret VolatileRead=access)")
     public ComplexMatchResult reinterpret(ReinterpretNode root, LIRLowerableAccess access) {
         return builder -> {
             LIRKind kind = getLIRGeneratorTool().getLIRKind(root.stamp(NodeView.DEFAULT));
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/GraalOptions.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/GraalOptions.java	Mon Nov 04 11:25:55 2019 +0000
@@ -115,7 +115,7 @@
     public static final OptionKey<Boolean> PartialUnroll = new OptionKey<>(true);
 
     @Option(help = "", type = OptionType.Expert)
-    public static final OptionKey<Float> MinimumPeelProbability = new OptionKey<>(0.35f);
+    public static final OptionKey<Float> MinimumPeelFrequency = new OptionKey<>(0.35f);
 
     @Option(help = "", type = OptionType.Expert)
     public static final OptionKey<Integer> LoopMaxUnswitch = new OptionKey<>(3);
@@ -242,9 +242,6 @@
     public static final OptionKey<Boolean> OptImplicitNullChecks = new OptionKey<>(true);
 
     @Option(help = "", type = OptionType.Debug)
-    public static final OptionKey<Boolean> OptLoopTransform = new OptionKey<>(true);
-
-    @Option(help = "", type = OptionType.Debug)
     public static final OptionKey<Boolean> OptFloatingReads = new OptionKey<>(true);
 
     @Option(help = "", type = OptionType.Debug)
@@ -291,4 +288,7 @@
 
     @Option(help = "Alignment in bytes for loop header blocks.", type = OptionType.Expert)
     public static final OptionKey<Integer> LoopHeaderAlignment = new OptionKey<>(16);
+
+    @Option(help = "Do not include membars for volatile accesses until the end of optimizations.", type = OptionType.Expert)
+    public static final OptionKey<Boolean> LateMembars = new OptionKey<>(true);
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/AbstractBlockBase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/AbstractBlockBase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -94,6 +94,38 @@
         return dominator;
     }
 
+    /**
+     * Returns the next dominator of this block that is either in the same loop of this block or in
+     * an outer loop.
+     *
+     * @return the next dominator while skipping over loops
+     */
+    public T getDominatorSkipLoops() {
+        T d = getDominator();
+
+        if (d == null) {
+            // We are at the start block and don't have a dominator.
+            return null;
+        }
+
+        if (isLoopHeader()) {
+            // We are moving out of current loop => just return dominator.
+            assert d.getLoopDepth() == getLoopDepth() - 1;
+            assert d.getLoop() != getLoop();
+            return d;
+        }
+
+        while (d.getLoop() != getLoop()) {
+            // We have a case where our dominator is in a different loop. Move further along
+            // the domiantor tree until we hit our loop again.
+            d = d.getDominator();
+        }
+
+        assert d.getLoopDepth() <= getLoopDepth();
+
+        return d;
+    }
+
     public void setDominator(T dominator) {
         this.dominator = dominator;
         this.domDepth = dominator.domDepth + 1;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/AbstractControlFlowGraph.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/AbstractControlFlowGraph.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -46,7 +46,7 @@
     T getStartBlock();
 
     /**
-     * True if block {@code a} is dominated by block {@code b}.
+     * True if block {@code a} is dominated by block {@code b} or {@code a} is equal to {@code b}.
      */
     static boolean isDominatedBy(AbstractBlockBase<?> a, AbstractBlockBase<?> b) {
         int domNumberA = a.getDominatorNumber();
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java	Mon Nov 04 11:25:55 2019 +0000
@@ -177,25 +177,6 @@
         return true;
     }
 
-    /**
-     * Determines if one loop is a transitive parent of another loop.
-     *
-     * @param childLoop The loop for which parentLoop might be a transitive parent loop.
-     * @param parentLoop The loop which might be a transitive parent loop of child loop.
-     * @return {@code true} if parentLoop is a (transitive) parent loop of childLoop, {@code false}
-     *         otherwise
-     */
-    public static <T extends AbstractBlockBase<T>> boolean transitiveParentLoop(Loop<T> childLoop, Loop<T> parentLoop) {
-        Loop<T> curr = childLoop;
-        while (curr != null) {
-            if (curr == parentLoop) {
-                return true;
-            }
-            curr = curr.getParent();
-        }
-        return false;
-    }
-
     @Override
     public int hashCode() {
         return index + depth * 31;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IntegerStamp.java	Mon Nov 04 11:25:55 2019 +0000
@@ -167,8 +167,15 @@
     @Override
     public Stamp constant(Constant c, MetaAccessProvider meta) {
         if (c instanceof PrimitiveConstant) {
-            long value = ((PrimitiveConstant) c).asLong();
-            return StampFactory.forInteger(getBits(), value, value);
+            PrimitiveConstant primitiveConstant = (PrimitiveConstant) c;
+            long value = primitiveConstant.asLong();
+            if (primitiveConstant.getJavaKind() == JavaKind.Boolean && value == 1) {
+                // Need to special case booleans as integer stamps are always signed values.
+                value = -1;
+            }
+            Stamp returnedStamp = StampFactory.forInteger(getBits(), value, value);
+            assert returnedStamp.hasValues();
+            return returnedStamp;
         }
         return this;
     }
@@ -1605,7 +1612,10 @@
                             long newUpMask = stamp.upMask() & defaultMask;
                             long newLowerBound = CodeUtil.signExtend((lowerBound | newDownMask) & newUpMask, resultBits);
                             long newUpperBound = CodeUtil.signExtend((upperBound | newDownMask) & newUpMask, resultBits);
-                            return new IntegerStamp(resultBits, newLowerBound, newUpperBound, newDownMask, newUpMask);
+
+                            IntegerStamp result = new IntegerStamp(resultBits, newLowerBound, newUpperBound, newDownMask, newUpMask);
+                            assert result.hasValues();
+                            return result;
                         }
                     },
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCNodeMatchRules.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCNodeMatchRules.java	Mon Nov 04 11:25:55 2019 +0000
@@ -134,12 +134,14 @@
 
     @MatchRule("(SignExtend Read=access)")
     @MatchRule("(SignExtend FloatingRead=access)")
+    @MatchRule("(SignExtend VolatileRead=access)")
     public ComplexMatchResult signExtend(SignExtendNode root, Access access) {
         return emitSignExtendMemory(access, root.getInputBits(), root.getResultBits());
     }
 
     @MatchRule("(ZeroExtend Read=access)")
     @MatchRule("(ZeroExtend FloatingRead=access)")
+    @MatchRule("(ZeroExtend VolatileRead=access)")
     public ComplexMatchResult zeroExtend(ZeroExtendNode root, Access access) {
         return emitZeroExtendMemory(access, root.getInputBits(), root.getResultBits());
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/BoxingEliminationTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/BoxingEliminationTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -313,7 +313,7 @@
         graph = parseEager(snippet, AllowAssumptions.NO);
         HighTierContext context = getDefaultHighTierContext();
         createInliningPhase().apply(graph, context);
-        new PartialEscapePhase(false, new CanonicalizerPhase(), graph.getOptions()).apply(graph, context);
+        new PartialEscapePhase(false, createCanonicalizerPhase(), graph.getOptions()).apply(graph, context);
     }
 
     private void compareGraphs(final String snippet, final String referenceSnippet) {
@@ -323,7 +323,7 @@
     private void compareGraphs(final String snippet, final String referenceSnippet, final boolean loopPeeling, final boolean excludeVirtual) {
         graph = parseEager(snippet, AllowAssumptions.NO);
         HighTierContext context = getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase();
         canonicalizer.apply(graph, context);
         createInliningPhase().apply(graph, context);
         if (loopPeeling) {
@@ -339,7 +339,7 @@
         StructuredGraph referenceGraph = parseEager(referenceSnippet, AllowAssumptions.YES);
         createInliningPhase().apply(referenceGraph, context);
         new DeadCodeEliminationPhase().apply(referenceGraph);
-        new CanonicalizerPhase().apply(referenceGraph, context);
+        this.createCanonicalizerPhase().apply(referenceGraph, context);
 
         assertEquals(referenceGraph, graph, excludeVirtual, true);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CheckGraalInvariants.java	Mon Nov 04 11:25:55 2019 +0000
@@ -78,7 +78,7 @@
 import org.graalvm.compiler.runtime.RuntimeProvider;
 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
 import org.graalvm.compiler.test.AddExports;
-import org.graalvm.compiler.test.ModuleSupport;
+import org.graalvm.compiler.api.test.ModuleSupport;
 import jdk.internal.vm.compiler.word.LocationIdentity;
 import org.junit.Assert;
 import org.junit.Assume;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CompareCanonicalizerTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CompareCanonicalizerTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -31,14 +31,13 @@
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.ConditionalNode;
 import org.graalvm.compiler.nodes.calc.IntegerTestNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class CompareCanonicalizerTest extends GraalCompilerTest {
 
     private StructuredGraph getCanonicalizedGraph(String name) {
         StructuredGraph graph = parseEager(name, AllowAssumptions.YES);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         return graph;
     }
 
@@ -55,7 +54,7 @@
             StructuredGraph graph = parseEager("canonicalCompare" + i, AllowAssumptions.NO);
             assertEquals(referenceGraph, graph);
         }
-        new CanonicalizerPhase().apply(referenceGraph, getProviders());
+        createCanonicalizerPhase().apply(referenceGraph, getProviders());
         for (int i = 1; i < 4; i++) {
             StructuredGraph graph = getCanonicalizedGraph("canonicalCompare" + i);
             assertEquals(referenceGraph, graph);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CompareCanonicalizerTest2.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CompareCanonicalizerTest2.java	Mon Nov 04 11:25:55 2019 +0000
@@ -26,7 +26,6 @@
 
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class CompareCanonicalizerTest2 extends GraalCompilerTest {
@@ -35,7 +34,7 @@
 
     private StructuredGraph getCanonicalizedGraph(String name) {
         StructuredGraph graph = getRegularGraph(name);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         return graph;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CompareCanonicalizerTest3.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CompareCanonicalizerTest3.java	Mon Nov 04 11:25:55 2019 +0000
@@ -239,7 +239,7 @@
     protected void assertCanonicallyEqual(String snippet, String reference) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
         CoreProviders context = getProviders();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase();
 
         canonicalizer.apply(graph, context);
         new GuardLoweringPhase().apply(graph, new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo()));
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationMulTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationMulTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -78,7 +78,7 @@
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
         HighTierContext context = getDefaultHighTierContext();
         new ConditionalEliminationPhase(false).apply(graph, context);
-        CanonicalizerPhase c = new CanonicalizerPhase();
+        CanonicalizerPhase c = createCanonicalizerPhase();
         c.apply(graph, context);
         new ConditionalEliminationPhase(false).apply(graph, context);
         c.apply(graph, context);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest10.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest10.java	Mon Nov 04 11:25:55 2019 +0000
@@ -30,7 +30,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.spi.CoreProviders;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.ConditionalEliminationPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.junit.Assert;
@@ -95,7 +94,7 @@
     private void test(String snippet, int guardCount) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
         CoreProviders context = getProviders();
-        new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+        new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
         new ConditionalEliminationPhase(true).apply(graph, context);
         Assert.assertEquals(guardCount, graph.getNodes().filter(GuardNode.class).count());
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest14.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest14.java	Mon Nov 04 11:25:55 2019 +0000
@@ -70,7 +70,7 @@
     @Test
     public void test1() {
         StructuredGraph graph = parseEager("test1Snippet", AllowAssumptions.YES);
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         CoreProviders context = getProviders();
 
         /* Convert the LoadIndexNode to ReadNode with floating guards. */
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest15.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest15.java	Mon Nov 04 11:25:55 2019 +0000
@@ -47,10 +47,10 @@
     private void checkNodeCount(String methodName, Class<? extends Node> nodeClass, int count) {
         StructuredGraph graph = parseEager(methodName, AllowAssumptions.YES);
 
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase();
         CoreProviders context = getProviders();
 
-        new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+        new LoweringPhase(this.createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
         canonicalizer.apply(graph, context);
 
         // Merge arr.length reads.
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest16.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest16.java	Mon Nov 04 11:25:55 2019 +0000
@@ -31,7 +31,6 @@
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -69,7 +68,7 @@
                     break;
                 }
             }
-            new CanonicalizerPhase().apply(graph, getDefaultHighTierContext());
+            this.createCanonicalizerPhase().apply(graph, getDefaultHighTierContext());
         }
         super.checkHighTierGraph(graph);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest2.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest2.java	Mon Nov 04 11:25:55 2019 +0000
@@ -109,7 +109,7 @@
     @Test
     public void testRedundantCompares() {
         StructuredGraph graph = parseEager("testRedundantComparesSnippet", AllowAssumptions.YES);
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         CoreProviders context = getProviders();
 
         new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
@@ -132,7 +132,7 @@
     public void testInstanceOfCheckCastLowered() {
         StructuredGraph graph = parseEager("testInstanceOfCheckCastSnippet", AllowAssumptions.YES);
 
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         CoreProviders context = getProviders();
 
         new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
@@ -146,7 +146,7 @@
     private void checkInstanceOfCount(String methodName, int count) {
         StructuredGraph graph = parseEager(methodName, AllowAssumptions.YES);
 
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase();
         CoreProviders context = getProviders();
 
         canonicalizer.apply(graph, context);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -69,8 +69,8 @@
         DebugContext debug = graph.getDebug();
         debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
         CoreProviders context = getProviders();
-        CanonicalizerPhase canonicalizer1 = new CanonicalizerPhase();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer1 = createCanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         try (DebugContext.Scope scope = debug.scope("ConditionalEliminationTest", graph)) {
             prepareGraph(graph, canonicalizer1, context, applyLowering);
             new IterativeConditionalEliminationPhase(canonicalizer, true).apply(graph, context);
@@ -106,10 +106,9 @@
     public void testProxies(String snippet, int expectedProxiesCreated) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
         CoreProviders context = getProviders();
-        CanonicalizerPhase canonicalizer1 = new CanonicalizerPhase();
-        canonicalizer1.disableSimplification();
+        CanonicalizerPhase canonicalizer1 = CanonicalizerPhase.createWithoutCFGSimplification();
         canonicalizer1.apply(graph, context);
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
         canonicalizer.apply(graph, context);
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -43,6 +43,7 @@
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
+import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Test;
@@ -559,7 +560,7 @@
         }
 
         public void rewrite(LoopsData loops) {
-            InductionVariable inductionVariable = loops.getInductionVariable(iv);
+            InductionVariable inductionVariable = loops.getInductionVariable(GraphUtil.unproxify(iv));
             ValueNode node = null;
             if (inductionVariable == null) {
                 assert loopCanBeRemoved;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DeMorganCanonicalizationTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DeMorganCanonicalizationTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,6 @@
 import org.graalvm.compiler.nodes.calc.AndNode;
 import org.graalvm.compiler.nodes.calc.NotNode;
 import org.graalvm.compiler.nodes.calc.OrNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -46,7 +45,7 @@
     @Test
     public void testAnd() {
         StructuredGraph g = parseEager("and", AllowAssumptions.NO, getInitialOptions());
-        new CanonicalizerPhase().apply(g, getDefaultHighTierContext());
+        createCanonicalizerPhase().apply(g, getDefaultHighTierContext());
         Assert.assertEquals(1, g.getNodes().filter(OrNode.class).count());
         Assert.assertEquals(1, g.getNodes().filter(NotNode.class).count());
 
@@ -59,7 +58,7 @@
     @Test
     public void testOr() {
         StructuredGraph g = parseEager("or", AllowAssumptions.NO, getInitialOptions());
-        new CanonicalizerPhase().apply(g, getDefaultHighTierContext());
+        createCanonicalizerPhase().apply(g, getDefaultHighTierContext());
         Assert.assertEquals(1, g.getNodes().filter(AndNode.class).count());
         Assert.assertEquals(1, g.getNodes().filter(NotNode.class).count());
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DegeneratedLoopsTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DegeneratedLoopsTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -28,7 +28,6 @@
 import org.graalvm.compiler.debug.DebugDumpScope;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Test;
 
@@ -85,7 +84,7 @@
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             HighTierContext context = getDefaultHighTierContext();
             createInliningPhase().apply(graph, context);
-            new CanonicalizerPhase().apply(graph, context);
+            createCanonicalizerPhase().apply(graph, context);
             debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
             StructuredGraph referenceGraph = parseEager(REFERENCE_SNIPPET, AllowAssumptions.YES);
             debug.dump(DebugContext.BASIC_LEVEL, referenceGraph, "ReferenceGraph");
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/EnumSwitchTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/EnumSwitchTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -29,7 +29,6 @@
 import org.graalvm.compiler.nodes.java.LoadIndexedNode;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.Phase;
-import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
 import org.graalvm.compiler.phases.tiers.Suites;
 import org.junit.Assume;
 import org.junit.Test;
@@ -157,7 +156,7 @@
                 return "CheckGraphPhase";
             }
         });
-        ret.getHighTier().findPhase(RemoveValueProxyPhase.class).add(new Phase() {
+        ret.getHighTier().addBeforeLast(new Phase() {
             @Override
             protected void run(StructuredGraph graph) {
                 /* Re-writing of the switch cases eliminates the array load. */
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FinalizableSubclassTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -38,7 +38,6 @@
 import org.graalvm.compiler.nodes.java.RegisterFinalizerNode;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Assert;
 import org.junit.Test;
@@ -80,7 +79,7 @@
         new GraphBuilderPhase.Instance(getProviders(), conf, OptimisticOptimizations.ALL, null).apply(graph);
         HighTierContext context = new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
         createInliningPhase().apply(graph, context);
-        new CanonicalizerPhase().apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         return graph;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FloatingReadTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/FloatingReadTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -34,7 +34,6 @@
 import org.graalvm.compiler.nodes.memory.FloatingReadNode;
 import org.graalvm.compiler.nodes.spi.CoreProviders;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.FloatingReadPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.junit.Assert;
@@ -68,7 +67,7 @@
 
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             CoreProviders context = getProviders();
-            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+            new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
             new FloatingReadPhase().apply(graph);
 
             ReturnNode returnNode = null;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -126,7 +126,7 @@
 import org.graalvm.compiler.runtime.RuntimeProvider;
 import org.graalvm.compiler.test.AddExports;
 import org.graalvm.compiler.test.GraalTest;
-import org.graalvm.compiler.test.ModuleSupport;
+import org.graalvm.compiler.api.test.ModuleSupport;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.BeforeClass;
@@ -643,7 +643,7 @@
     }
 
     protected final BasePhase<HighTierContext> createInliningPhase() {
-        return createInliningPhase(new CanonicalizerPhase());
+        return createInliningPhase(this.createCanonicalizerPhase());
     }
 
     protected BasePhase<HighTierContext> createInliningPhase(CanonicalizerPhase canonicalizer) {
@@ -1119,6 +1119,12 @@
         return graph;
     }
 
+    protected StructuredGraph getFinalGraph(ResolvedJavaMethod method, OptionValues options) {
+        StructuredGraph graph = parseForCompile(method, options);
+        applyFrontEnd(graph);
+        return graph;
+    }
+
     @SuppressWarnings("try")
     protected void applyFrontEnd(StructuredGraph graph) {
         DebugContext debug = graph.getDebug();
@@ -1515,4 +1521,8 @@
     protected boolean isArchitecture(String name) {
         return name.equals(backend.getTarget().arch.getName());
     }
+
+    protected CanonicalizerPhase createCanonicalizerPhase() {
+        return CanonicalizerPhase.create();
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraphEncoderTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraphEncoderTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -35,7 +35,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.spi.CoreProviders;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -61,7 +60,7 @@
                 StructuredGraph originalGraph = parseEager(javaMethod, AllowAssumptions.YES);
                 if (canonicalize) {
                     CoreProviders context = getProviders();
-                    new CanonicalizerPhase().apply(originalGraph, context);
+                    createCanonicalizerPhase().apply(originalGraph, context);
                 }
                 originalGraphs.add(originalGraph);
             }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardPrioritiesTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardPrioritiesTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -86,7 +86,7 @@
     private StructuredGraph prepareGraph(String method) {
         StructuredGraph graph = parseEager(method, StructuredGraph.AllowAssumptions.YES);
         HighTierContext highTierContext = getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         new ConvertDeoptimizeToGuardPhase().apply(graph, highTierContext);
         new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highTierContext);
         new FloatingReadPhase().apply(graph);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/IfCanonicalizerTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/IfCanonicalizerTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -35,7 +35,6 @@
 import org.graalvm.compiler.nodes.spi.CoreProviders;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.FloatingReadPhase;
 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
@@ -227,12 +226,12 @@
     private void testCombinedIf(String snippet, int count) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
         CoreProviders context = getProviders();
-        new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+        new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
         new FloatingReadPhase().apply(graph);
         MidTierContext midContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
         new GuardLoweringPhase().apply(graph, midContext);
-        new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
-        new CanonicalizerPhase().apply(graph, context);
+        new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
+        createCanonicalizerPhase().apply(graph, context);
         assertDeepEquals(count, graph.getNodes().filter(IfNode.class).count());
     }
 
@@ -247,7 +246,7 @@
             }
         }
         debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         for (FrameState fs : param.usages().filter(FrameState.class).snapshot()) {
             fs.replaceFirstInput(param, null);
             param.safeDelete();
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ImplicitNullCheckTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ImplicitNullCheckTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -34,7 +34,6 @@
 import org.graalvm.compiler.nodes.spi.CoreProviders;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.FloatingReadPhase;
 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
@@ -72,7 +71,7 @@
         try (DebugContext.Scope s = debug.scope("FloatingReadTest", new DebugDumpScope(snippet))) {
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
             CoreProviders context = getProviders();
-            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+            new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
             new FloatingReadPhase().apply(graph);
             MidTierContext midTierContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
             new GuardLoweringPhase().apply(graph, midTierContext);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/IntegerEqualsCanonicalizerTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/IntegerEqualsCanonicalizerTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -27,7 +27,6 @@
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class IntegerEqualsCanonicalizerTest extends GraalCompilerTest {
@@ -166,7 +165,7 @@
 
     private StructuredGraph getCanonicalizedGraph(String snippet) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         for (FrameState state : graph.getNodes(FrameState.TYPE).snapshot()) {
             state.replaceAtUsages(null);
             state.safeDelete();
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InterfaceMethodHandleTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InterfaceMethodHandleTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -30,7 +30,7 @@
 
 import org.graalvm.compiler.code.CompilationResult;
 import org.graalvm.compiler.debug.DebugContext;
-import org.graalvm.compiler.test.ExportingClassLoader;
+import org.graalvm.compiler.api.test.ExportingClassLoader;
 import org.junit.Test;
 import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.Label;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InvokeExceptionTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InvokeExceptionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -30,7 +30,6 @@
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Test;
@@ -69,8 +68,8 @@
             hints.put(invoke, 1000d);
         }
         HighTierContext context = getDefaultHighTierContext();
-        createInliningPhase(hints, new CanonicalizerPhase()).apply(graph, context);
-        new CanonicalizerPhase().apply(graph, context);
+        createInliningPhase(hints, createCanonicalizerPhase()).apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         new DeadCodeEliminationPhase().apply(graph);
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InvokeHintsTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/InvokeHintsTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -30,7 +30,6 @@
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Test;
@@ -80,8 +79,8 @@
         }
 
         HighTierContext context = getDefaultHighTierContext();
-        createInliningPhase(hints, new CanonicalizerPhase()).apply(graph, context);
-        new CanonicalizerPhase().apply(graph, context);
+        createInliningPhase(hints, createCanonicalizerPhase()).apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         new DeadCodeEliminationPhase().apply(graph);
         StructuredGraph referenceGraph = parseEager(REFERENCE_SNIPPET, AllowAssumptions.NO);
         assertEquals(referenceGraph, graph);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LateMembarInsertionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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 jdk.vm.ci.meta.ConstantReflectionProvider;
+import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.internal.vm.compiler.collections.EconomicMap;
+import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.cfg.Block;
+import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
+import org.graalvm.compiler.nodes.memory.FixedAccessNode;
+import org.graalvm.compiler.nodes.memory.MemoryAccess;
+import org.graalvm.compiler.nodes.memory.ReadNode;
+import org.graalvm.compiler.nodes.memory.address.AddressNode;
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionValues;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.graalvm.compiler.core.common.GraalOptions.StressTestEarlyReads;
+
+public class LateMembarInsertionTest extends GraalCompilerTest {
+
+    private final ResolvedJavaType volatileAccessType = getMetaAccess().lookupJavaType(VolatileAccess.class);
+    private final ResolvedJavaType regularAccessField = getMetaAccess().lookupJavaType(RegularAccess.class);
+    private final ResolvedJavaType volatileAccess2Type = getMetaAccess().lookupJavaType(VolatileAccess2.class);
+
+    static class VolatileAccess {
+        static volatile int field;
+    }
+
+    static class VolatileAccess2 {
+        static volatile int field;
+    }
+
+    static class RegularAccess {
+        static int field;
+    }
+
+    public static int volatileFieldLoadFieldLoad() {
+        int v1 = VolatileAccess.field;
+        int v2 = RegularAccess.field;
+        return v1 + v2;
+    }
+
+    @Test
+    public void test01() {
+        List<TypePair> accesses = compile("volatileFieldLoadFieldLoad", stressTestEarlyReads());
+
+        Assert.assertEquals(accesses.size(), 2);
+        Assert.assertEquals(accesses.get(0).getType(), volatileAccessType);
+        Assert.assertEquals(accesses.get(1).getType(), regularAccessField);
+        Assert.assertTrue(accesses.get(0).isRead());
+        Assert.assertTrue(accesses.get(1).isRead());
+    }
+
+    public static int volatileFieldLoadVolatileFieldLoad() {
+        int v1 = VolatileAccess.field;
+        int v2 = VolatileAccess2.field;
+        return v1 + v2;
+    }
+
+    @Test
+    public void test02() {
+        List<TypePair> accesses = compile("volatileFieldLoadVolatileFieldLoad", stressTestEarlyReads());
+
+        Assert.assertEquals(accesses.size(), 2);
+        Assert.assertEquals(accesses.get(0).getType(), volatileAccessType);
+        Assert.assertEquals(accesses.get(1).getType(), volatileAccess2Type);
+        Assert.assertTrue(accesses.get(0).isRead());
+        Assert.assertTrue(accesses.get(1).isRead());
+    }
+
+    public static int volatileFieldLoadVolatileFieldStore(int v2) {
+        int v1 = VolatileAccess.field;
+        VolatileAccess2.field = v2;
+        return v1;
+    }
+
+    @Test
+    public void test03() {
+        List<TypePair> accesses = compile("volatileFieldLoadVolatileFieldStore");
+
+        Assert.assertEquals(accesses.size(), 2);
+        Assert.assertEquals(accesses.get(0).getType(), volatileAccessType);
+        Assert.assertEquals(accesses.get(1).getType(), volatileAccess2Type);
+        Assert.assertTrue(accesses.get(0).isRead());
+        Assert.assertTrue(accesses.get(1).isWrite());
+    }
+
+    public static int volatileFieldStoreVolatileFieldLoad(int v2) {
+        VolatileAccess.field = v2;
+        return VolatileAccess2.field;
+    }
+
+    @Test
+    public void test04() {
+        List<TypePair> accesses = compile("volatileFieldStoreVolatileFieldLoad", stressTestEarlyReads());
+
+        Assert.assertEquals(accesses.size(), 2);
+        Assert.assertEquals(accesses.get(0).getType(), volatileAccessType);
+        Assert.assertEquals(accesses.get(1).getType(), volatileAccess2Type);
+        Assert.assertTrue(accesses.get(0).isWrite());
+        Assert.assertTrue(accesses.get(1).isRead());
+    }
+
+    public static int fieldLoadVolatileFieldStore(int v2) {
+        int v1 = RegularAccess.field;
+        VolatileAccess2.field = v2;
+        return v1;
+    }
+
+    @Test
+    public void test05() {
+        List<TypePair> accesses = compile("fieldLoadVolatileFieldStore");
+
+        Assert.assertEquals(accesses.size(), 2);
+        Assert.assertEquals(accesses.get(0).getType(), regularAccessField);
+        Assert.assertEquals(accesses.get(1).getType(), volatileAccess2Type);
+        Assert.assertTrue(accesses.get(0).isRead());
+        Assert.assertTrue(accesses.get(1).isWrite());
+    }
+
+    public static void volatileFieldStoreVolatileFieldStore(int v1, int v2) {
+        VolatileAccess.field = v1;
+        VolatileAccess2.field = v2;
+    }
+
+    @Test
+    public void test06() {
+        List<TypePair> accesses = compile("volatileFieldStoreVolatileFieldStore");
+
+        Assert.assertEquals(accesses.size(), 2);
+        Assert.assertEquals(accesses.get(0).getType(), volatileAccessType);
+        Assert.assertEquals(accesses.get(1).getType(), volatileAccess2Type);
+        Assert.assertTrue(accesses.get(0).isWrite());
+        Assert.assertTrue(accesses.get(1).isWrite());
+    }
+
+    private static OptionValues stressTestEarlyReads() {
+        EconomicMap<OptionKey<?>, Object> overrides = OptionValues.newOptionMap();
+        overrides.put(StressTestEarlyReads, true);
+        return new OptionValues(getInitialOptions(), overrides);
+    }
+
+    static class TypePair {
+        private boolean isRead;
+        private ResolvedJavaType type;
+
+        TypePair(boolean isRead, ResolvedJavaType type) {
+            this.isRead = isRead;
+            this.type = type;
+        }
+
+        public boolean isRead() {
+            return isRead;
+        }
+
+        public boolean isWrite() {
+            return !isRead;
+        }
+
+        public ResolvedJavaType getType() {
+            return type;
+        }
+    }
+
+    private List<TypePair> compile(String test, OptionValues options) {
+        StructuredGraph graph = getFinalGraph(getResolvedJavaMethod(test), options);
+        return getAccesses(graph);
+    }
+
+    private List<TypePair> getAccesses(StructuredGraph graph) {
+        StructuredGraph.ScheduleResult schedule = graph.getLastSchedule();
+        ControlFlowGraph cfg = schedule.getCFG();
+        Block[] blocks = cfg.getBlocks();
+
+        return Arrays.stream(blocks).flatMap(b -> schedule.nodesFor(b).stream()).filter(n -> n instanceof MemoryAccess).map(
+                        n -> new TypePair(n instanceof ReadNode, classForAccess((FixedAccessNode) n))).collect(Collectors.toList());
+    }
+
+    private List<TypePair> compile(String test) {
+        StructuredGraph graph = getFinalGraph(getResolvedJavaMethod(test));
+        return getAccesses(graph);
+    }
+
+    private ResolvedJavaType classForAccess(FixedAccessNode n) {
+        AddressNode address = n.getAddress();
+        ValueNode base = address.getBase();
+        Stamp stamp = base.stamp(NodeView.DEFAULT);
+        MetaAccessProvider metaAccess = getMetaAccess();
+        ResolvedJavaType javaType = stamp.javaType(metaAccess);
+        if (javaType == metaAccess.lookupJavaType(Class.class) && base instanceof ConstantNode) {
+            ConstantReflectionProvider constantReflection = getConstantReflection();
+            javaType = constantReflection.asJavaType(base.asConstant());
+        }
+        return javaType;
+    }
+
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LockEliminationTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LockEliminationTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -69,7 +69,7 @@
         test("testSynchronizedSnippet", new A(), new A());
 
         StructuredGraph graph = getGraph("testSynchronizedSnippet", false);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         new LockEliminationPhase().apply(graph);
         assertDeepEquals(1, graph.getNodes().filter(RawMonitorEnterNode.class).count());
         assertDeepEquals(1, graph.getNodes().filter(MonitorExitNode.class).count());
@@ -87,7 +87,7 @@
         test("testSynchronizedMethodSnippet", new A());
 
         StructuredGraph graph = getGraph("testSynchronizedMethodSnippet", false);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         new LockEliminationPhase().apply(graph);
         assertDeepEquals(1, graph.getNodes().filter(RawMonitorEnterNode.class).count());
         assertDeepEquals(1, graph.getNodes().filter(MonitorExitNode.class).count());
@@ -104,7 +104,7 @@
     @Test
     public void testUnrolledSync() {
         StructuredGraph graph = getGraph("testUnrolledSyncSnippet", false);
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         canonicalizer.apply(graph, getProviders());
         HighTierContext context = getDefaultHighTierContext();
         new LoopFullUnrollPhase(canonicalizer, new DefaultLoopPolicies()).apply(graph, context);
@@ -117,15 +117,15 @@
         ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
         StructuredGraph graph = parseEager(method, AllowAssumptions.YES);
         HighTierContext context = getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         canonicalizer.apply(graph, context);
         createInliningPhase().apply(graph, context);
-        new CanonicalizerPhase().apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         new DeadCodeEliminationPhase().apply(graph);
         if (doEscapeAnalysis) {
             new PartialEscapePhase(true, canonicalizer, graph.getOptions()).apply(graph, context);
         }
-        new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+        new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
         return graph;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LongNodeChainTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LongNodeChainTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -37,7 +37,6 @@
 import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.extended.OpaqueNode;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
@@ -85,7 +84,7 @@
             new SchedulePhase(s).apply(graph);
         }
 
-        new CanonicalizerPhase().apply(graph, context);
+        this.createCanonicalizerPhase().apply(graph, context);
         JavaConstant asConstant = (JavaConstant) returnNode.result().asConstant();
         Assert.assertEquals(N + 1, asConstant.asInt());
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LoopFullUnrollTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LoopFullUnrollTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -32,7 +32,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.spi.CoreProviders;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class LoopFullUnrollTest extends GraalCompilerTest {
@@ -89,7 +88,7 @@
             final StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO, debug);
 
             CoreProviders context = getProviders();
-            new LoopFullUnrollPhase(new CanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context);
+            new LoopFullUnrollPhase(createCanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context);
 
             assertTrue(graph.getNodes().filter(LoopBeginNode.class).count() == loopCount);
         } catch (Throwable e) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LoopUnswitchTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LoopUnswitchTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -30,7 +30,6 @@
 import org.graalvm.compiler.loop.phases.LoopUnswitchingPhase;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class LoopUnswitchTest extends GraalCompilerTest {
@@ -133,8 +132,8 @@
         graph.clearAllStateAfter();
         referenceGraph.clearAllStateAfter();
 
-        new CanonicalizerPhase().apply(graph, getProviders());
-        new CanonicalizerPhase().apply(referenceGraph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(referenceGraph, getProviders());
         try (DebugContext.Scope s = debug.scope("Test", new DebugDumpScope("Test:" + snippet))) {
             assertEquals(referenceGraph, graph);
         } catch (Throwable e) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MarkUnsafeAccessTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MarkUnsafeAccessTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -37,7 +37,6 @@
 import java.nio.file.Path;
 
 import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
 import org.graalvm.compiler.phases.common.inlining.policy.InlineEverythingPolicy;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
@@ -125,8 +124,8 @@
         Assert.assertNotNull(getMethodImpl);
         StructuredGraph graph = parseForCompile(getMethodImpl);
         HighTierContext highContext = getDefaultHighTierContext();
-        new CanonicalizerPhase().apply(graph, highContext);
-        new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase()).apply(graph, highContext);
+        createCanonicalizerPhase().apply(graph, highContext);
+        new InliningPhase(new InlineEverythingPolicy(), createCanonicalizerPhase()).apply(graph, highContext);
         InstalledCode compiledCode = getCode(getMethodImpl, graph);
         testMappedByteBuffer(mbb -> {
             try {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryGraphCanonicalizeTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryGraphCanonicalizeTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -28,7 +28,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.memory.WriteNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.FloatingReadPhase;
 import org.graalvm.compiler.phases.common.IncrementalCanonicalizerPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
@@ -76,10 +75,9 @@
     public void testGraph(String name, int expectedWrites) {
         StructuredGraph graph = parseEager(name, StructuredGraph.AllowAssumptions.YES);
         HighTierContext context = getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
-        new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
-        new IncrementalCanonicalizerPhase<>(canonicalizer, new FloatingReadPhase()).apply(graph, context);
-        new CanonicalizerPhase().apply(graph, context);
+        new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+        new IncrementalCanonicalizerPhase<>(createCanonicalizerPhase(), new FloatingReadPhase()).apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         int writes = graph.getNodes().filter(WriteNode.class).count();
         assertTrue(writes == expectedWrites, "Expected %d writes, found %d", expectedWrites, writes);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryScheduleTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MemoryScheduleTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -38,7 +38,6 @@
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.nodes.ReturnNode;
-import org.graalvm.compiler.nodes.StartNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
@@ -177,7 +176,6 @@
     @Test
     public void testLoop1() {
         ScheduleResult schedule = getFinalSchedule("testLoop1Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(6, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, true);
         assertReadWithinAllReturnBlocks(schedule, false);
     }
@@ -202,7 +200,6 @@
     @Test
     public void testLoop2() {
         ScheduleResult schedule = getFinalSchedule("testLoop2Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(6, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, true);
     }
@@ -224,7 +221,6 @@
     @Test
     public void testLoop3() {
         ScheduleResult schedule = getFinalSchedule("testLoop3Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(6, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, true);
         assertReadWithinAllReturnBlocks(schedule, false);
     }
@@ -260,7 +256,6 @@
     @Test
     public void testLoop5() {
         ScheduleResult schedule = getFinalSchedule("testLoop5Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(10, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, false);
     }
@@ -289,7 +284,6 @@
     @Test
     public void testLoop6() {
         ScheduleResult schedule = getFinalSchedule("testLoop6Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(13, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, false);
     }
@@ -322,7 +316,6 @@
     @Test
     public void testLoop7() {
         ScheduleResult schedule = getFinalSchedule("testLoop7Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(18, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, false);
     }
@@ -349,7 +342,6 @@
     @Test
     public void testLoop8() {
         ScheduleResult schedule = getFinalSchedule("testLoop8Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(10, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, true);
         assertReadWithinAllReturnBlocks(schedule, false);
     }
@@ -391,7 +383,6 @@
     @Test
     public void testIfRead1() {
         ScheduleResult schedule = getFinalSchedule("testIfRead1Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(3, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, true);
         assertReadAndWriteInSameBlock(schedule, false);
     }
@@ -412,7 +403,6 @@
     @Test
     public void testIfRead2() {
         ScheduleResult schedule = getFinalSchedule("testIfRead2Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(3, schedule.getCFG().getBlocks().length);
         assertDeepEquals(1, schedule.getCFG().graph.getNodes().filter(FloatingReadNode.class).count());
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, false);
@@ -434,7 +424,6 @@
     @Test
     public void testIfRead3() {
         ScheduleResult schedule = getFinalSchedule("testIfRead3Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(4, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, true);
     }
@@ -455,7 +444,6 @@
     @Test
     public void testIfRead4() {
         ScheduleResult schedule = getFinalSchedule("testIfRead4Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(3, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, false);
         assertReadAndWriteInSameBlock(schedule, true);
@@ -474,7 +462,6 @@
     @Test
     public void testIfRead5() {
         ScheduleResult schedule = getFinalSchedule("testIfRead5Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(4, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, true);
         assertReadAndWriteInSameBlock(schedule, false);
@@ -500,7 +487,6 @@
     @Test
     public void testAntiDependency() {
         ScheduleResult schedule = getFinalSchedule("testAntiDependencySnippet", TestMode.WITHOUT_FRAMESTATES);
-        assertDeepEquals(4, schedule.getCFG().getBlocks().length);
         assertReadBeforeAllWritesInStartBlock(schedule);
     }
 
@@ -527,7 +513,6 @@
         StructuredGraph graph = schedule.getCFG().graph;
         NodeIterable<WriteNode> writeNodes = graph.getNodes().filter(WriteNode.class);
 
-        assertDeepEquals(1, schedule.getCFG().getBlocks().length);
         assertDeepEquals(8, writeNodes.count());
         assertDeepEquals(1, graph.getNodes().filter(FloatingReadNode.class).count());
 
@@ -708,7 +693,7 @@
         DebugContext debug = graph.getDebug();
         try (DebugContext.Scope d = debug.scope("FloatingReadTest", graph)) {
             HighTierContext context = getDefaultHighTierContext();
-            CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+            CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
             canonicalizer.apply(graph, context);
             if (mode == TestMode.INLINED_WITHOUT_FRAMESTATES) {
                 createInliningPhase(canonicalizer).apply(graph, context);
@@ -729,7 +714,6 @@
 
             SchedulePhase schedule = new SchedulePhase(schedulingStrategy);
             schedule.apply(graph);
-            assertDeepEquals(1, graph.getNodes().filter(StartNode.class).count());
             return graph.getLastSchedule();
         } catch (Throwable e) {
             throw debug.handle(e);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MergeCanonicalizerTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -30,7 +30,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Test;
 
@@ -70,8 +69,8 @@
 
     private void testReturnCount(String snippet, int returnCount) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
-        new CanonicalizerPhase().apply(graph, getProviders());
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         graph.getDebug().dump(DebugContext.BASIC_LEVEL, graph, "Graph");
         assertDeepEquals(returnCount, graph.getNodes(ReturnNode.TYPE).count());
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MonitorGraphTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/MonitorGraphTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -36,7 +36,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.java.MonitorExitNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Assert;
@@ -103,8 +102,8 @@
             hints.put(invoke, 1000d);
         }
         HighTierContext context = getDefaultHighTierContext();
-        createInliningPhase(hints, new CanonicalizerPhase()).apply(graph, context);
-        new CanonicalizerPhase().apply(graph, context);
+        createInliningPhase(hints, createCanonicalizerPhase()).apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         new DeadCodeEliminationPhase().apply(graph);
         return graph;
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -36,7 +36,7 @@
 import org.graalvm.compiler.nodes.spi.CoreProviders;
 import org.graalvm.compiler.phases.BasePhase;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase.CustomCanonicalizer;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase.CustomSimplification;
 import org.graalvm.compiler.phases.contract.NodeCostUtil;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Assert;
@@ -162,19 +162,21 @@
     @Test
     public void testCanonicalizationExample() {
         HighTierContext htc = getDefaultHighTierContext();
-        ImprovementSavingCanonicalizer c1 = new ImprovementSavingCanonicalizer();
+        ImprovementSavingCalculator c1 = new ImprovementSavingCalculator();
         StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("test1Snippet"));
-        new CanonicalizerPhase(c1).apply(g1, htc);
-        ImprovementSavingCanonicalizer c2 = new ImprovementSavingCanonicalizer();
+        CanonicalizerPhase canonicalizer1 = this.createCanonicalizerPhase().copyWithCustomSimplification(c1);
+        canonicalizer1.apply(g1, htc);
+        ImprovementSavingCalculator c2 = new ImprovementSavingCalculator();
         StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("test2Snippet"));
-        new CanonicalizerPhase(c2).apply(g2, htc);
+        CanonicalizerPhase canonicalizer2 = this.createCanonicalizerPhase().copyWithCustomSimplification(c2);
+        canonicalizer2.apply(g2, htc);
         Assert.assertEquals(0, c1.savedCycles);
         Assert.assertEquals(0, c2.savedCycles);
     }
 
-    private static void prepareGraphForLoopFrequencies(StructuredGraph g, HighTierContext htc) {
+    private void prepareGraphForLoopFrequencies(StructuredGraph g, HighTierContext htc) {
         // let canonicalizer work away branch probability nodes
-        new CanonicalizerPhase().apply(g, htc);
+        createCanonicalizerPhase().apply(g, htc);
         // recompute the loop frequencies
         ComputeLoopFrequenciesClosure.compute(g);
     }
@@ -242,8 +244,8 @@
         StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("test1Snippet"));
         StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("test2Snippet"));
         HighTierContext htc = getDefaultHighTierContext();
-        new CanonicalizerPhase().apply(g1, htc);
-        new CanonicalizerPhase().apply(g2, htc);
+        createCanonicalizerPhase().apply(g1, htc);
+        createCanonicalizerPhase().apply(g2, htc);
         GraphCostPhase gc1 = new GraphCostPhase();
         GraphCostPhase gc2 = new GraphCostPhase();
         gc1.apply(g1, htc);
@@ -257,7 +259,7 @@
     public void testExpectUntrusted() {
         StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("untrused01"));
         HighTierContext htc = getDefaultHighTierContext();
-        new CanonicalizerPhase().apply(g1, htc);
+        createCanonicalizerPhase().apply(g1, htc);
         GraphCostPhase gc1 = new GraphCostPhase();
         gc1.apply(g1, htc);
     }
@@ -266,7 +268,7 @@
     public void testArrayLoad() {
         StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("arrayLoadTest"));
         HighTierContext htc = getDefaultHighTierContext();
-        new CanonicalizerPhase().apply(g1, htc);
+        createCanonicalizerPhase().apply(g1, htc);
         GraphCostPhase gc1 = new GraphCostPhase();
         gc1.apply(g1, htc);
         Assert.assertEquals(15, gc1.finalCycles, 25);
@@ -276,7 +278,7 @@
     public void testArrayStore() {
         StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("arrayStoreTest"));
         HighTierContext htc = getDefaultHighTierContext();
-        new CanonicalizerPhase().apply(g1, htc);
+        createCanonicalizerPhase().apply(g1, htc);
         GraphCostPhase gc1 = new GraphCostPhase();
         gc1.apply(g1, htc);
         Assert.assertEquals(15, gc1.finalCycles, 25);
@@ -286,7 +288,7 @@
     public void testFieldLoad() {
         StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("fieldLoad"));
         HighTierContext htc = getDefaultHighTierContext();
-        new CanonicalizerPhase().apply(g1, htc);
+        createCanonicalizerPhase().apply(g1, htc);
         GraphCostPhase gc1 = new GraphCostPhase();
         gc1.apply(g1, htc);
         Assert.assertEquals(15, gc1.finalCycles, 25);
@@ -296,13 +298,13 @@
     public void testFieldStore() {
         StructuredGraph g1 = parseForCompile(getResolvedJavaMethod("fieldStore"));
         HighTierContext htc = getDefaultHighTierContext();
-        new CanonicalizerPhase().apply(g1, htc);
+        createCanonicalizerPhase().apply(g1, htc);
         GraphCostPhase gc1 = new GraphCostPhase();
         gc1.apply(g1, htc);
         Assert.assertEquals(15, gc1.finalCycles, 25);
     }
 
-    static class ImprovementSavingCanonicalizer extends CustomCanonicalizer {
+    static class ImprovementSavingCalculator implements CustomSimplification {
         private int savedCycles;
 
         @Override
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PushNodesThroughPiTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PushNodesThroughPiTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -104,7 +104,7 @@
     private StructuredGraph compileTestSnippet(final String snippet) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
         CoreProviders context = getProviders();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase();
         new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
         canonicalizer.apply(graph, context);
         canonicalizer.apply(graph, context);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PushThroughIfTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/PushThroughIfTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -29,7 +29,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.util.GraphUtil;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class PushThroughIfTest extends GraalCompilerTest {
@@ -65,15 +64,15 @@
             fs.replaceAtUsages(null);
             GraphUtil.killWithUnusedFloatingInputs(fs);
         }
-        new CanonicalizerPhase().apply(graph, getProviders());
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
 
         StructuredGraph referenceGraph = parseEager(reference, AllowAssumptions.YES);
         for (FrameState fs : referenceGraph.getNodes(FrameState.TYPE).snapshot()) {
             fs.replaceAtUsages(null);
             GraphUtil.killWithUnusedFloatingInputs(fs);
         }
-        new CanonicalizerPhase().apply(referenceGraph, getProviders());
+        createCanonicalizerPhase().apply(referenceGraph, getProviders());
         assertEquals(referenceGraph, graph);
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReadAfterCheckCastTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReadAfterCheckCastTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -90,7 +90,7 @@
             // structure changes significantly
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             CoreProviders context = getProviders();
-            CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+            CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
             new FloatingReadPhase().apply(graph);
             canonicalizer.apply(graph, context);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReassociateAndCanonicalTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReassociateAndCanonicalTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -28,7 +28,6 @@
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class ReassociateAndCanonicalTest extends GraalCompilerTest {
@@ -245,9 +244,9 @@
 
     private <T extends Node & IterableNodeType> void test(String test, String ref) {
         StructuredGraph testGraph = parseEager(test, AllowAssumptions.NO);
-        new CanonicalizerPhase().apply(testGraph, getProviders());
+        createCanonicalizerPhase().apply(testGraph, getProviders());
         StructuredGraph refGraph = parseEager(ref, AllowAssumptions.NO);
-        new CanonicalizerPhase().apply(refGraph, getProviders());
+        createCanonicalizerPhase().apply(refGraph, getProviders());
         assertEquals(testGraph, refGraph);
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ScalarTypeSystemTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ScalarTypeSystemTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -28,7 +28,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.spi.CoreProviders;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 /**
@@ -134,7 +133,7 @@
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
         graph.getDebug().dump(DebugContext.BASIC_LEVEL, graph, "Graph");
         CoreProviders context = getProviders();
-        new CanonicalizerPhase().apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         StructuredGraph referenceGraph = parseEager(referenceSnippet, AllowAssumptions.NO);
         assertEquals(referenceGraph, graph);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SchedulingTest2.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SchedulingTest2.java	Mon Nov 04 11:25:55 2019 +0000
@@ -44,7 +44,6 @@
 import org.graalvm.compiler.nodes.spi.CoreProviders;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase;
 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
@@ -99,8 +98,8 @@
         }
 
         CoreProviders context = getProviders();
-        new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
-        new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, context);
+        new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+        new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, context);
         MidTierContext midContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
 
         new GuardLoweringPhase().apply(graph, midContext);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StampCanonicalizerTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StampCanonicalizerTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -27,7 +27,6 @@
 import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.junit.Test;
 
@@ -111,7 +110,7 @@
 
     private void testZeroReturn(String methodName) {
         StructuredGraph graph = parseEager(methodName, AllowAssumptions.YES);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         new DeadCodeEliminationPhase().apply(graph);
         assertConstantReturn(graph, 0);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StraighteningTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/StraighteningTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -27,7 +27,6 @@
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class StraighteningTest extends GraalCompilerTest {
@@ -90,7 +89,7 @@
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
         DebugContext debug = graph.getDebug();
         debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         StructuredGraph referenceGraph = parseEager(REFERENCE_SNIPPET, AllowAssumptions.YES);
         assertEquals(referenceGraph, graph);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SwitchCanonicalizerTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SwitchCanonicalizerTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -26,7 +26,6 @@
 
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.extended.IntegerSwitchNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class SwitchCanonicalizerTest extends GraalCompilerTest {
@@ -119,7 +118,7 @@
 
     private void shouldFoldSwitch(String methodName) {
         StructuredGraph graph = parseForCompile(getResolvedJavaMethod(methodName));
-        new CanonicalizerPhase().apply(graph, getDefaultHighTierContext());
+        createCanonicalizerPhase().apply(graph, getDefaultHighTierContext());
         assertTrue(graph.getNodes().filter(IntegerSwitchNode.class).isEmpty());
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SwitchDyingLoopTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SwitchDyingLoopTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -70,7 +70,7 @@
 
     @Test
     public void test() {
-        CanonicalizerPhase canonicalizerPhase = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizerPhase = createCanonicalizerPhase();
         HighTierContext highTierContext = getDefaultHighTierContext();
         StructuredGraph graph = parseEager("snippet", StructuredGraph.AllowAssumptions.YES);
         // there should be 1 loop and 1 switch
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SwitchFoldingTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SwitchFoldingTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -27,7 +27,6 @@
 
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.StructuredGraph;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class SwitchFoldingTest extends GraalCompilerTest {
@@ -484,7 +483,7 @@
         StructuredGraph graph = parseEager(snippet, StructuredGraph.AllowAssumptions.YES);
         DebugContext debug = graph.getDebug();
         debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         StructuredGraph referenceGraph = parseEager(ref, StructuredGraph.AllowAssumptions.YES);
         assertEquals(referenceGraph, graph);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/TypeSystemTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/TypeSystemTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -41,7 +41,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.nodes.java.InstanceOfNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.ConditionalEliminationPhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.junit.Assert;
@@ -188,13 +187,13 @@
          * reference graph.
          */
         new ConditionalEliminationPhase(false).apply(graph, getProviders());
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         // a second canonicalizer is needed to process nested MaterializeNodes
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         StructuredGraph referenceGraph = parseEager(referenceSnippet, AllowAssumptions.NO);
         new ConditionalEliminationPhase(false).apply(referenceGraph, getProviders());
-        new CanonicalizerPhase().apply(referenceGraph, getProviders());
-        new CanonicalizerPhase().apply(referenceGraph, getProviders());
+        this.createCanonicalizerPhase().apply(referenceGraph, getProviders());
+        this.createCanonicalizerPhase().apply(referenceGraph, getProviders());
         assertEquals(referenceGraph, graph);
     }
 
@@ -244,8 +243,8 @@
 
     private <T extends Node> void testHelper(String snippet, Class<T> clazz) {
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
-        new CanonicalizerPhase().apply(graph, getProviders());
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         DebugContext debug = graph.getDebug();
         debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph " + snippet);
         Assert.assertFalse("shouldn't have nodes of type " + clazz, graph.getNodes().filter(clazz).iterator().hasNext());
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeReadEliminationTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeReadEliminationTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -119,7 +119,7 @@
 
     public void testEarlyReadElimination(StructuredGraph graph, int reads, int writes) {
         CoreProviders context = getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         canonicalizer.apply(graph, context);
         new EarlyReadEliminationPhase(canonicalizer).apply(graph, context);
         Assert.assertEquals(3, graph.getNodes().filter(UnsafeAccessNode.class).count());
@@ -134,7 +134,7 @@
     public void testPartialEscapeReadElimination(StructuredGraph graph, int reads, int writes) {
         OptionValues options = graph.getOptions();
         CoreProviders context = getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         canonicalizer.apply(graph, context);
         new PartialEscapePhase(true, true, canonicalizer, null, options).apply(graph, context);
         Assert.assertEquals(3, graph.getNodes().filter(UnsafeAccessNode.class).count());
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeVirtualizationTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnsafeVirtualizationTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -147,7 +147,7 @@
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
         OptionValues options = graph.getOptions();
         CoreProviders context = getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         if (canonicalizeBefore) {
             canonicalizer.apply(graph, context);
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnusedArray.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UnusedArray.java	Mon Nov 04 11:25:55 2019 +0000
@@ -30,7 +30,6 @@
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.java.NewArrayNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Test;
 
 public class UnusedArray extends GraalCompilerTest {
@@ -66,7 +65,7 @@
 
     public void test(String method) {
         StructuredGraph graph = parseEager(method, StructuredGraph.AllowAssumptions.YES);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         NodeIterable<NewArrayNode> newArrayNodes = graph.getNodes().filter(NewArrayNode.class);
         assertThat(newArrayNodes, isEmpty());
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -29,7 +29,6 @@
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.junit.Assert;
 import org.junit.Test;
@@ -62,7 +61,7 @@
     public void test1() {
         final ResolvedJavaMethod javaMethod = getResolvedJavaMethod("testMethod");
         final StructuredGraph graph = parseEager(javaMethod, AllowAssumptions.NO);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
         new DeadCodeEliminationPhase().apply(graph);
 
         for (ConstantNode node : ConstantNode.getConstantNodes(graph)) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EATestBase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EATestBase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -37,7 +37,6 @@
 import org.graalvm.compiler.nodes.java.NewInstanceNode;
 import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
@@ -173,7 +172,7 @@
             createInliningPhase().apply(graph, context);
             new DeadCodeEliminationPhase().apply(graph);
             canonicalizeGraph();
-            new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
+            new PartialEscapePhase(iterativeEscapeAnalysis, false, createCanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
             postEACanonicalizeGraph();
             returnNodes = graph.getNodes(ReturnNode.TYPE).snapshot();
         } catch (Throwable e) {
@@ -185,6 +184,6 @@
     }
 
     protected void canonicalizeGraph() {
-        new CanonicalizerPhase().apply(graph, context);
+        this.createCanonicalizerPhase().apply(graph, context);
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EarlyReadEliminationTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EarlyReadEliminationTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -27,6 +27,9 @@
 import java.util.List;
 
 import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.nodes.IfNode;
+import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -37,7 +40,6 @@
 import org.graalvm.compiler.nodes.java.StoreFieldNode;
 import org.graalvm.compiler.nodes.memory.ReadNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.virtual.phases.ea.EarlyReadEliminationPhase;
@@ -47,6 +49,50 @@
 
     public static Object staticField;
 
+    static void cfgSnippet() {
+        if (staticField != null) {
+            staticField = 12;
+            if (staticField != null) {
+                staticField = 12;
+            }
+            if (staticField != null) {
+                staticField = 12;
+            }
+            if (staticField != null) {
+                staticField = 12;
+            }
+            if (staticField != null) {
+                staticField = 12;
+            }
+        } else {
+            if (staticField != null) {
+                staticField = 12;
+            } else {
+                if (staticField != null) {
+                    staticField = 12;
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testDeadBranches() {
+        StructuredGraph graph = parseEager(getResolvedJavaMethod("cfgSnippet"), AllowAssumptions.NO);
+        HighTierContext context = getDefaultHighTierContext();
+        int index = 0;
+        boolean[] conditions = new boolean[]{true, false, false, true, true, true, false};
+        /*
+         * Create a graph with "dead" branches in the beginning.
+         */
+        for (Node n : graph.getNodes()) {
+            if (n instanceof IfNode) {
+                IfNode ifNode = (IfNode) n;
+                ifNode.setCondition(LogicConstantNode.forBoolean(conditions[index++], graph));
+            }
+        }
+        new EarlyReadEliminationPhase(createCanonicalizerPhase()).apply(graph, context);
+    }
+
     public static class TestObject {
 
         public int x;
@@ -264,9 +310,9 @@
         HighTierContext context = getDefaultHighTierContext();
         createInliningPhase().apply(graph, context);
         if (doLowering) {
-            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+            new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
         }
-        new EarlyReadEliminationPhase(new CanonicalizerPhase()).apply(graph, context);
+        new EarlyReadEliminationPhase(createCanonicalizerPhase()).apply(graph, context);
         return graph;
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EscapeAnalysisTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/EscapeAnalysisTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -38,7 +38,6 @@
 import org.graalvm.compiler.nodes.java.LoadFieldNode;
 import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.graalvm.compiler.test.SubprocessUtil;
 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
@@ -327,7 +326,7 @@
         Assert.assertEquals(1, graph.getNodes().filter(BoxNode.class).count());
         List<Node> nodes = graph.getNodes().snapshot();
         // verify that an additional run doesn't add or remove nodes
-        new PartialEscapePhase(false, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
+        new PartialEscapePhase(false, false, createCanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
         Assert.assertEquals(nodes.size(), graph.getNodeCount());
         for (Node node : nodes) {
             Assert.assertTrue(node.isAlive());
@@ -362,9 +361,9 @@
         Assert.assertEquals(2, graph.getNodes().filter(CommitAllocationNode.class).count());
         // create the situation by removing the if
         graph.replaceFixedWithFloating(graph.getNodes().filter(LoadFieldNode.class).first(), graph.unique(ConstantNode.forInt(0)));
-        new CanonicalizerPhase().apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         // verify that an additional run removes all allocations
-        new PartialEscapePhase(false, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
+        new PartialEscapePhase(false, false, createCanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
         Assert.assertEquals(0, graph.getNodes().filter(CommitAllocationNode.class).count());
     }
 
@@ -440,8 +439,8 @@
     @Test
     public void testFullyUnrolledLoop() {
         prepareGraph("testFullyUnrolledLoopSnippet", false);
-        new LoopFullUnrollPhase(new CanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context);
-        new PartialEscapePhase(false, new CanonicalizerPhase(), graph.getOptions()).apply(graph, context);
+        new LoopFullUnrollPhase(createCanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context);
+        new PartialEscapePhase(false, createCanonicalizerPhase(), graph.getOptions()).apply(graph, context);
         Assert.assertEquals(1, returnNodes.size());
         Assert.assertTrue(returnNodes.get(0).result() instanceof AllocatedObjectNode);
         CommitAllocationNode commit = ((AllocatedObjectNode) returnNodes.get(0).result()).getCommit();
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PEAReadEliminationTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PEAReadEliminationTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -30,7 +30,6 @@
 import org.graalvm.compiler.nodes.extended.RawLoadNode;
 import org.graalvm.compiler.nodes.java.LoadIndexedNode;
 import org.graalvm.compiler.nodes.java.StoreIndexedNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
 import org.junit.Test;
@@ -181,7 +180,7 @@
         StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO);
         HighTierContext context = getDefaultHighTierContext();
         createInliningPhase().apply(graph, context);
-        new PartialEscapePhase(false, true, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
+        new PartialEscapePhase(false, true, createCanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
         return graph;
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisIterationTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisIterationTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -31,7 +31,6 @@
 import org.graalvm.compiler.nodes.extended.UnboxNode;
 import org.graalvm.compiler.nodes.java.StoreFieldNode;
 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
 import org.junit.Assert;
 import org.junit.Test;
@@ -147,7 +146,7 @@
         for (String name : new String[]{"noLoopIterationEmpty", "noLoopIteration"}) {
             prepareGraph(name, false);
             List<CommitAllocationNode> allocations = graph.getNodes().filter(CommitAllocationNode.class).snapshot();
-            new PartialEscapePhase(true, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
+            new PartialEscapePhase(true, false, createCanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
             Assert.assertEquals(1, allocations.size());
             Assert.assertTrue(allocations.get(0).isAlive());
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -44,7 +44,6 @@
 import org.graalvm.compiler.nodes.java.NewInstanceNode;
 import org.graalvm.compiler.nodes.java.StoreFieldNode;
 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 
 /**
@@ -287,7 +286,7 @@
             merge.setStateAfter(null);
         }
         new DeadCodeEliminationPhase().apply(graph);
-        new CanonicalizerPhase().apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         try {
             Assert.assertTrue("partial escape analysis should have removed all NewInstanceNode allocations", graph.getNodes().filter(NewInstanceNode.class).isEmpty());
             Assert.assertTrue("partial escape analysis should have removed all NewArrayNode allocations", graph.getNodes().filter(NewArrayNode.class).isEmpty());
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisTreesTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeAnalysisTreesTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -30,7 +30,6 @@
 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;
@@ -118,7 +117,7 @@
                 graph.removeFixed(node);
             }
             new DeadCodeEliminationPhase().apply(graph);
-            new CanonicalizerPhase().apply(graph, context);
+            createCanonicalizerPhase().apply(graph, context);
 
             InstalledCode code = getCode(method, graph, true);
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PoorMansEATest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PoorMansEATest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -35,7 +35,6 @@
 import org.graalvm.compiler.nodes.java.NewInstanceNode;
 import org.graalvm.compiler.nodes.spi.CoreProviders;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Test;
@@ -68,7 +67,7 @@
             HighTierContext highTierContext = getDefaultHighTierContext();
             createInliningPhase().apply(graph, highTierContext);
             CoreProviders context = getProviders();
-            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+            new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
 
             // remove framestates in order to trigger the simplification.
             cleanup: for (FrameState fs : graph.getNodes(FrameState.TYPE).snapshot()) {
@@ -80,7 +79,7 @@
                     }
                 }
             }
-            new CanonicalizerPhase().apply(graph, context);
+            createCanonicalizerPhase().apply(graph, context);
         } catch (Throwable e) {
             throw debug.handle(e);
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/TrufflePEATest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/TrufflePEATest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -32,7 +32,6 @@
 import org.graalvm.compiler.nodes.extended.RawLoadNode;
 import org.graalvm.compiler.nodes.extended.RawStoreNode;
 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
 import org.junit.Test;
@@ -123,7 +122,7 @@
         StructuredGraph graph = parseEager(snippet, StructuredGraph.AllowAssumptions.NO);
         HighTierContext context = getDefaultHighTierContext();
         createInliningPhase().apply(graph, context);
-        new PartialEscapePhase(true, true, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
+        new PartialEscapePhase(true, true, createCanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
         return graph;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/UnsafeEATest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -37,7 +37,6 @@
 import org.graalvm.compiler.nodes.extended.RawStoreNode;
 import org.graalvm.compiler.nodes.extended.UnsafeAccessNode;
 import org.graalvm.compiler.nodes.java.LoadFieldNode;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -123,7 +122,7 @@
         for (UnpackEndianHalfNode node : graph.getNodes().filter(UnpackEndianHalfNode.class)) {
             node.lower(getTarget().arch.getByteOrder());
         }
-        new CanonicalizerPhase().applyIncremental(graph, context, mark);
+        createCanonicalizerPhase().applyIncremental(graph, context, mark);
     }
 
     private boolean testingUnsafe;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/InliningTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/InliningTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -41,7 +41,6 @@
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
 import org.graalvm.compiler.phases.PhaseSuite;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.junit.Assert;
@@ -291,10 +290,10 @@
                                 : getDefaultGraphBuilderSuite();
                 HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL);
                 debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
-                new CanonicalizerPhase().apply(graph, context);
+                createCanonicalizerPhase().apply(graph, context);
                 createInliningPhase().apply(graph, context);
                 debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
-                new CanonicalizerPhase().apply(graph, context);
+                createCanonicalizerPhase().apply(graph, context);
                 new DeadCodeEliminationPhase().apply(graph);
                 return graph;
             }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -112,8 +112,8 @@
             StructuredGraph g1 = prepareGraph(snippet, i);
             StructuredGraph g2 = (StructuredGraph) g1.copy(g1.getDebug());
             ResolvedJavaMethod method = g1.method();
-            long elapsedRE = runAndTimePhase(g1, new EarlyReadEliminationPhase(new CanonicalizerPhase()));
-            long elapsedPEA = runAndTimePhase(g2, new PartialEscapePhase(true, new CanonicalizerPhase(), g1.getOptions()));
+            long elapsedRE = runAndTimePhase(g1, new EarlyReadEliminationPhase(createCanonicalizerPhase()));
+            long elapsedPEA = runAndTimePhase(g2, new PartialEscapePhase(true, createCanonicalizerPhase(), g1.getOptions()));
             if (LOG_PHASE_TIMINGS) {
                 TTY.printf("Needed %dms to run early partial escape analysis on a graph with %d nested loops compiling method %s\n", elapsedPEA, i, method);
             }
@@ -138,7 +138,7 @@
         StructuredGraph callerGraph = parseEager(callerMethod, AllowAssumptions.YES);
         PhaseSuite<HighTierContext> graphBuilderSuite = getDefaultGraphBuilderSuite();
         HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL);
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         Invoke next = callerGraph.getNodes(MethodCallTargetNode.TYPE).first().invoke();
         StructuredGraph calleeGraph = parseBytecodes(next.callTarget().targetMethod(), context, canonicalizer);
         ResolvedJavaMethod calleeMethod = next.callTarget().targetMethod();
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeMatchRules.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeMatchRules.java	Mon Nov 04 11:25:55 2019 +0000
@@ -63,6 +63,7 @@
 import org.graalvm.compiler.nodes.java.ValueCompareAndSwapNode;
 import org.graalvm.compiler.nodes.memory.FloatingReadNode;
 import org.graalvm.compiler.nodes.memory.ReadNode;
+import org.graalvm.compiler.nodes.memory.VolatileReadNode;
 import org.graalvm.compiler.nodes.memory.WriteNode;
 
 import jdk.vm.ci.meta.Value;
@@ -75,6 +76,7 @@
 @MatchableNode(nodeClass = LeftShiftNode.class, inputs = {"x", "y"}, ignoresSideEffects = true)
 @MatchableNode(nodeClass = NarrowNode.class, inputs = {"value"}, ignoresSideEffects = true)
 @MatchableNode(nodeClass = ReadNode.class, inputs = {"address"})
+@MatchableNode(nodeClass = VolatileReadNode.class, inputs = {"address"})
 @MatchableNode(nodeClass = ReinterpretNode.class, inputs = {"value"}, ignoresSideEffects = true)
 @MatchableNode(nodeClass = SignExtendNode.class, inputs = {"value"}, ignoresSideEffects = true)
 @MatchableNode(nodeClass = UnsignedRightShiftNode.class, inputs = {"x", "y"}, ignoresSideEffects = true)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/BaseTier.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019, 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.phases;
+
+import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
+
+import org.graalvm.compiler.loop.DefaultLoopPolicies;
+import org.graalvm.compiler.loop.LoopPolicies;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+
+public class BaseTier<C> extends PhaseSuite<C> {
+
+    public LoopPolicies createLoopPolicies() {
+        return new DefaultLoopPolicies();
+    }
+
+    public CanonicalizerPhase createCanonicalizerPhase(OptionValues options) {
+        CanonicalizerPhase canonicalizer = null;
+        if (ImmutableCode.getValue(options)) {
+            canonicalizer = CanonicalizerPhase.createWithoutReadCanonicalization();
+        } else {
+            canonicalizer = CanonicalizerPhase.create();
+        }
+        return canonicalizer;
+    }
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/EconomyHighTier.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/EconomyHighTier.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -24,23 +24,16 @@
 
 package org.graalvm.compiler.core.phases;
 
-import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
-
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 
-public class EconomyHighTier extends PhaseSuite<HighTierContext> {
+public class EconomyHighTier extends BaseTier<HighTierContext> {
 
     public EconomyHighTier(OptionValues options) {
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
-        if (ImmutableCode.getValue(options)) {
-            canonicalizer.disableReadCanonicalization();
-        }
-
+        CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase(options);
         appendPhase(canonicalizer);
         appendPhase(new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER));
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/EconomyLowTier.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/EconomyLowTier.java	Mon Nov 04 11:25:55 2019 +0000
@@ -24,29 +24,20 @@
 
 package org.graalvm.compiler.core.phases;
 
-import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
-
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.ExpandLogicPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.graalvm.compiler.phases.tiers.LowTierContext;
 
-public class EconomyLowTier extends PhaseSuite<LowTierContext> {
+public class EconomyLowTier extends BaseTier<LowTierContext> {
 
     public EconomyLowTier(OptionValues options) {
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
-        if (ImmutableCode.getValue(options)) {
-            canonicalizer.disableReadCanonicalization();
-        }
-
+        CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase(options);
         appendPhase(new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.LOW_TIER));
-
         appendPhase(new ExpandLogicPhase());
-
         appendPhase(new SchedulePhase(SchedulePhase.SchedulingStrategy.LATEST_OUT_OF_LOOPS));
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/EconomyMidTier.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/EconomyMidTier.java	Mon Nov 04 11:25:55 2019 +0000
@@ -24,11 +24,8 @@
 
 package org.graalvm.compiler.core.phases;
 
-import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
-
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.FrameStateAssignmentPhase;
 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
@@ -38,23 +35,15 @@
 import org.graalvm.compiler.phases.common.WriteBarrierAdditionPhase;
 import org.graalvm.compiler.phases.tiers.MidTierContext;
 
-public class EconomyMidTier extends PhaseSuite<MidTierContext> {
+public class EconomyMidTier extends BaseTier<MidTierContext> {
 
     public EconomyMidTier(OptionValues options) {
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
-        if (ImmutableCode.getValue(options)) {
-            canonicalizer.disableReadCanonicalization();
-        }
+        CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase(options);
         appendPhase(new RemoveValueProxyPhase());
-
         appendPhase(new LoopSafepointInsertionPhase());
-
         appendPhase(new GuardLoweringPhase());
-
         appendPhase(new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.MID_TIER));
-
         appendPhase(new FrameStateAssignmentPhase());
-
         appendPhase(new WriteBarrierAdditionPhase());
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/HighTier.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/HighTier.java	Mon Nov 04 11:25:55 2019 +0000
@@ -25,17 +25,16 @@
 package org.graalvm.compiler.core.phases;
 
 import static org.graalvm.compiler.core.common.GraalOptions.ConditionalElimination;
-import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
 import static org.graalvm.compiler.core.common.GraalOptions.LoopPeeling;
 import static org.graalvm.compiler.core.common.GraalOptions.LoopUnswitch;
 import static org.graalvm.compiler.core.common.GraalOptions.OptConvertDeoptsToGuards;
-import static org.graalvm.compiler.core.common.GraalOptions.OptLoopTransform;
 import static org.graalvm.compiler.core.common.GraalOptions.OptReadElimination;
 import static org.graalvm.compiler.core.common.GraalOptions.PartialEscapeAnalysis;
 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Optional;
 
 import org.graalvm.compiler.loop.DefaultLoopPolicies;
 import org.graalvm.compiler.loop.LoopPolicies;
+import org.graalvm.compiler.loop.phases.ConvertDeoptimizeToGuardPhase;
 import org.graalvm.compiler.loop.phases.LoopFullUnrollPhase;
 import org.graalvm.compiler.loop.phases.LoopPeelingPhase;
 import org.graalvm.compiler.loop.phases.LoopUnswitchingPhase;
@@ -44,22 +43,19 @@
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
-import org.graalvm.compiler.loop.phases.ConvertDeoptimizeToGuardPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.common.IncrementalCanonicalizerPhase;
 import org.graalvm.compiler.phases.common.IterativeConditionalEliminationPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.common.NodeCounterPhase;
-import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
 import org.graalvm.compiler.phases.common.inlining.policy.GreedyInliningPolicy;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.virtual.phases.ea.EarlyReadEliminationPhase;
 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
 
-public class HighTier extends PhaseSuite<HighTierContext> {
+public class HighTier extends BaseTier<HighTierContext> {
 
     public static class Options {
 
@@ -70,11 +66,7 @@
     }
 
     public HighTier(OptionValues options) {
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
-        if (ImmutableCode.getValue(options)) {
-            canonicalizer.disableReadCanonicalization();
-        }
-
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase(options);
         appendPhase(canonicalizer);
 
         if (NodeCounterPhase.Options.NodeCounters.getValue(options)) {
@@ -101,13 +93,12 @@
         LoopPolicies loopPolicies = createLoopPolicies();
         appendPhase(new LoopFullUnrollPhase(canonicalizer, loopPolicies));
 
-        if (OptLoopTransform.getValue(options)) {
-            if (LoopPeeling.getValue(options)) {
-                appendPhase(new IncrementalCanonicalizerPhase<>(canonicalizer, new LoopPeelingPhase(loopPolicies)));
-            }
-            if (LoopUnswitch.getValue(options)) {
-                appendPhase(new IncrementalCanonicalizerPhase<>(canonicalizer, new LoopUnswitchingPhase(loopPolicies)));
-            }
+        if (LoopPeeling.getValue(options)) {
+            appendPhase(new IncrementalCanonicalizerPhase<>(canonicalizer, new LoopPeelingPhase(loopPolicies)));
+        }
+
+        if (LoopUnswitch.getValue(options)) {
+            appendPhase(new IncrementalCanonicalizerPhase<>(canonicalizer, new LoopUnswitchingPhase(loopPolicies)));
         }
 
         if (PartialEscapeAnalysis.getValue(options)) {
@@ -118,8 +109,6 @@
             appendPhase(new EarlyReadEliminationPhase(canonicalizer));
         }
 
-        appendPhase(new RemoveValueProxyPhase());
-
         if (NodeCounterPhase.Options.NodeCounters.getValue(options)) {
             appendPhase(new NodeCounterPhase(NodeCounterPhase.Stage.LATE));
         }
@@ -127,6 +116,7 @@
         appendPhase(new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER));
     }
 
+    @Override
     public LoopPolicies createLoopPolicies() {
         return new DefaultLoopPolicies();
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/LowTier.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/LowTier.java	Mon Nov 04 11:25:55 2019 +0000
@@ -24,7 +24,7 @@
 
 package org.graalvm.compiler.core.phases;
 
-import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
+import static org.graalvm.compiler.core.common.GraalOptions.LateMembars;
 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Required;
 
 import org.graalvm.compiler.core.common.GraalOptions;
@@ -33,11 +33,11 @@
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.common.ExpandLogicPhase;
 import org.graalvm.compiler.phases.common.FixReadsPhase;
+import org.graalvm.compiler.phases.common.InsertMembarsPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.common.ProfileCompiledMethodsPhase;
 import org.graalvm.compiler.phases.common.PropagateDeoptimizeProbabilityPhase;
@@ -46,7 +46,7 @@
 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
 import org.graalvm.compiler.phases.tiers.LowTierContext;
 
-public class LowTier extends PhaseSuite<LowTierContext> {
+public class LowTier extends BaseTier<LowTierContext> {
 
     static class Options {
 
@@ -58,13 +58,8 @@
     }
 
     public LowTier(OptionValues options) {
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
-        CanonicalizerPhase canonicalizerWithoutGVN = new CanonicalizerPhase();
-        canonicalizerWithoutGVN.disableGVN();
-        if (ImmutableCode.getValue(options)) {
-            canonicalizer.disableReadCanonicalization();
-            canonicalizerWithoutGVN.disableReadCanonicalization();
-        }
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase(options);
+        CanonicalizerPhase canonicalizerWithoutGVN = canonicalizer.copyWithoutGVN();
 
         if (Options.ProfileCompiledMethods.getValue(options)) {
             appendPhase(new ProfileCompiledMethodsPhase());
@@ -85,6 +80,9 @@
 
         appendPhase(new PropagateDeoptimizeProbabilityPhase());
 
+        if (LateMembars.getValue(options)) {
+            appendPhase(new InsertMembarsPhase());
+        }
         appendPhase(new SchedulePhase(SchedulePhase.SchedulingStrategy.LATEST_OUT_OF_LOOPS));
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/MidTier.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/MidTier.java	Mon Nov 04 11:25:55 2019 +0000
@@ -25,10 +25,8 @@
 package org.graalvm.compiler.core.phases;
 
 import static org.graalvm.compiler.core.common.GraalOptions.ConditionalElimination;
-import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
 import static org.graalvm.compiler.core.common.GraalOptions.OptDeoptimizationGrouping;
 import static org.graalvm.compiler.core.common.GraalOptions.OptFloatingReads;
-import static org.graalvm.compiler.core.common.GraalOptions.OptLoopTransform;
 import static org.graalvm.compiler.core.common.GraalOptions.PartialUnroll;
 import static org.graalvm.compiler.core.common.GraalOptions.ReassociateInvariants;
 import static org.graalvm.compiler.core.common.GraalOptions.VerifyHeapAtReturn;
@@ -43,7 +41,6 @@
 import org.graalvm.compiler.loop.phases.ReassociateInvariantPhase;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.PhaseSuite;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeoptimizationGroupingPhase;
 import org.graalvm.compiler.phases.common.FloatingReadPhase;
@@ -56,17 +53,15 @@
 import org.graalvm.compiler.phases.common.LoopSafepointInsertionPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.common.OptimizeDivPhase;
+import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
 import org.graalvm.compiler.phases.common.VerifyHeapAtReturnPhase;
 import org.graalvm.compiler.phases.common.WriteBarrierAdditionPhase;
 import org.graalvm.compiler.phases.tiers.MidTierContext;
 
-public class MidTier extends PhaseSuite<MidTierContext> {
+public class MidTier extends BaseTier<MidTierContext> {
 
     public MidTier(OptionValues options) {
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
-        if (ImmutableCode.getValue(options)) {
-            canonicalizer.disableReadCanonicalization();
-        }
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase(options);
 
         appendPhase(new LockEliminationPhase());
 
@@ -80,8 +75,6 @@
 
         appendPhase(new LoopSafepointEliminationPhase());
 
-        appendPhase(new LoopSafepointInsertionPhase());
-
         appendPhase(new GuardLoweringPhase());
 
         if (MitigateSpeculativeExecutionAttacks.getValue(options) == GuardTargets || MitigateSpeculativeExecutionAttacks.getValue(options) == NonDeoptGuardTargets) {
@@ -92,18 +85,21 @@
             appendPhase(new VerifyHeapAtReturnPhase());
         }
 
+        appendPhase(new IncrementalCanonicalizerPhase<>(canonicalizer, new RemoveValueProxyPhase()));
+
+        appendPhase(new LoopSafepointInsertionPhase());
+
         appendPhase(new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.MID_TIER));
 
         appendPhase(new OptimizeDivPhase());
 
         appendPhase(new FrameStateAssignmentPhase());
 
-        LoopPolicies loopPolicies = createLoopPolicies();
-        if (OptLoopTransform.getValue(options)) {
-            if (PartialUnroll.getValue(options)) {
-                appendPhase(new LoopPartialUnrollPhase(loopPolicies, canonicalizer));
-            }
+        if (PartialUnroll.getValue(options)) {
+            LoopPolicies loopPolicies = createLoopPolicies();
+            appendPhase(new LoopPartialUnrollPhase(loopPolicies, canonicalizer));
         }
+
         if (ReassociateInvariants.getValue(options)) {
             appendPhase(new ReassociateInvariantPhase());
         }
@@ -117,6 +113,7 @@
         appendPhase(new WriteBarrierAdditionPhase());
     }
 
+    @Override
     public LoopPolicies createLoopPolicies() {
         return new DefaultLoopPolicies();
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotBackendFactory.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotBackendFactory.java	Mon Nov 04 11:25:55 2019 +0000
@@ -100,6 +100,7 @@
     public HotSpotBackend createBackend(HotSpotGraalRuntimeProvider graalRuntime, CompilerConfiguration compilerConfiguration, HotSpotJVMCIRuntime jvmciRuntime, HotSpotBackend host) {
         assert host == null;
 
+        OptionValues options = graalRuntime.getOptions();
         JVMCIBackend jvmci = jvmciRuntime.getHostJVMCIBackend();
         GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
         HotSpotProviders providers;
@@ -156,7 +157,7 @@
             }
             try (InitTimer rt = timer("create GraphBuilderPhase plugins")) {
                 plugins = createGraphBuilderPlugins(graalRuntime, compilerConfiguration, config, constantReflection, foreignCalls, metaAccess, snippetReflection, replacements, wordTypes,
-                                graalRuntime.getOptions());
+                                graalRuntime.getOptions(), target);
                 replacements.setGraphBuilderPlugins(plugins);
             }
             try (InitTimer rt = timer("create Suites provider")) {
@@ -165,6 +166,7 @@
             providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, constantFieldProvider, foreignCalls, lowerer, replacements, suites, registers,
                             snippetReflection, wordTypes, plugins, gc);
             replacements.setProviders(providers);
+            replacements.maybeInitializeEncoder(options);
         }
         try (InitTimer rt = timer("instantiate backend")) {
             return createBackend(config, graalRuntime, providers);
@@ -180,7 +182,8 @@
                     HotSpotSnippetReflectionProvider snippetReflection,
                     HotSpotReplacementsImpl replacements,
                     HotSpotWordTypes wordTypes,
-                    OptionValues options) {
+                    OptionValues options,
+                    TargetDescription target) {
         Plugins plugins = HotSpotGraphBuilderPlugins.create(graalRuntime,
                         compilerConfiguration,
                         config,
@@ -190,8 +193,9 @@
                         snippetReflection,
                         foreignCalls,
                         replacements,
-                        options);
-        AArch64GraphBuilderPlugins.register(plugins, replacements.getDefaultReplacementBytecodeProvider(), false,
+                        options,
+                        target);
+        AArch64GraphBuilderPlugins.register(plugins, replacements, false, //
                         /* registerMathPlugins */true, /* emitJDK9StringSubstitutions */true, config.useFMAIntrinsics);
         return plugins;
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotBackendFactory.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotBackendFactory.java	Mon Nov 04 11:25:55 2019 +0000
@@ -158,6 +158,7 @@
             providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, constantFieldProvider, foreignCalls, lowerer, replacements, suites, registers,
                             snippetReflection, wordTypes, plugins, gc);
             replacements.setProviders(providers);
+            replacements.maybeInitializeEncoder(options);
         }
         try (InitTimer rt = timer("instantiate backend")) {
             return createBackend(config, graalRuntime, providers);
@@ -185,8 +186,9 @@
                         snippetReflection,
                         foreignCalls,
                         replacements,
-                        options);
-        AMD64GraphBuilderPlugins.register(plugins, replacements.getDefaultReplacementBytecodeProvider(), (AMD64) target.arch, false, JavaVersionUtil.JAVA_SPEC >= 9, config.useFMAIntrinsics);
+                        options,
+                        target);
+        AMD64GraphBuilderPlugins.register(plugins, replacements, (AMD64) target.arch, false, JavaVersionUtil.JAVA_SPEC >= 9, config.useFMAIntrinsics);
         return plugins;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotBackendFactory.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotBackendFactory.java	Mon Nov 04 11:25:55 2019 +0000
@@ -87,6 +87,7 @@
     public HotSpotBackend createBackend(HotSpotGraalRuntimeProvider runtime, CompilerConfiguration compilerConfiguration, HotSpotJVMCIRuntime jvmciRuntime, HotSpotBackend host) {
         assert host == null;
 
+        OptionValues options = runtime.getOptions();
         GraalHotSpotVMConfig config = runtime.getVMConfig();
         JVMCIBackend jvmci = jvmciRuntime.getHostJVMCIBackend();
         HotSpotRegistersProvider registers = createRegisters();
@@ -106,12 +107,13 @@
         BytecodeProvider bytecodeProvider = createBytecodeProvider(metaAccess, snippetReflection);
         HotSpotReplacementsImpl replacements = createReplacements(target, p, snippetReflection, bytecodeProvider);
         Plugins plugins = createGraphBuilderPlugins(runtime, compilerConfiguration, config, metaAccess, constantReflection, foreignCalls, snippetReflection, replacements, wordTypes,
-                        runtime.getOptions());
+                        runtime.getOptions(), target);
         replacements.setGraphBuilderPlugins(plugins);
         HotSpotSuitesProvider suites = createSuites(config, runtime, compilerConfiguration, plugins, replacements);
         HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, constantFieldProvider, foreignCalls, lowerer, replacements, suites, registers,
                         snippetReflection, wordTypes, plugins, gc);
         replacements.setProviders(providers);
+        replacements.maybeInitializeEncoder(options);
 
         return createBackend(config, runtime, providers);
     }
@@ -125,7 +127,8 @@
                     HotSpotSnippetReflectionProvider snippetReflection,
                     HotSpotReplacementsImpl replacements,
                     HotSpotWordTypes wordTypes,
-                    OptionValues options) {
+                    OptionValues options,
+                    TargetDescription target) {
         Plugins plugins = HotSpotGraphBuilderPlugins.create(
                         graalRuntime,
                         compilerConfiguration,
@@ -136,8 +139,9 @@
                         snippetReflection,
                         foreignCalls,
                         replacements,
-                        options);
-        SPARCGraphBuilderPlugins.register(plugins, replacements.getDefaultReplacementBytecodeProvider(), false);
+                        options,
+                        target);
+        SPARCGraphBuilderPlugins.register(plugins, replacements, false);
         return plugins;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java	Mon Nov 04 11:25:55 2019 +0000
@@ -24,9 +24,6 @@
 
 package org.graalvm.compiler.hotspot.test;
 
-import static org.graalvm.compiler.hotspot.meta.HotSpotGraphBuilderPlugins.aesDecryptName;
-import static org.graalvm.compiler.hotspot.meta.HotSpotGraphBuilderPlugins.aesEncryptName;
-
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -368,10 +365,6 @@
                 add(ignore,
                                 "com/sun/crypto/provider/GHASH.processBlocks([BII[J[J)V");
             }
-            if (!(config.useSHA1Intrinsics() || config.useSHA256Intrinsics() || config.useSHA512Intrinsics())) {
-                add(ignore,
-                                "sun/security/provider/DigestBase.implCompressMultiBlock0([BII)I");
-            }
             if (!config.useFMAIntrinsics) {
                 add(ignore,
                                 "java/lang/Math.fma(DDD)D",
@@ -516,15 +509,16 @@
                             "java/util/zip/CRC32C.updateDirectByteBuffer(IJII)I");
         }
 
-        boolean implNames = HotSpotGraphBuilderPlugins.cbcUsesImplNames(config);
-        String cbcEncryptName = implNames ? "implEncrypt" : "encrypt";
-        String cbcDecryptName = implNames ? "implDecrypt" : "decrypt";
+        String cbcEncryptName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(config, "com/sun/crypto/provider/CipherBlockChaining", "implEncrypt", "encrypt");
+        String cbcDecryptName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(config, "com/sun/crypto/provider/CipherBlockChaining", "implDecrypt", "decrypt");
+        String aesEncryptName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(config, "com/sun/crypto/provider/AESCrypt", "implEncryptBlock", "encryptBlock");
+        String aesDecryptName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(config, "com/sun/crypto/provider/AESCrypt", "implDecryptBlock", "decryptBlock");
 
         // AES intrinsics
         if (!config.useAESIntrinsics) {
             add(ignore,
+                            "com/sun/crypto/provider/AESCrypt." + aesEncryptName + "([BI[BI)V",
                             "com/sun/crypto/provider/AESCrypt." + aesDecryptName + "([BI[BI)V",
-                            "com/sun/crypto/provider/AESCrypt." + aesEncryptName + "([BI[BI)V",
                             "com/sun/crypto/provider/CipherBlockChaining." + cbcDecryptName + "([BII[BI)I",
                             "com/sun/crypto/provider/CipherBlockChaining." + cbcEncryptName + "([BII[BI)I");
         }
@@ -549,28 +543,21 @@
         if (!config.useSquareToLenIntrinsic()) {
             add(ignore, "java/math/BigInteger.implSquareToLen([II[II)[I");
         }
-
+        // DigestBase intrinsics
+        if (HotSpotGraphBuilderPlugins.isIntrinsicName(config, "sun/security/provider/DigestBase", "implCompressMultiBlock0") &&
+                        !(config.useSHA1Intrinsics() || config.useSHA256Intrinsics() || config.useSHA512Intrinsics())) {
+            add(ignore, "sun/security/provider/DigestBase.implCompressMultiBlock0([BII)I");
+        }
         // SHA intrinsics
+        String shaCompressName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(config, "sun/security/provider/SHA", "implCompress0", "implCompress");
         if (!config.useSHA1Intrinsics()) {
-            if (isJDK9OrHigher()) {
-                add(ignore, "sun/security/provider/SHA.implCompress0([BI)V");
-            } else {
-                add(ignore, "sun/security/provider/SHA.implCompress([BI)V");
-            }
+            add(ignore, "sun/security/provider/SHA." + shaCompressName + "([BI)V");
         }
         if (!config.useSHA256Intrinsics()) {
-            if (isJDK9OrHigher()) {
-                add(ignore, "sun/security/provider/SHA2.implCompress0([BI)V");
-            } else {
-                add(ignore, "sun/security/provider/SHA2.implCompress([BI)V");
-            }
+            add(ignore, "sun/security/provider/SHA2." + shaCompressName + "([BI)V");
         }
         if (!config.useSHA512Intrinsics()) {
-            if (isJDK9OrHigher()) {
-                add(ignore, "sun/security/provider/SHA5.implCompress0([BI)V");
-            } else {
-                add(ignore, "sun/security/provider/SHA5.implCompress([BI)V");
-            }
+            add(ignore, "sun/security/provider/SHA5." + shaCompressName + "([BI)V");
         }
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java	Mon Nov 04 11:25:55 2019 +0000
@@ -101,10 +101,10 @@
 import org.graalvm.compiler.options.OptionsParser;
 import org.graalvm.compiler.serviceprovider.GraalUnsafeAccess;
 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
-import org.graalvm.compiler.test.ModuleSupport;
+import org.graalvm.compiler.api.test.ModuleSupport;
 import jdk.internal.vm.compiler.libgraal.LibGraal;
 import jdk.internal.vm.compiler.libgraal.LibGraalScope;
-import jdk.internal.vm.compiler.libgraal.OptionsEncoder;
+import org.graalvm.util.OptionsEncoder;
 
 import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
 import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ConstantPoolSubstitutionsTests.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ConstantPoolSubstitutionsTests.java	Mon Nov 04 11:25:55 2019 +0000
@@ -34,7 +34,7 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
-import org.graalvm.compiler.test.ModuleSupport;
+import org.graalvm.compiler.api.test.ModuleSupport;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.objectweb.asm.ClassWriter;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/DeferredBarrierAdditionTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/DeferredBarrierAdditionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -38,7 +38,6 @@
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.common.WriteBarrierAdditionPhase;
@@ -83,12 +82,12 @@
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO, debug);
             HighTierContext highContext = getDefaultHighTierContext();
             MidTierContext midContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
-            new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase()).apply(graph, highContext);
-            new CanonicalizerPhase().apply(graph, highContext);
-            new PartialEscapePhase(false, new CanonicalizerPhase(), debug.getOptions()).apply(graph, highContext);
-            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext);
+            new InliningPhase(new InlineEverythingPolicy(), createCanonicalizerPhase()).apply(graph, highContext);
+            this.createCanonicalizerPhase().apply(graph, highContext);
+            new PartialEscapePhase(false, createCanonicalizerPhase(), debug.getOptions()).apply(graph, highContext);
+            new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext);
             new GuardLoweringPhase().apply(graph, midContext);
-            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
+            new LoweringPhase(createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
             new WriteBarrierAdditionPhase().apply(graph, midContext);
             debug.dump(DebugContext.BASIC_LEVEL, graph, "After Write Barrier Addition");
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotCryptoSubstitutionTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotCryptoSubstitutionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -80,7 +80,9 @@
 
     @Test
     public void testAESCryptIntrinsics() throws Exception {
-        if (compileAndInstall("com.sun.crypto.provider.AESCrypt", HotSpotGraphBuilderPlugins.aesEncryptName, HotSpotGraphBuilderPlugins.aesDecryptName)) {
+        String aesEncryptName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(runtime().getVMConfig(), "com/sun/crypto/provider/AESCrypt", "implEncryptBlock", "encryptBlock");
+        String aesDecryptName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(runtime().getVMConfig(), "com/sun/crypto/provider/AESCrypt", "implDecryptBlock", "decryptBlock");
+        if (compileAndInstall("com.sun.crypto.provider.AESCrypt", aesEncryptName, aesDecryptName)) {
             ByteArrayOutputStream actual = new ByteArrayOutputStream();
             actual.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding"));
             actual.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding"));
@@ -90,9 +92,8 @@
 
     @Test
     public void testCipherBlockChainingIntrinsics() throws Exception {
-        boolean implNames = HotSpotGraphBuilderPlugins.cbcUsesImplNames(runtime().getVMConfig());
-        String cbcEncryptName = implNames ? "implEncrypt" : "encrypt";
-        String cbcDecryptName = implNames ? "implDecrypt" : "decrypt";
+        String cbcEncryptName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(runtime().getVMConfig(), "com/sun/crypto/provider/CipherBlockChaining", "implEncrypt", "encrypt");
+        String cbcDecryptName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(runtime().getVMConfig(), "com/sun/crypto/provider/CipherBlockChaining", "implDecrypt", "decrypt");
         if (compileAndInstall("com.sun.crypto.provider.CipherBlockChaining", cbcEncryptName, cbcDecryptName)) {
             ByteArrayOutputStream actual = new ByteArrayOutputStream();
             actual.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding"));
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotInvokeDynamicPluginTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotInvokeDynamicPluginTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -93,7 +93,7 @@
         StructuredGraph graph = parseEager(name, AllowAssumptions.NO, new OptionValues(getInitialOptions(), GraalOptions.GeneratePIC, true));
         MidTierContext midTierContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
 
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         Assert.assertEquals(expectedResolves, graph.getNodes().filter(ResolveDynamicConstantNode.class).count());
         Assert.assertEquals(0, graph.getNodes().filter(ResolveDynamicStubCall.class).count());
         CoreProviders context = getProviders();
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotMethodSubstitutionTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotMethodSubstitutionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -28,12 +28,13 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 
-import org.graalvm.compiler.nodes.IfNode;
-import org.junit.Test;
-
 import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.api.replacements.MethodSubstitution;
+import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
+import org.graalvm.compiler.hotspot.HotSpotBackend;
+import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.replacements.test.MethodSubstitutionTest;
+import org.junit.Test;
 
 /**
  * Tests HotSpot specific {@link MethodSubstitution}s.
@@ -133,13 +134,18 @@
 
     @Test
     public void testThreadSubstitutions() {
+        GraalHotSpotVMConfig config = ((HotSpotBackend) getBackend()).getRuntime().getVMConfig();
         testGraph("currentThread");
-        assertInGraph(testGraph("threadIsInterrupted", "isInterrupted", true), IfNode.class);
-        assertInGraph(testGraph("threadInterrupted", "isInterrupted", true), IfNode.class);
+        if (config.osThreadInterruptedOffset != Integer.MAX_VALUE) {
+            assertInGraph(testGraph("threadIsInterrupted", "isInterrupted", true), IfNode.class);
+            assertInGraph(testGraph("threadInterrupted", "isInterrupted", true), IfNode.class);
+        }
 
         Thread currentThread = Thread.currentThread();
         test("currentThread", currentThread);
-        test("threadIsInterrupted", currentThread);
+        if (config.osThreadInterruptedOffset != Integer.MAX_VALUE) {
+            test("threadIsInterrupted", currentThread);
+        }
     }
 
     @SuppressWarnings("all")
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ReplaceConstantNodesPhaseTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ReplaceConstantNodesPhaseTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -117,7 +117,7 @@
     private void test(String name, int expectedInits, int expectedResolves, int expectedLoads) {
         StructuredGraph graph = parseEager(name, AllowAssumptions.NO, new OptionValues(getInitialOptions(), GraalOptions.GeneratePIC, true));
         HighTierContext highTierContext = getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         new EliminateRedundantInitializationPhase().apply(graph, highTierContext);
         new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highTierContext);
         new LoadJavaMirrorWithKlassPhase(config).apply(graph, highTierContext);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/TestSHASubstitutions.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/TestSHASubstitutions.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -34,9 +34,7 @@
 import org.graalvm.compiler.api.test.Graal;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
-import org.graalvm.compiler.hotspot.replacements.SHA2Substitutions;
-import org.graalvm.compiler.hotspot.replacements.SHA5Substitutions;
-import org.graalvm.compiler.hotspot.replacements.SHASubstitutions;
+import org.graalvm.compiler.hotspot.meta.HotSpotGraphBuilderPlugins;
 import org.graalvm.compiler.runtime.RuntimeProvider;
 
 import jdk.vm.ci.code.InstalledCode;
@@ -74,7 +72,8 @@
     @Test
     public void testSha1() {
         if (getConfig().useSHA1Intrinsics()) {
-            testWithInstalledIntrinsic("sun.security.provider.SHA", SHASubstitutions.implCompressName, "testDigest", "SHA-1", getData());
+            String implCompressName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(getConfig(), "sun/security/provider/SHA", "implCompress0", "implCompress");
+            testWithInstalledIntrinsic("sun.security.provider.SHA", implCompressName, "testDigest", "SHA-1", getData());
         }
     }
 
@@ -107,14 +106,16 @@
     @Test
     public void testSha256() {
         if (getConfig().useSHA256Intrinsics()) {
-            testWithInstalledIntrinsic("sun.security.provider.SHA2", SHA2Substitutions.implCompressName, "testDigest", "SHA-256", getData());
+            String implCompressName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(getConfig(), "sun/security/provider/SHA", "implCompress0", "implCompress");
+            testWithInstalledIntrinsic("sun.security.provider.SHA2", implCompressName, "testDigest", "SHA-256", getData());
         }
     }
 
     @Test
     public void testSha512() {
         if (getConfig().useSHA512Intrinsics()) {
-            testWithInstalledIntrinsic("sun.security.provider.SHA5", SHA5Substitutions.implCompressName, "testDigest", "SHA-512", getData());
+            String implCompressName = HotSpotGraphBuilderPlugins.lookupIntrinsicName(getConfig(), "sun/security/provider/SHA", "implCompress0", "implCompress");
+            testWithInstalledIntrinsic("sun.security.provider.SHA5", implCompressName, "testDigest", "SHA-512", getData());
         }
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierAdditionTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/WriteBarrierAdditionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -44,7 +44,6 @@
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.common.WriteBarrierAdditionPhase;
@@ -263,11 +262,11 @@
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO, debug);
             HighTierContext highContext = getDefaultHighTierContext();
             MidTierContext midContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
-            new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase()).apply(graph, highContext);
-            new CanonicalizerPhase().apply(graph, highContext);
-            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext);
+            new InliningPhase(new InlineEverythingPolicy(), createCanonicalizerPhase()).apply(graph, highContext);
+            this.createCanonicalizerPhase().apply(graph, highContext);
+            new LoweringPhase(this.createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext);
             new GuardLoweringPhase().apply(graph, midContext);
-            new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
+            new LoweringPhase(this.createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
             new WriteBarrierAdditionPhase().apply(graph, midContext);
             debug.dump(DebugContext.BASIC_LEVEL, graph, "After Write Barrier Addition");
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java	Mon Nov 04 11:25:55 2019 +0000
@@ -315,7 +315,7 @@
     public final int javaThreadAnchorOffset = getFieldOffset("JavaThread::_anchor", Integer.class, "JavaFrameAnchor");
     public final int javaThreadShouldPostOnExceptionsFlagOffset = getFieldOffset("JavaThread::_should_post_on_exceptions_flag", Integer.class, "int", Integer.MIN_VALUE);
     public final int threadObjectOffset = getFieldOffset("JavaThread::_threadObj", Integer.class, "oop");
-    public final int osThreadOffset = getFieldOffset("JavaThread::_osthread", Integer.class, "OSThread*");
+    public final int osThreadOffset = getFieldOffset("JavaThread::_osthread", Integer.class, "OSThread*", Integer.MAX_VALUE);
     public final int threadIsMethodHandleReturnOffset = getFieldOffset("JavaThread::_is_method_handle_return", Integer.class, "int");
     public final int threadObjectResultOffset = getFieldOffset("JavaThread::_vm_result", Integer.class, "oop");
     public final int jvmciCountersThreadOffset = getFieldOffset("JavaThread::_jvmci_counters", Integer.class, "jlong*");
@@ -419,7 +419,7 @@
     public final int frameInterpreterFrameSenderSpOffset = getConstant("frame::interpreter_frame_sender_sp_offset", Integer.class, intRequiredOnAMD64);
     public final int frameInterpreterFrameLastSpOffset = getConstant("frame::interpreter_frame_last_sp_offset", Integer.class, intRequiredOnAMD64);
 
-    public final int osThreadInterruptedOffset = getFieldOffset("OSThread::_interrupted", Integer.class, "jint");
+    public final int osThreadInterruptedOffset = getFieldOffset("OSThread::_interrupted", Integer.class, "jint", Integer.MAX_VALUE);
 
     public final long markWordHashShift = getConstant(markWordField("hash_shift"), Long.class);
 
@@ -714,7 +714,6 @@
         return true;
     }
 
-    public final long threadIsInterruptedAddress = getAddress("JVMCIRuntime::thread_is_interrupted");
     public final long vmMessageAddress = getAddress("JVMCIRuntime::vm_message");
     public final long identityHashCodeAddress = getAddress("JVMCIRuntime::identity_hash_code");
     public final long exceptionHandlerForPcAddress = getAddress("JVMCIRuntime::exception_handler_for_pc");
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotReplacementsImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotReplacementsImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -72,26 +72,40 @@
                         replacements.getDefaultReplacementBytecodeProvider(), replacements.target);
     }
 
+    public void maybeInitializeEncoder(OptionValues options) {
+        if (IS_BUILDING_NATIVE_IMAGE || UseEncodedGraphs.getValue(options)) {
+            synchronized (HotSpotReplacementsImpl.class) {
+                if (snippetEncoder == null) {
+                    snippetEncoder = new SymbolicSnippetEncoder(this);
+                }
+            }
+        }
+    }
+
     @Override
     public Class<? extends GraphBuilderPlugin> getIntrinsifyingPlugin(ResolvedJavaMethod method) {
         return method.getAnnotation(HotSpotOperation.class) != null ? HotSpotWordOperationPlugin.class : super.getIntrinsifyingPlugin(method);
     }
 
     @Override
-    public void registerMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context, OptionValues options) {
-        if (!IS_IN_NATIVE_IMAGE) {
-            if (IS_BUILDING_NATIVE_IMAGE || UseEncodedGraphs.getValue(options)) {
-                synchronized (HotSpotReplacementsImpl.class) {
-                    if (snippetEncoder == null) {
-                        snippetEncoder = new SymbolicSnippetEncoder(this);
-                    }
-                    snippetEncoder.registerMethodSubstitution(plugin, original, context, options);
-                }
-            }
+    public void registerMethodSubstitution(MethodSubstitutionPlugin plugin) {
+        if (snippetEncoder != null) {
+            snippetEncoder.registerMethodSubstitution(plugin);
         }
     }
 
     @Override
+    public void registerConditionalPlugin(InvocationPlugin plugin) {
+        if (snippetEncoder != null) {
+            snippetEncoder.registerConditionalPlugin(plugin);
+        }
+    }
+
+    public void checkRegistered(MethodSubstitutionPlugin plugin) {
+        snippetEncoder.checkRegistered(plugin);
+    }
+
+    @Override
     public StructuredGraph getIntrinsicGraph(ResolvedJavaMethod method, CompilationIdentifier compilationId, DebugContext debug, Cancellable cancellable) {
         boolean useEncodedGraphs = UseEncodedGraphs.getValue(debug.getOptions());
         if (IS_IN_NATIVE_IMAGE || useEncodedGraphs) {
@@ -99,8 +113,9 @@
             InvocationPlugin plugin = replacements.getGraphBuilderPlugins().getInvocationPlugins().lookupInvocation(method);
             if (plugin instanceof MethodSubstitutionPlugin) {
                 MethodSubstitutionPlugin msp = (MethodSubstitutionPlugin) plugin;
-                if (useEncodedGraphs) {
-                    replacements.registerMethodSubstitution(msp, method, ROOT_COMPILATION, debug.getOptions());
+                if (!IS_IN_NATIVE_IMAGE && useEncodedGraphs) {
+                    replacements.maybeInitializeEncoder(debug.getOptions());
+                    replacements.registerMethodSubstitution(msp);
                 }
                 StructuredGraph methodSubstitution = replacements.getMethodSubstitution(msp, method, ROOT_COMPILATION, StructuredGraph.AllowAssumptions.YES, cancellable, debug.getOptions());
                 methodSubstitution.resetDebug(debug);
@@ -119,7 +134,8 @@
             if (plugin instanceof MethodSubstitutionPlugin && (!plugin.inlineOnly() || invokeBci >= 0)) {
                 MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin;
                 if (!IS_IN_NATIVE_IMAGE && useEncodedGraphs) {
-                    registerMethodSubstitution(msPlugin, targetMethod, INLINE_AFTER_PARSING, options);
+                    maybeInitializeEncoder(options);
+                    registerMethodSubstitution(msPlugin);
                 }
                 // This assumes the normal path creates the graph using
                 // GraphBuilderConfiguration.getSnippetDefault with omits exception edges
@@ -155,9 +171,6 @@
             assert registeredSnippets.add(method) : "Cannot register snippet twice: " + method.format("%H.%n(%p)");
             if (IS_BUILDING_NATIVE_IMAGE || UseEncodedGraphs.getValue(options)) {
                 synchronized (HotSpotReplacementsImpl.class) {
-                    if (snippetEncoder == null) {
-                        snippetEncoder = new SymbolicSnippetEncoder(this);
-                    }
                     snippetEncoder.registerSnippet(method, original, receiver, trackNodeSourcePosition, options);
                 }
             }
@@ -169,7 +182,10 @@
         snippetRegistrationClosed = true;
     }
 
-    private static SymbolicSnippetEncoder.EncodedSnippets getEncodedSnippets() {
+    private static SymbolicSnippetEncoder.EncodedSnippets getEncodedSnippets(OptionValues options) {
+        if (!IS_IN_NATIVE_IMAGE && snippetEncoder != null) {
+            snippetEncoder.encode(options);
+        }
         return encodedSnippets;
     }
 
@@ -210,20 +226,15 @@
 
     @SuppressWarnings("try")
     private StructuredGraph getEncodedSnippet(ResolvedJavaMethod method, Object[] args, StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options) {
-        boolean useEncodedGraphs = UseEncodedGraphs.getValue(options);
-        if (IS_IN_NATIVE_IMAGE || useEncodedGraphs) {
+        if (IS_IN_NATIVE_IMAGE || UseEncodedGraphs.getValue(options)) {
             synchronized (HotSpotReplacementsImpl.class) {
-                if (!IS_IN_NATIVE_IMAGE) {
-                    snippetEncoder.encode(options);
-                }
-
-                if (getEncodedSnippets() == null) {
+                if (getEncodedSnippets(options) == null) {
                     throw GraalError.shouldNotReachHere("encoded snippets not found");
                 }
                 // Snippets graphs can contain foreign object reference and
                 // outlive a single compilation.
                 try (CompilationContext scope = HotSpotGraalServices.enterGlobalCompilationContext()) {
-                    StructuredGraph graph = getEncodedSnippets().getEncodedSnippet(method, this, args, allowAssumptions, options);
+                    StructuredGraph graph = getEncodedSnippets(options).getEncodedSnippet(method, this, args, allowAssumptions, options);
                     if (graph == null) {
                         throw GraalError.shouldNotReachHere("snippet not found: " + method.format("%H.%n(%p)"));
                     }
@@ -239,16 +250,11 @@
     @Override
     public StructuredGraph getMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context,
                     StructuredGraph.AllowAssumptions allowAssumptions, Cancellable cancellable, OptionValues options) {
-        boolean useEncodedGraphs = UseEncodedGraphs.getValue(options);
-        if (IS_IN_NATIVE_IMAGE || useEncodedGraphs) {
-            if (!IS_IN_NATIVE_IMAGE) {
-                snippetEncoder.encode(options);
-            }
-
-            if (getEncodedSnippets() == null) {
+        if (IS_IN_NATIVE_IMAGE || UseEncodedGraphs.getValue(options)) {
+            if (getEncodedSnippets(options) == null) {
                 throw GraalError.shouldNotReachHere("encoded snippets not found");
             }
-            return getEncodedSnippets().getMethodSubstitutionGraph(plugin, original, this, context, allowAssumptions, cancellable, options);
+            return getEncodedSnippets(options).getMethodSubstitutionGraph(plugin, original, this, context, allowAssumptions, cancellable, options);
         }
         return null;
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SymbolicSnippetEncoder.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SymbolicSnippetEncoder.java	Mon Nov 04 11:25:55 2019 +0000
@@ -30,6 +30,7 @@
 import static org.graalvm.compiler.core.common.GraalOptions.UseEncodedGraphs;
 import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createIntrinsicInlineInfo;
 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
+import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -156,6 +157,12 @@
      */
     private Map<String, StructuredGraph> preparedSnippetGraphs = new HashMap<>();
 
+    private Set<MethodSubstitutionPlugin> knownPlugins = new HashSet<>();
+
+    private Set<InvocationPlugin> conditionalPlugins = new HashSet<>();
+
+    private int preparedPlugins = 0;
+
     /**
      * The invocation plugins which were delayed during graph preparation.
      */
@@ -239,7 +246,7 @@
      * for lookup.
      */
     private static String methodKey(ResolvedJavaMethod method) {
-        return method.format("%f %H.%n(%P)");
+        return method.format("%H.%n(%P)");
     }
 
     SymbolicSnippetEncoder(HotSpotReplacementsImpl replacements) {
@@ -255,15 +262,30 @@
         this.snippetReplacements.setGraphBuilderPlugins(copy);
     }
 
+    synchronized void registerMethodSubstitution(MethodSubstitutionPlugin plugin) {
+        knownPlugins.add(plugin);
+    }
+
+    void registerConditionalPlugin(InvocationPlugin plugin) {
+        conditionalPlugins.add(plugin);
+    }
+
+    synchronized void checkRegistered(MethodSubstitutionPlugin plugin) {
+        if (!knownPlugins.contains(plugin)) {
+            throw new GraalError("missing plugin should have been registered during construction");
+        }
+    }
+
     /**
      * Compiles the snippet and stores the graph.
      */
-    synchronized void registerMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context, OptionValues options) {
+    private synchronized void registerMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context, OptionValues options) {
         ResolvedJavaMethod method = plugin.getSubstitute(snippetReplacements.getProviders().getMetaAccess());
         assert method.getAnnotation(MethodSubstitution.class) != null : "MethodSubstitution must be annotated with @" + MethodSubstitution.class.getSimpleName();
-        StructuredGraph subst = buildGraph(method, original, null, true, false, context, options);
+        String originalMethodString = plugin.originalMethodAsString();
+        StructuredGraph subst = buildGraph(method, original, originalMethodString, null, true, false, context, options);
         snippetMethods.add(method);
-        originalMethods.put(methodKey(method), methodKey(original));
+        originalMethods.put(methodKey(method), originalMethodString);
         preparedSnippetGraphs.put(plugin.toString() + context, subst);
     }
 
@@ -370,7 +392,7 @@
         }
     }
 
-    private StructuredGraph buildGraph(ResolvedJavaMethod method, ResolvedJavaMethod original, Object receiver, boolean requireInlining, boolean trackNodeSourcePosition,
+    private StructuredGraph buildGraph(ResolvedJavaMethod method, ResolvedJavaMethod original, String originalMethodString, Object receiver, boolean requireInlining, boolean trackNodeSourcePosition,
                     IntrinsicContext.CompilationContext context, OptionValues options) {
         assert method.hasBytecodes() : "Snippet must not be abstract or native";
         Object[] args = null;
@@ -378,7 +400,13 @@
             args = new Object[method.getSignature().getParameterCount(true)];
             args[0] = receiver;
         }
-        try (DebugContext debug = openDebugContext("Snippet_", method, options)) {
+        // To get dumping out from this context during image building, it's necessary to pass the
+        // dumping options directly to the VM, otherwise they aren't available during initialization
+        // of the backend. Use this:
+        //
+        // -J-Dgraal.Dump=SymbolicSnippetEncoder_:2 -J-Dgraal.PrintGraph=File
+        // -J-Dgraal.DebugStubsAndSnippets=true
+        try (DebugContext debug = openDebugContext("SymbolicSnippetEncoder_", method, options)) {
             StructuredGraph graph = snippetReplacements.makeGraph(debug, snippetReplacements.getDefaultReplacementBytecodeProvider(), method, args, original, trackNodeSourcePosition, null, context);
 
             // Check if all methods which should be inlined are really inlined.
@@ -388,7 +416,7 @@
                     throw GraalError.shouldNotReachHere("method " + callee.format("%H.%n") + " not inlined in snippet " + method.getName() + " (maybe not final?)");
                 }
             }
-            assert verifySnippetEncodeDecode(method, original, trackNodeSourcePosition, graph);
+            assert verifySnippetEncodeDecode(debug, method, original, originalMethodString, trackNodeSourcePosition, graph);
             debug.dump(DebugContext.VERBOSE_LEVEL, graph, "After buildGraph");
             return graph;
         }
@@ -426,46 +454,45 @@
     }
 
     @SuppressWarnings("try")
-    private boolean verifySnippetEncodeDecode(ResolvedJavaMethod method, ResolvedJavaMethod original, boolean trackNodeSourcePosition, StructuredGraph graph) {
+    private boolean verifySnippetEncodeDecode(DebugContext debug, ResolvedJavaMethod method, ResolvedJavaMethod original, String originalMethodString, boolean trackNodeSourcePosition,
+                    StructuredGraph graph) {
         // Verify the encoding and decoding process
         EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, HotSpotJVMCIRuntime.runtime().getHostJVMCIBackend().getTarget().arch);
 
-        try (DebugContext debug = snippetReplacements.openDebugContext("VerifySnippetEncodeDecode_", method, graph.getOptions())) {
-            HotSpotProviders originalProvider = (HotSpotProviders) snippetReplacements.getProviders();
+        HotSpotProviders originalProvider = (HotSpotProviders) snippetReplacements.getProviders();
 
-            SnippetReflectionProvider snippetReflection = originalProvider.getSnippetReflection();
-            SymbolicSnippetEncoder.HotSpotSubstrateConstantReflectionProvider constantReflection = new SymbolicSnippetEncoder.HotSpotSubstrateConstantReflectionProvider(
-                            originalProvider.getConstantReflection());
-            HotSpotProviders newProviders = new HotSpotProviders(originalProvider.getMetaAccess(), originalProvider.getCodeCache(), constantReflection,
-                            originalProvider.getConstantFieldProvider(), originalProvider.getForeignCalls(), originalProvider.getLowerer(), null, originalProvider.getSuites(),
-                            originalProvider.getRegisters(), snippetReflection, originalProvider.getWordTypes(), originalProvider.getGraphBuilderPlugins(), originalProvider.getGC());
-            HotSpotSnippetReplacementsImpl filteringReplacements = new HotSpotSnippetReplacementsImpl(newProviders, snippetReflection,
-                            originalProvider.getReplacements().getDefaultReplacementBytecodeProvider(), originalProvider.getCodeCache().getTarget());
-            filteringReplacements.setGraphBuilderPlugins(originalProvider.getReplacements().getGraphBuilderPlugins());
-            try (DebugContext.Scope scaope = debug.scope("VerifySnippetEncodeDecode", graph)) {
-                for (int i = 0; i < encodedGraph.getNumObjects(); i++) {
-                    filterSnippetObject(encodedGraph.getObject(i));
-                }
-                StructuredGraph snippet = filteringReplacements.makeGraph(debug, filteringReplacements.getDefaultReplacementBytecodeProvider(), method, null, original,
-                                trackNodeSourcePosition, null);
-                SymbolicEncodedGraph symbolicGraph = new SymbolicEncodedGraph(encodedGraph, method.getDeclaringClass(), original != null ? methodKey(original) : null);
-                StructuredGraph decodedSnippet = decodeSnippetGraph(symbolicGraph, original != null ? original : method, originalReplacements, null,
-                                StructuredGraph.AllowAssumptions.ifNonNull(graph.getAssumptions()), graph.getOptions());
-                String snippetString = getCanonicalGraphString(snippet, true, false);
-                String decodedSnippetString = getCanonicalGraphString(decodedSnippet, true, false);
-                if (snippetString.equals(decodedSnippetString)) {
-                    debug.log("Snippet decode for %s produces exactly same graph", method);
-                    debug.dump(DebugContext.VERBOSE_LEVEL, decodedSnippet, "Decoded snippet graph for %s", method);
-                } else {
-                    debug.log("Snippet decode for %s produces different graph", method);
-                    debug.log("%s", compareGraphStrings(snippet, snippetString, decodedSnippet, decodedSnippetString));
-                    debug.dump(DebugContext.VERBOSE_LEVEL, snippet, "Snippet graph for %s", method);
-                    debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Encoded snippet graph for %s", method);
-                    debug.dump(DebugContext.VERBOSE_LEVEL, decodedSnippet, "Decoded snippet graph for %s", method);
-                }
-            } catch (Throwable t) {
-                throw debug.handle(t);
+        SnippetReflectionProvider snippetReflection = originalProvider.getSnippetReflection();
+        SymbolicSnippetEncoder.HotSpotSubstrateConstantReflectionProvider constantReflection = new SymbolicSnippetEncoder.HotSpotSubstrateConstantReflectionProvider(
+                        originalProvider.getConstantReflection());
+        HotSpotProviders newProviders = new HotSpotProviders(originalProvider.getMetaAccess(), originalProvider.getCodeCache(), constantReflection,
+                        originalProvider.getConstantFieldProvider(), originalProvider.getForeignCalls(), originalProvider.getLowerer(), null, originalProvider.getSuites(),
+                        originalProvider.getRegisters(), snippetReflection, originalProvider.getWordTypes(), originalProvider.getGraphBuilderPlugins(), originalProvider.getGC());
+        HotSpotSnippetReplacementsImpl filteringReplacements = new HotSpotSnippetReplacementsImpl(newProviders, snippetReflection,
+                        originalProvider.getReplacements().getDefaultReplacementBytecodeProvider(), originalProvider.getCodeCache().getTarget());
+        filteringReplacements.setGraphBuilderPlugins(originalProvider.getReplacements().getGraphBuilderPlugins());
+        try (DebugContext.Scope scaope = debug.scope("VerifySnippetEncodeDecode", graph)) {
+            for (int i = 0; i < encodedGraph.getNumObjects(); i++) {
+                filterSnippetObject(encodedGraph.getObject(i));
             }
+            StructuredGraph snippet = filteringReplacements.makeGraph(debug, filteringReplacements.getDefaultReplacementBytecodeProvider(), method, null, original,
+                            trackNodeSourcePosition, null);
+            SymbolicEncodedGraph symbolicGraph = new SymbolicEncodedGraph(encodedGraph, method.getDeclaringClass(), originalMethodString);
+            StructuredGraph decodedSnippet = decodeSnippetGraph(symbolicGraph, original != null ? original : method, originalReplacements, null,
+                            StructuredGraph.AllowAssumptions.ifNonNull(graph.getAssumptions()), graph.getOptions());
+            String snippetString = getCanonicalGraphString(snippet, true, false);
+            String decodedSnippetString = getCanonicalGraphString(decodedSnippet, true, false);
+            if (snippetString.equals(decodedSnippetString)) {
+                debug.log("Snippet decode for %s produces exactly same graph", method);
+                debug.dump(DebugContext.VERBOSE_LEVEL, decodedSnippet, "Decoded snippet graph for %s", method);
+            } else {
+                debug.log("Snippet decode for %s produces different graph", method);
+                debug.log("%s", compareGraphStrings(snippet, snippetString, decodedSnippet, decodedSnippetString));
+                debug.dump(DebugContext.VERBOSE_LEVEL, snippet, "Snippet graph for %s", method);
+                debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Encoded snippet graph for %s", method);
+                debug.dump(DebugContext.VERBOSE_LEVEL, decodedSnippet, "Decoded snippet graph for %s", method);
+            }
+        } catch (Throwable t) {
+            throw debug.handle(t);
         }
         return true;
     }
@@ -475,6 +502,17 @@
      */
     @SuppressWarnings("try")
     private synchronized EncodedSnippets maybeEncodeSnippets(OptionValues options) {
+        Set<MethodSubstitutionPlugin> plugins = this.knownPlugins;
+        if (preparedPlugins != plugins.size()) {
+            for (MethodSubstitutionPlugin plugin : plugins) {
+                ResolvedJavaMethod original = plugin.getOriginalMethod(originalReplacements.getProviders().getMetaAccess());
+                registerMethodSubstitution(plugin, original, INLINE_AFTER_PARSING, options);
+                if (!original.isNative()) {
+                    registerMethodSubstitution(plugin, original, ROOT_COMPILATION, options);
+                }
+            }
+            preparedPlugins = plugins.size();
+        }
         Map<String, StructuredGraph> graphs = this.preparedSnippetGraphs;
         if (encodedGraphs != graphs.size()) {
             DebugContext debug = openDebugContext("SnippetEncoder", null, options);
@@ -499,7 +537,7 @@
                 if (original != null) {
                     originalMethods.put(key, methodKey(original));
                 }
-                StructuredGraph snippet = buildGraph(method, original, receiver, true, trackNodeSourcePosition, INLINE_AFTER_PARSING, options);
+                StructuredGraph snippet = buildGraph(method, original, null, receiver, true, trackNodeSourcePosition, INLINE_AFTER_PARSING, options);
                 snippetMethods.add(method);
                 preparedSnippetGraphs.put(key, snippet);
             }
@@ -1001,7 +1039,7 @@
      * This horror show of classes exists solely get {@link HotSpotSnippetBytecodeParser} to be used
      * as the parser for these snippets.
      */
-    static class HotSpotSnippetReplacementsImpl extends HotSpotReplacementsImpl {
+    class HotSpotSnippetReplacementsImpl extends HotSpotReplacementsImpl {
         HotSpotSnippetReplacementsImpl(HotSpotReplacementsImpl replacements, Providers providers) {
             super(replacements, providers);
         }
@@ -1016,7 +1054,7 @@
         }
     }
 
-    static class SnippetGraphMaker extends ReplacementsImpl.GraphMaker {
+    class SnippetGraphMaker extends ReplacementsImpl.GraphMaker {
         SnippetGraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod) {
             super(replacements, substitute, substitutedMethod);
         }
@@ -1028,7 +1066,7 @@
         }
     }
 
-    static class HotSpotSnippetGraphBuilderPhase extends GraphBuilderPhase.Instance {
+    class HotSpotSnippetGraphBuilderPhase extends GraphBuilderPhase.Instance {
         HotSpotSnippetGraphBuilderPhase(Providers theProviders, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) {
             super(theProviders, graphBuilderConfig, optimisticOpts, initialIntrinsicContext);
         }
@@ -1039,7 +1077,7 @@
         }
     }
 
-    static class HotSpotSnippetBytecodeParser extends BytecodeParser {
+    class HotSpotSnippetBytecodeParser extends BytecodeParser {
         HotSpotSnippetBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI,
                         IntrinsicContext intrinsicContext) {
             super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext);
@@ -1066,6 +1104,13 @@
                 // Always defer Fold until decode time but NodeIntrinsics may fold if they are able.
                 return false;
             }
+            InvocationPlugin plugin = graphBuilderConfig.getPlugins().getInvocationPlugins().lookupInvocation(targetMethod);
+            if (conditionalPlugins.contains(plugin)) {
+                // Because supporting arbitrary plugins in the context of encoded graphs is complex
+                // we disallow it. This limitation can be worked around through the use of method
+                // substitutions.
+                throw new GraalError("conditional plugins are unsupported in snippets and method substitutions: " + targetMethod + " " + plugin);
+            }
             return super.tryInvocationPlugin(invokeKind, args, targetMethod, resultType);
         }
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java	Mon Nov 04 11:25:55 2019 +0000
@@ -646,7 +646,7 @@
 
                 // write the displaced mark to the correct stack slot
                 AddressNode addressDisplacedMark = createOffsetAddress(graph, beginLockScope, runtime.getVMConfig().basicLockDisplacedHeaderOffset);
-                WriteNode writeStackSlot = graph.add(new WriteNode(addressDisplacedMark, DISPLACED_MARK_WORD_LOCATION, loadDisplacedHeader, BarrierType.NONE));
+                WriteNode writeStackSlot = graph.add(new WriteNode(addressDisplacedMark, DISPLACED_MARK_WORD_LOCATION, loadDisplacedHeader, BarrierType.NONE, false));
                 graph.addBeforeFixed(migrationEnd, writeStackSlot);
 
                 // load the lock object from the osr buffer
@@ -776,7 +776,7 @@
         }
 
         AddressNode address = createOffsetAddress(graph, object, runtime.getVMConfig().hubOffset);
-        return graph.add(new WriteNode(address, HUB_WRITE_LOCATION, writeValue, BarrierType.NONE));
+        return graph.add(new WriteNode(address, HUB_WRITE_LOCATION, writeValue, BarrierType.NONE, false));
     }
 
     @Override
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java	Mon Nov 04 11:25:55 2019 +0000
@@ -39,7 +39,6 @@
 import java.util.zip.CRC32;
 
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
-import org.graalvm.compiler.bytecode.BytecodeProvider;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
 import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
@@ -92,6 +91,7 @@
 import org.graalvm.compiler.nodes.memory.ReadNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
+import org.graalvm.compiler.nodes.spi.Replacements;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.tiers.CompilerConfiguration;
@@ -108,6 +108,7 @@
 import jdk.internal.vm.compiler.word.LocationIdentity;
 
 import jdk.vm.ci.code.CodeUtil;
+import jdk.vm.ci.code.TargetDescription;
 import jdk.vm.ci.hotspot.VMIntrinsicMethod;
 import jdk.vm.ci.meta.ConstantReflectionProvider;
 import jdk.vm.ci.meta.DeoptimizationAction;
@@ -128,6 +129,7 @@
      * @param snippetReflection
      * @param foreignCalls
      * @param options
+     * @param target
      */
     public static Plugins create(HotSpotGraalRuntimeProvider graalRuntime,
                     CompilerConfiguration compilerConfiguration,
@@ -138,11 +140,11 @@
                     SnippetReflectionProvider snippetReflection,
                     ForeignCallsProvider foreignCalls,
                     ReplacementsImpl replacements,
-                    OptionValues options) {
+                    OptionValues options, TargetDescription target) {
         InvocationPlugins invocationPlugins = new HotSpotInvocationPlugins(graalRuntime, config, compilerConfiguration);
 
         Plugins plugins = new Plugins(invocationPlugins);
-        NodeIntrinsificationProvider nodeIntrinsificationProvider = new NodeIntrinsificationProvider(metaAccess, snippetReflection, foreignCalls, wordTypes);
+        NodeIntrinsificationProvider nodeIntrinsificationProvider = new NodeIntrinsificationProvider(metaAccess, snippetReflection, foreignCalls, wordTypes, target);
         HotSpotWordOperationPlugin wordOperationPlugin = new HotSpotWordOperationPlugin(snippetReflection, wordTypes);
         HotSpotNodePlugin nodePlugin = new HotSpotNodePlugin(wordOperationPlugin, config, wordTypes);
 
@@ -171,29 +173,28 @@
 
             @Override
             public void run() {
-                BytecodeProvider replacementBytecodeProvider = replacements.getDefaultReplacementBytecodeProvider();
-                registerObjectPlugins(invocationPlugins, options, config, replacementBytecodeProvider);
-                registerClassPlugins(plugins, config, replacementBytecodeProvider);
+                registerObjectPlugins(invocationPlugins, options, config, replacements);
+                registerClassPlugins(plugins, config, replacements);
                 registerSystemPlugins(invocationPlugins, foreignCalls);
-                registerThreadPlugins(invocationPlugins, metaAccess, wordTypes, config, replacementBytecodeProvider);
+                registerThreadPlugins(invocationPlugins, metaAccess, wordTypes, config, replacements);
                 if (!GeneratePIC.getValue(options)) {
                     registerCallSitePlugins(invocationPlugins);
                 }
-                registerReflectionPlugins(invocationPlugins, replacementBytecodeProvider);
-                registerConstantPoolPlugins(invocationPlugins, wordTypes, config, replacementBytecodeProvider);
-                registerAESPlugins(invocationPlugins, config, replacementBytecodeProvider);
-                registerCRC32Plugins(invocationPlugins, config, replacementBytecodeProvider);
-                registerCRC32CPlugins(invocationPlugins, config, replacementBytecodeProvider);
-                registerBigIntegerPlugins(invocationPlugins, config, replacementBytecodeProvider);
-                registerSHAPlugins(invocationPlugins, config, replacementBytecodeProvider);
+                registerReflectionPlugins(invocationPlugins, replacements);
+                registerConstantPoolPlugins(invocationPlugins, wordTypes, config, replacements);
+                registerAESPlugins(invocationPlugins, config, replacements);
+                registerCRC32Plugins(invocationPlugins, config, replacements);
+                registerCRC32CPlugins(invocationPlugins, config, replacements);
+                registerBigIntegerPlugins(invocationPlugins, config, replacements);
+                registerSHAPlugins(invocationPlugins, config, replacements);
                 registerGHASHPlugins(invocationPlugins, config, metaAccess, foreignCalls);
-                registerCounterModePlugins(invocationPlugins, config, replacementBytecodeProvider);
+                registerCounterModePlugins(invocationPlugins, config, replacements);
                 registerBase64Plugins(invocationPlugins, config, metaAccess, foreignCalls);
-                registerUnsafePlugins(invocationPlugins, config, replacementBytecodeProvider);
-                StandardGraphBuilderPlugins.registerInvocationPlugins(metaAccess, snippetReflection, invocationPlugins, replacementBytecodeProvider, true, false);
-                registerArrayPlugins(invocationPlugins, replacementBytecodeProvider);
-                registerStringPlugins(invocationPlugins, replacementBytecodeProvider);
-                registerArraysSupportPlugins(invocationPlugins, config, replacementBytecodeProvider);
+                registerUnsafePlugins(invocationPlugins, config, replacements);
+                StandardGraphBuilderPlugins.registerInvocationPlugins(metaAccess, snippetReflection, invocationPlugins, replacements, true, false);
+                registerArrayPlugins(invocationPlugins, replacements);
+                registerStringPlugins(invocationPlugins, replacements);
+                registerArraysSupportPlugins(invocationPlugins, config, replacements);
 
                 for (NodeIntrinsicPluginFactory factory : GraalServices.load(NodeIntrinsicPluginFactory.class)) {
                     factory.registerPlugins(invocationPlugins, nodeIntrinsificationProvider);
@@ -203,8 +204,8 @@
         return plugins;
     }
 
-    private static void registerObjectPlugins(InvocationPlugins plugins, OptionValues options, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, Object.class, bytecodeProvider);
+    private static void registerObjectPlugins(InvocationPlugins plugins, OptionValues options, GraalHotSpotVMConfig config, Replacements replacements) {
+        Registration r = new Registration(plugins, Object.class, replacements);
         if (!GeneratePIC.getValue(options)) {
             // FIXME: clone() requires speculation and requires a fix in here (to check that
             // b.getAssumptions() != null), and in ReplacementImpl.getSubstitution() where there is
@@ -235,8 +236,8 @@
         }
     }
 
-    private static void registerClassPlugins(Plugins plugins, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins.getInvocationPlugins(), Class.class, bytecodeProvider);
+    private static void registerClassPlugins(Plugins plugins, GraalHotSpotVMConfig config, Replacements replacements) {
+        Registration r = new Registration(plugins.getInvocationPlugins(), Class.class, replacements);
 
         r.registerMethodSubstitution(HotSpotClassSubstitutions.class, "getModifiers", Receiver.class);
         r.registerMethodSubstitution(HotSpotClassSubstitutions.class, "isInterface", Receiver.class);
@@ -273,8 +274,8 @@
         plugins.register(plugin, VolatileCallSite.class, "getTarget", Receiver.class);
     }
 
-    private static void registerReflectionPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, reflectionClass, bytecodeProvider);
+    private static void registerReflectionPlugins(InvocationPlugins plugins, Replacements replacements) {
+        Registration r = new Registration(plugins, reflectionClass, replacements);
         r.register0("getCallerClass", new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
@@ -290,12 +291,12 @@
         r.registerMethodSubstitution(ReflectionSubstitutions.class, "getClassAccessFlags", Class.class);
     }
 
-    private static void registerUnsafePlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, BytecodeProvider replacementBytecodeProvider) {
+    private static void registerUnsafePlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, Replacements replacements) {
         Registration r;
         if (JavaVersionUtil.JAVA_SPEC <= 8) {
-            r = new Registration(plugins, Unsafe.class, replacementBytecodeProvider);
+            r = new Registration(plugins, Unsafe.class, replacements);
         } else {
-            r = new Registration(plugins, "jdk.internal.misc.Unsafe", replacementBytecodeProvider);
+            r = new Registration(plugins, "jdk.internal.misc.Unsafe", replacements);
         }
         String substituteMethodName = config.doingUnsafeAccessOffset != Integer.MAX_VALUE ? "copyMemoryGuarded" : "copyMemory";
         r.registerMethodSubstitution(HotSpotUnsafeSubstitutions.class, HotSpotUnsafeSubstitutions.copyMemoryName, substituteMethodName, Receiver.class, Object.class, long.class, Object.class,
@@ -340,8 +341,8 @@
         return true;
     }
 
-    private static void registerConstantPoolPlugins(InvocationPlugins plugins, WordTypes wordTypes, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, constantPoolClass, bytecodeProvider);
+    private static void registerConstantPoolPlugins(InvocationPlugins plugins, WordTypes wordTypes, GraalHotSpotVMConfig config, Replacements replacements) {
+        Registration r = new Registration(plugins, constantPoolClass, replacements);
 
         r.register2("getSize0", Receiver.class, Object.class, new InvocationPlugin() {
             @Override
@@ -411,22 +412,22 @@
         });
     }
 
-    private static void registerArrayPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, Array.class, bytecodeProvider);
+    private static void registerArrayPlugins(InvocationPlugins plugins, Replacements replacements) {
+        Registration r = new Registration(plugins, Array.class, replacements);
         r.setAllowOverwrite(true);
         r.registerMethodSubstitution(HotSpotArraySubstitutions.class, "newInstance", Class.class, int.class);
     }
 
-    private static void registerStringPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
+    private static void registerStringPlugins(InvocationPlugins plugins, Replacements replacements) {
         if (JavaVersionUtil.JAVA_SPEC > 8) {
-            final Registration utf16r = new Registration(plugins, "java.lang.StringUTF16", bytecodeProvider);
+            final Registration utf16r = new Registration(plugins, "java.lang.StringUTF16", replacements);
             utf16r.registerMethodSubstitution(StringUTF16Substitutions.class, "toBytes", char[].class, int.class, int.class);
             utf16r.registerMethodSubstitution(StringUTF16Substitutions.class, "getChars", byte[].class, int.class, int.class, char[].class, int.class);
         }
     }
 
-    private static void registerThreadPlugins(InvocationPlugins plugins, MetaAccessProvider metaAccess, WordTypes wordTypes, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, Thread.class, bytecodeProvider);
+    private static void registerThreadPlugins(InvocationPlugins plugins, MetaAccessProvider metaAccess, WordTypes wordTypes, GraalHotSpotVMConfig config, Replacements replacements) {
+        Registration r = new Registration(plugins, Thread.class, replacements);
         r.register0("currentThread", new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
@@ -440,43 +441,57 @@
             }
         });
 
-        r.registerMethodSubstitution(ThreadSubstitutions.class, "isInterrupted", Receiver.class, boolean.class);
-    }
+        if (config.osThreadInterruptedOffset != Integer.MAX_VALUE) {
+            r.registerMethodSubstitution(ThreadSubstitutions.class, "isInterrupted", Receiver.class, boolean.class);
+        }
 
-    public static final String aesEncryptName;
-    public static final String aesDecryptName;
+    }
 
     public static final String reflectionClass;
     public static final String constantPoolClass;
 
     static {
         if (JavaVersionUtil.JAVA_SPEC <= 8) {
-            aesEncryptName = "encryptBlock";
-            aesDecryptName = "decryptBlock";
             reflectionClass = "sun.reflect.Reflection";
             constantPoolClass = "sun.reflect.ConstantPool";
         } else {
-            aesEncryptName = "implEncryptBlock";
-            aesDecryptName = "implDecryptBlock";
             reflectionClass = "jdk.internal.reflect.Reflection";
             constantPoolClass = "jdk.internal.reflect.ConstantPool";
         }
     }
 
-    public static boolean cbcUsesImplNames(GraalHotSpotVMConfig config) {
+    public static String lookupIntrinsicName(GraalHotSpotVMConfig config, String className, String name1, String name2) {
+        boolean foundName1 = false;
+        boolean foundName2 = false;
+        String name = name1;
         for (VMIntrinsicMethod intrinsic : config.getStore().getIntrinsics()) {
-            if ("com/sun/crypto/provider/CipherBlockChaining".equals(intrinsic.declaringClass)) {
-                if ("encrypt".equals(intrinsic.name)) {
-                    return false;
-                } else if ("implEncrypt".equals(intrinsic.name)) {
+            if (className.equals(intrinsic.declaringClass)) {
+                if (name1.equals(intrinsic.name)) {
+                    foundName1 = true;
+                } else if (name2.equals(intrinsic.name)) {
+                    foundName2 = true;
+                    name = name2;
+                }
+            }
+        }
+        if (foundName1 != foundName2) {
+            return name;
+        }
+        throw GraalError.shouldNotReachHere();
+    }
+
+    public static boolean isIntrinsicName(GraalHotSpotVMConfig config, String className, String name) {
+        for (VMIntrinsicMethod intrinsic : config.getStore().getIntrinsics()) {
+            if (className.equals(intrinsic.declaringClass)) {
+                if (name.equals(intrinsic.name)) {
                     return true;
                 }
             }
         }
-        throw GraalError.shouldNotReachHere();
+        return false;
     }
 
-    private static void registerAESPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
+    private static void registerAESPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, Replacements replacements) {
         if (config.useAESIntrinsics) {
             assert config.aescryptEncryptBlockStub != 0L;
             assert config.aescryptDecryptBlockStub != 0L;
@@ -484,72 +499,66 @@
             assert config.cipherBlockChainingDecryptAESCryptStub != 0L;
             String arch = config.osArch;
             String decryptSuffix = arch.equals("sparc") ? "WithOriginalKey" : "";
-            Registration r = new Registration(plugins, "com.sun.crypto.provider.CipherBlockChaining", bytecodeProvider);
 
-            boolean implNames = cbcUsesImplNames(config);
-            String cbcEncryptName = implNames ? "implEncrypt" : "encrypt";
-            String cbcDecryptName = implNames ? "implDecrypt" : "decrypt";
-
+            String cbcEncryptName = lookupIntrinsicName(config, "com/sun/crypto/provider/CipherBlockChaining", "implEncrypt", "encrypt");
+            String cbcDecryptName = lookupIntrinsicName(config, "com/sun/crypto/provider/CipherBlockChaining", "implDecrypt", "decrypt");
+            Registration r = new Registration(plugins, "com.sun.crypto.provider.CipherBlockChaining", replacements);
             r.registerMethodSubstitution(CipherBlockChainingSubstitutions.class, cbcEncryptName, Receiver.class, byte[].class, int.class, int.class, byte[].class, int.class);
             r.registerMethodSubstitution(CipherBlockChainingSubstitutions.class, cbcDecryptName, cbcDecryptName + decryptSuffix, Receiver.class, byte[].class, int.class, int.class, byte[].class,
                             int.class);
 
-            r = new Registration(plugins, "com.sun.crypto.provider.AESCrypt", bytecodeProvider);
+            String aesEncryptName = lookupIntrinsicName(config, "com/sun/crypto/provider/AESCrypt", "implEncryptBlock", "encryptBlock");
+            String aesDecryptName = lookupIntrinsicName(config, "com/sun/crypto/provider/AESCrypt", "implDecryptBlock", "decryptBlock");
+
+            r = new Registration(plugins, "com.sun.crypto.provider.AESCrypt", replacements);
             r.registerMethodSubstitution(AESCryptSubstitutions.class, aesEncryptName, Receiver.class, byte[].class, int.class, byte[].class, int.class);
             r.registerMethodSubstitution(AESCryptSubstitutions.class, aesDecryptName, aesDecryptName + decryptSuffix, Receiver.class, byte[].class, int.class, byte[].class, int.class);
         }
     }
 
-    private static void registerBigIntegerPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, BigInteger.class, bytecodeProvider);
-        if (config.useMultiplyToLenIntrinsic()) {
-            assert config.multiplyToLen != 0L;
-            if (JavaVersionUtil.JAVA_SPEC <= 8) {
-                r.registerMethodSubstitution(BigIntegerSubstitutions.class, "multiplyToLen", "multiplyToLenStatic", int[].class, int.class, int[].class, int.class,
-                                int[].class);
-            } else {
-                r.registerMethodSubstitution(BigIntegerSubstitutions.class, "implMultiplyToLen", "multiplyToLenStatic", int[].class, int.class, int[].class, int.class,
-                                int[].class);
-            }
+    private static void registerBigIntegerPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, Replacements replacements) {
+        Registration r = new Registration(plugins, BigInteger.class, replacements);
+        assert !config.useMultiplyToLenIntrinsic() || config.multiplyToLen != 0L;
+        if (JavaVersionUtil.JAVA_SPEC <= 8) {
+            r.registerConditionalMethodSubstitution(config.useMultiplyToLenIntrinsic(), BigIntegerSubstitutions.class, "multiplyToLen", "multiplyToLenStatic", int[].class, int.class, int[].class,
+                            int.class, int[].class);
+        } else {
+            r.registerConditionalMethodSubstitution(config.useMultiplyToLenIntrinsic(), BigIntegerSubstitutions.class, "implMultiplyToLen", "multiplyToLenStatic", int[].class, int.class, int[].class,
+                            int.class, int[].class);
         }
-        if (config.useMulAddIntrinsic()) {
-            r.registerMethodSubstitution(BigIntegerSubstitutions.class, "implMulAdd", int[].class, int[].class, int.class, int.class, int.class);
-        }
-        if (config.useMontgomeryMultiplyIntrinsic()) {
-            r.registerMethodSubstitution(BigIntegerSubstitutions.class, "implMontgomeryMultiply", int[].class, int[].class, int[].class, int.class, long.class, int[].class);
-        }
-        if (config.useMontgomerySquareIntrinsic()) {
-            r.registerMethodSubstitution(BigIntegerSubstitutions.class, "implMontgomerySquare", int[].class, int[].class, int.class, long.class, int[].class);
-        }
-        if (config.useSquareToLenIntrinsic()) {
-            r.registerMethodSubstitution(BigIntegerSubstitutions.class, "implSquareToLen", int[].class, int.class, int[].class, int.class);
-        }
+        r.registerConditionalMethodSubstitution(config.useMulAddIntrinsic(), BigIntegerSubstitutions.class, "implMulAdd", int[].class, int[].class, int.class, int.class, int.class);
+        r.registerConditionalMethodSubstitution(config.useMontgomeryMultiplyIntrinsic(), BigIntegerSubstitutions.class, "implMontgomeryMultiply", int[].class, int[].class, int[].class, int.class,
+                        long.class, int[].class);
+        r.registerConditionalMethodSubstitution(config.useMontgomerySquareIntrinsic(), BigIntegerSubstitutions.class, "implMontgomerySquare", int[].class, int[].class, int.class, long.class,
+                        int[].class);
+        r.registerConditionalMethodSubstitution(config.useSquareToLenIntrinsic(), BigIntegerSubstitutions.class, "implSquareToLen", int[].class, int.class, int[].class, int.class);
     }
 
-    private static void registerSHAPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
+    private static void registerSHAPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, Replacements replacements) {
         boolean useSha1 = config.useSHA1Intrinsics();
         boolean useSha256 = config.useSHA256Intrinsics();
         boolean useSha512 = config.useSHA512Intrinsics();
 
-        if (JavaVersionUtil.JAVA_SPEC > 8 && (useSha1 || useSha256 || useSha512)) {
-            Registration r = new Registration(plugins, "sun.security.provider.DigestBase", bytecodeProvider);
+        if (isIntrinsicName(config, "sun/security/provider/DigestBase", "implCompressMultiBlock0") && (useSha1 || useSha256 || useSha512)) {
+            Registration r = new Registration(plugins, "sun.security.provider.DigestBase", replacements);
             r.registerMethodSubstitution(DigestBaseSubstitutions.class, "implCompressMultiBlock0", Receiver.class, byte[].class, int.class, int.class);
         }
 
+        String implCompressName = lookupIntrinsicName(config, "sun/security/provider/SHA", "implCompress", "implCompress0");
         if (useSha1) {
             assert config.sha1ImplCompress != 0L;
-            Registration r = new Registration(plugins, "sun.security.provider.SHA", bytecodeProvider);
-            r.registerMethodSubstitution(SHASubstitutions.class, SHASubstitutions.implCompressName, "implCompress0", Receiver.class, byte[].class, int.class);
+            Registration r = new Registration(plugins, "sun.security.provider.SHA", replacements);
+            r.registerMethodSubstitution(SHASubstitutions.class, implCompressName, "implCompress0", Receiver.class, byte[].class, int.class);
         }
         if (useSha256) {
             assert config.sha256ImplCompress != 0L;
-            Registration r = new Registration(plugins, "sun.security.provider.SHA2", bytecodeProvider);
-            r.registerMethodSubstitution(SHA2Substitutions.class, SHA2Substitutions.implCompressName, "implCompress0", Receiver.class, byte[].class, int.class);
+            Registration r = new Registration(plugins, "sun.security.provider.SHA2", replacements);
+            r.registerMethodSubstitution(SHA2Substitutions.class, implCompressName, "implCompress0", Receiver.class, byte[].class, int.class);
         }
         if (useSha512) {
             assert config.sha512ImplCompress != 0L;
-            Registration r = new Registration(plugins, "sun.security.provider.SHA5", bytecodeProvider);
-            r.registerMethodSubstitution(SHA5Substitutions.class, SHA5Substitutions.implCompressName, "implCompress0", Receiver.class, byte[].class, int.class);
+            Registration r = new Registration(plugins, "sun.security.provider.SHA5", replacements);
+            r.registerMethodSubstitution(SHA5Substitutions.class, implCompressName, "implCompress0", Receiver.class, byte[].class, int.class);
         }
     }
 
@@ -586,11 +595,12 @@
         }
     }
 
-    private static void registerCounterModePlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
-        if (config.useAESCTRIntrinsics) {
-            assert config.counterModeAESCrypt != 0L;
-            Registration r = new Registration(plugins, "com.sun.crypto.provider.CounterMode", bytecodeProvider);
-            r.registerMethodSubstitution(CounterModeSubstitutions.class, "implCrypt", Receiver.class, byte[].class, int.class, int.class, byte[].class, int.class);
+    private static void registerCounterModePlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, Replacements replacements) {
+        if (JavaVersionUtil.JAVA_SPEC > 8) {
+            assert !config.useAESCTRIntrinsics || config.counterModeAESCrypt != 0L;
+            Registration r = new Registration(plugins, "com.sun.crypto.provider.CounterMode", replacements);
+            r.registerConditionalMethodSubstitution(config.useAESCTRIntrinsics, CounterModeSubstitutions.class, "implCrypt", Receiver.class, byte[].class, int.class, int.class, byte[].class,
+                            int.class);
         }
     }
 
@@ -626,32 +636,31 @@
         }
     }
 
-    private static void registerCRC32Plugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
-        if (config.useCRC32Intrinsics) {
-            Registration r = new Registration(plugins, CRC32.class, bytecodeProvider);
-            r.registerMethodSubstitution(CRC32Substitutions.class, "update", int.class, int.class);
-            if (JavaVersionUtil.JAVA_SPEC <= 8) {
-                r.registerMethodSubstitution(CRC32Substitutions.class, "updateBytes", int.class, byte[].class, int.class, int.class);
-                r.registerMethodSubstitution(CRC32Substitutions.class, "updateByteBuffer", int.class, long.class, int.class, int.class);
-            } else {
-                r.registerMethodSubstitution(CRC32Substitutions.class, "updateBytes0", int.class, byte[].class, int.class, int.class);
-                r.registerMethodSubstitution(CRC32Substitutions.class, "updateByteBuffer0", int.class, long.class, int.class, int.class);
-            }
+    private static void registerCRC32Plugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, Replacements replacements) {
+        Registration r = new Registration(plugins, CRC32.class, replacements);
+        r.registerConditionalMethodSubstitution(config.useCRC32Intrinsics, CRC32Substitutions.class, "update", int.class, int.class);
+        if (JavaVersionUtil.JAVA_SPEC <= 8) {
+            r.registerConditionalMethodSubstitution(config.useCRC32Intrinsics, CRC32Substitutions.class, "updateBytes", int.class, byte[].class, int.class, int.class);
+            r.registerConditionalMethodSubstitution(config.useCRC32Intrinsics, CRC32Substitutions.class, "updateByteBuffer", int.class, long.class, int.class, int.class);
+        } else {
+            r.registerConditionalMethodSubstitution(config.useCRC32Intrinsics, CRC32Substitutions.class, "updateBytes0", int.class, byte[].class, int.class, int.class);
+            r.registerConditionalMethodSubstitution(config.useCRC32Intrinsics, CRC32Substitutions.class, "updateByteBuffer0", int.class, long.class, int.class, int.class);
         }
     }
 
-    private static void registerCRC32CPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
-        if (config.useCRC32CIntrinsics) {
-            Registration r = new Registration(plugins, "java.util.zip.CRC32C", bytecodeProvider);
-            r.registerMethodSubstitution(CRC32CSubstitutions.class, "updateBytes", int.class, byte[].class, int.class, int.class);
-            r.registerMethodSubstitution(CRC32CSubstitutions.class, "updateDirectByteBuffer", int.class, long.class, int.class, int.class);
+    private static void registerCRC32CPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, Replacements replacements) {
+        if (JavaVersionUtil.JAVA_SPEC > 8) {
+            Registration r = new Registration(plugins, "java.util.zip.CRC32C", replacements);
+            r.registerConditionalMethodSubstitution(config.useCRC32CIntrinsics, CRC32CSubstitutions.class, "updateBytes", int.class, byte[].class, int.class, int.class);
+            r.registerConditionalMethodSubstitution(config.useCRC32CIntrinsics, CRC32CSubstitutions.class, "updateDirectByteBuffer", int.class, long.class, int.class, int.class);
         }
     }
 
-    private static void registerArraysSupportPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, BytecodeProvider bytecodeProvider) {
-        if (config.useVectorizedMismatchIntrinsic) {
-            Registration r = new Registration(plugins, "jdk.internal.util.ArraysSupport", bytecodeProvider);
-            r.registerMethodSubstitution(ArraysSupportSubstitutions.class, "vectorizedMismatch", Object.class, long.class, Object.class, long.class, int.class, int.class);
+    private static void registerArraysSupportPlugins(InvocationPlugins plugins, GraalHotSpotVMConfig config, Replacements replacements) {
+        if (JavaVersionUtil.JAVA_SPEC > 8) {
+            Registration r = new Registration(plugins, "jdk.internal.util.ArraysSupport", replacements);
+            r.registerConditionalMethodSubstitution(config.useVectorizedMismatchIntrinsic, ArraysSupportSubstitutions.class, "vectorizedMismatch", Object.class, long.class, Object.class, long.class,
+                            int.class, int.class);
         }
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java	Mon Nov 04 11:25:55 2019 +0000
@@ -339,8 +339,8 @@
         linkForeignCall(options, providers, REGISTER_FINALIZER, c.registerFinalizerAddress, PREPEND_THREAD, SAFEPOINT, NOT_REEXECUTABLE, any());
         linkForeignCall(options, providers, MONITORENTER, c.monitorenterAddress, PREPEND_THREAD, SAFEPOINT, NOT_REEXECUTABLE, any());
         linkForeignCall(options, providers, MONITOREXIT, c.monitorexitAddress, PREPEND_THREAD, STACK_INSPECTABLE_LEAF, NOT_REEXECUTABLE, any());
-        linkForeignCall(options, providers, NOTIFY, c.notifyAddress, PREPEND_THREAD, SAFEPOINT, NOT_REEXECUTABLE, any());
-        linkForeignCall(options, providers, NOTIFY_ALL, c.notifyAllAddress, PREPEND_THREAD, SAFEPOINT, NOT_REEXECUTABLE, any());
+        linkForeignCall(options, providers, NOTIFY, c.notifyAddress, PREPEND_THREAD, LEAF_NO_VZERO, NOT_REEXECUTABLE, any());
+        linkForeignCall(options, providers, NOTIFY_ALL, c.notifyAllAddress, PREPEND_THREAD, LEAF_NO_VZERO, NOT_REEXECUTABLE, any());
         linkForeignCall(options, providers, LOG_PRINTF, c.logPrintfAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS);
         linkForeignCall(options, providers, LOG_OBJECT, c.logObjectAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS);
         linkForeignCall(options, providers, LOG_PRIMITIVE, c.logPrimitiveAddress, PREPEND_THREAD, LEAF, REEXECUTABLE, NO_LOCATIONS);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSuitesProvider.java	Mon Nov 04 11:25:55 2019 +0000
@@ -168,7 +168,7 @@
         if (profileInstructions != null) {
             suites.getPostAllocationOptimizationStage().appendPhase(new HotSpotInstructionProfiling(profileInstructions));
         }
-        if (Assertions.detailedAssertionsEnabled(options)) {
+        if (Assertions.assertionsEnabled()) {
             suites.getPostAllocationOptimizationStage().appendPhase(new VerifyMaxRegisterSizePhase(config.maxVectorSize));
         }
         return suites;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/BeginLockScopeNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/BeginLockScopeNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -73,7 +73,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/EndLockScopeNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/EndLockScopeNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -56,7 +56,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/StubForeignCallNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/StubForeignCallNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -72,7 +72,7 @@
     }
 
     @Override
-    public LocationIdentity[] getLocationIdentities() {
+    public LocationIdentity[] getKilledLocationIdentities() {
         LocationIdentity[] killedLocations = foreignCalls.getKilledLocations(descriptor);
         killedLocations = Arrays.copyOf(killedLocations, killedLocations.length + 1);
         killedLocations[killedLocations.length - 1] = HotSpotReplacementsUtil.PENDING_EXCEPTION_LOCATION;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -64,7 +64,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -98,7 +98,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicConstantNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicConstantNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -64,7 +64,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -93,7 +93,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/AheadOfTimeVerificationPhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/AheadOfTimeVerificationPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -70,10 +70,22 @@
     }
 
     private static boolean isDirectMethodHandle(ConstantNode node) {
+        String typeName = StampTool.typeOrNull(node).getName();
         if (!isObject(node)) {
             return false;
         }
-        return "Ljava/lang/invoke/DirectMethodHandle;".equals(StampTool.typeOrNull(node).getName());
+
+        switch (typeName) {
+            case "Ljava/lang/invoke/DirectMethodHandle;":
+            case "Ljava/lang/invoke/DirectMethodHandle$StaticAccessor;":
+            case "Ljava/lang/invoke/DirectMethodHandle$Accessor;":
+            case "Ljava/lang/invoke/DirectMethodHandle$Constructor;":
+            case "Ljava/lang/invoke/DirectMethodHandle$Special;":
+            case "Ljava/lang/invoke/DirectMethodHandle$Interface;":
+                return true;
+            default:
+                return false;
+        }
     }
 
     private static boolean isBoundMethodHandle(ConstantNode node) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java	Mon Nov 04 11:25:55 2019 +0000
@@ -308,11 +308,13 @@
 
     @Fold
     public static int osThreadOffset(@InjectedParameter GraalHotSpotVMConfig config) {
+        assert config.osThreadOffset != Integer.MAX_VALUE;
         return config.osThreadOffset;
     }
 
     @Fold
     public static int osThreadInterruptedOffset(@InjectedParameter GraalHotSpotVMConfig config) {
+        assert config.osThreadInterruptedOffset != Integer.MAX_VALUE;
         return config.osThreadInterruptedOffset;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -61,7 +61,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return HotSpotReplacementsUtil.MARK_WORD_LOCATION;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA2Substitutions.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA2Substitutions.java	Mon Nov 04 11:25:55 2019 +0000
@@ -36,7 +36,6 @@
 import org.graalvm.compiler.nodes.extended.RawLoadNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
 import org.graalvm.compiler.replacements.ReplacementsUtil;
-import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
 import org.graalvm.compiler.word.Word;
 import jdk.internal.vm.compiler.word.LocationIdentity;
 import jdk.internal.vm.compiler.word.WordFactory;
@@ -46,8 +45,6 @@
 @ClassSubstitution(className = "sun.security.provider.SHA2", optional = true)
 public class SHA2Substitutions {
 
-    public static final String implCompressName = JavaVersionUtil.JAVA_SPEC <= 8 ? "implCompress" : "implCompress0";
-
     @MethodSubstitution(isStatic = false)
     static void implCompress0(Object receiver, byte[] buf, int ofs) {
         Object realReceiver = PiNode.piCastNonNull(receiver, HotSpotReplacementsUtil.methodHolderClass(INJECTED_INTRINSIC_CONTEXT));
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA5Substitutions.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA5Substitutions.java	Mon Nov 04 11:25:55 2019 +0000
@@ -37,7 +37,6 @@
 import org.graalvm.compiler.nodes.extended.RawLoadNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
 import org.graalvm.compiler.replacements.ReplacementsUtil;
-import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
 import org.graalvm.compiler.word.Word;
 import jdk.internal.vm.compiler.word.LocationIdentity;
 import jdk.internal.vm.compiler.word.WordFactory;
@@ -47,8 +46,6 @@
 @ClassSubstitution(className = "sun.security.provider.SHA5", optional = true)
 public class SHA5Substitutions {
 
-    public static final String implCompressName = JavaVersionUtil.JAVA_SPEC <= 8 ? "implCompress" : "implCompress0";
-
     @MethodSubstitution(isStatic = false)
     static void implCompress0(Object receiver, byte[] buf, int ofs) {
         Object realReceiver = PiNode.piCastNonNull(receiver, HotSpotReplacementsUtil.methodHolderClass(INJECTED_INTRINSIC_CONTEXT));
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHASubstitutions.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHASubstitutions.java	Mon Nov 04 11:25:55 2019 +0000
@@ -37,7 +37,6 @@
 import org.graalvm.compiler.nodes.extended.RawLoadNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
 import org.graalvm.compiler.replacements.ReplacementsUtil;
-import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
 import org.graalvm.compiler.word.Word;
 import jdk.internal.vm.compiler.word.LocationIdentity;
 import jdk.internal.vm.compiler.word.WordFactory;
@@ -47,8 +46,6 @@
 @ClassSubstitution(className = "sun.security.provider.SHA", optional = true)
 public class SHASubstitutions {
 
-    public static final String implCompressName = JavaVersionUtil.JAVA_SPEC <= 8 ? "implCompress" : "implCompress0";
-
     @MethodSubstitution(isStatic = false)
     static void implCompress0(Object receiver, byte[] buf, int ofs) {
         Object realReceiver = PiNode.piCastNonNull(receiver, HotSpotReplacementsUtil.methodHolderClass(INJECTED_INTRINSIC_CONTEXT));
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ForeignCallStub.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/ForeignCallStub.java	Mon Nov 04 11:25:55 2019 +0000
@@ -56,7 +56,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
 import org.graalvm.compiler.replacements.GraphKit;
 import org.graalvm.compiler.replacements.nodes.ReadRegisterNode;
 import org.graalvm.compiler.word.Word;
@@ -258,7 +257,6 @@
             debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Initial stub graph");
 
             kit.inlineInvokes("Foreign call stub.", "Backend");
-            new RemoveValueProxyPhase().apply(graph);
 
             debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Stub graph before compilation");
             return graph;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java	Mon Nov 04 11:25:55 2019 +0000
@@ -41,7 +41,6 @@
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
-import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
 import org.graalvm.compiler.replacements.SnippetTemplate;
 import org.graalvm.compiler.replacements.Snippets;
 
@@ -100,9 +99,8 @@
                 }
             }
 
-            new RemoveValueProxyPhase().apply(graph);
             graph.setGuardsStage(GuardsStage.FLOATING_GUARDS);
-            CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+            CanonicalizerPhase canonicalizer = CanonicalizerPhase.create();
             canonicalizer.apply(graph, providers);
             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, providers);
         } catch (Throwable e) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java	Mon Nov 04 11:25:55 2019 +0000
@@ -3221,7 +3221,7 @@
             lastInstr = loopBegin;
 
             // Create phi functions for all local variables and operand stack slots.
-            frameState.insertLoopPhis(liveness, block.loopId, loopBegin, forceLoopPhis(), stampFromValueForForcedPhis());
+            frameState.insertLoopPhis(liveness, block.loopId, loopBegin, forceLoopPhis() || this.graphBuilderConfig.replaceLocalsWithConstants(), stampFromValueForForcedPhis());
             loopBegin.setStateAfter(createFrameState(block.startBci, loopBegin));
 
             /*
@@ -3545,8 +3545,9 @@
                      * will never see that the branch is taken. This can lead to deopt loops or OSR
                      * failure.
                      */
+                    double calculatedProbability = negated ? BranchProbabilityNode.DEOPT_PROBABILITY : 1.0 - BranchProbabilityNode.DEOPT_PROBABILITY;
                     FixedNode deoptSuccessor = BeginNode.begin(deopt);
-                    ValueNode ifNode = genIfNode(condition, negated ? deoptSuccessor : noDeoptSuccessor, negated ? noDeoptSuccessor : deoptSuccessor, negated ? 1 - probability : probability);
+                    ValueNode ifNode = genIfNode(condition, negated ? deoptSuccessor : noDeoptSuccessor, negated ? noDeoptSuccessor : deoptSuccessor, calculatedProbability);
                     postProcessIfNode(ifNode);
                     append(ifNode);
                 }
@@ -3569,8 +3570,28 @@
             }
 
             this.controlFlowSplit = true;
-            FixedNode trueSuccessor = createTarget(trueBlock, frameState, false, false);
-            FixedNode falseSuccessor = createTarget(falseBlock, frameState, false, true);
+            FixedNode falseSuccessor = createTarget(falseBlock, frameState, false, false);
+            FixedNode trueSuccessor = createTarget(trueBlock, frameState, false, true);
+
+            if (this.graphBuilderConfig.replaceLocalsWithConstants() && condition instanceof CompareNode) {
+                CompareNode compareNode = (CompareNode) condition;
+                if (compareNode.condition() == CanonicalCondition.EQ) {
+                    ValueNode constantNode = null;
+                    ValueNode nonConstantNode = null;
+                    if (compareNode.getX() instanceof ConstantNode) {
+                        constantNode = compareNode.getX();
+                        nonConstantNode = compareNode.getY();
+                    } else if (compareNode.getY() instanceof ConstantNode) {
+                        constantNode = compareNode.getY();
+                        nonConstantNode = compareNode.getX();
+                    }
+
+                    if (constantNode != null && nonConstantNode != null) {
+                        this.getEntryState(trueBlock).replaceValue(nonConstantNode, constantNode);
+                    }
+                }
+            }
+
             ValueNode ifNode = genIfNode(condition, trueSuccessor, falseSuccessor, probability);
             postProcessIfNode(ifNode);
             append(ifNode);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java	Mon Nov 04 11:25:55 2019 +0000
@@ -328,8 +328,7 @@
             outerFrameState = parent.getFrameStateBuilder().create(parent.bci(), parent.getNonIntrinsicAncestor(), true, null, null);
         }
         if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) {
-            FrameState newFrameState = outerFrameState.duplicateModified(outerFrameState.bci, true, false, JavaKind.Void, new JavaKind[]{JavaKind.Object}, new ValueNode[]{stack[0]});
-            return newFrameState;
+            return outerFrameState.duplicateModified(graph, outerFrameState.bci, true, false, JavaKind.Void, new JavaKind[]{JavaKind.Object}, new ValueNode[]{stack[0]});
         }
         if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
             throw shouldNotReachHere();
@@ -527,21 +526,21 @@
             ValueNode value = locals[i];
             if (value != null && value != TWO_SLOT_MARKER && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
                 debug.log(" inserting proxy for %s", value);
-                locals[i] = ProxyNode.forValue(value, loopExit, graph);
+                locals[i] = ProxyNode.forValue(value, loopExit);
             }
         }
         for (int i = 0; i < stackSize(); i++) {
             ValueNode value = stack[i];
             if (value != null && value != TWO_SLOT_MARKER && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
                 debug.log(" inserting proxy for %s", value);
-                stack[i] = ProxyNode.forValue(value, loopExit, graph);
+                stack[i] = ProxyNode.forValue(value, loopExit);
             }
         }
         for (int i = 0; i < lockedObjects.length; i++) {
             ValueNode value = lockedObjects[i];
             if (value != null && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
                 debug.log(" inserting proxy for %s", value);
-                lockedObjects[i] = ProxyNode.forValue(value, loopExit, graph);
+                lockedObjects[i] = ProxyNode.forValue(value, loopExit);
             }
         }
     }
@@ -1028,4 +1027,17 @@
         }
         sideEffects.add(sideEffect);
     }
+
+    public void replaceValue(ValueNode oldValue, ValueNode newValue) {
+        for (int i = 0; i < locals.length; ++i) {
+            if (locals[i] == oldValue) {
+                locals[i] = newValue;
+            }
+        }
+        for (int i = 0; i < stack.length; ++i) {
+            if (stack[i] == oldValue) {
+                stack[i] = newValue;
+            }
+        }
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/backend/LargeConstantSectionTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/backend/LargeConstantSectionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -38,7 +38,7 @@
 
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.jtt.JTTTest;
-import org.graalvm.compiler.test.ExportingClassLoader;
+import org.graalvm.compiler.api.test.ExportingClassLoader;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/except/UntrustedInterfaces.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/except/UntrustedInterfaces.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -25,7 +25,7 @@
 package org.graalvm.compiler.jtt.except;
 
 import org.graalvm.compiler.jtt.JTTTest;
-import org.graalvm.compiler.test.ExportingClassLoader;
+import org.graalvm.compiler.api.test.ExportingClassLoader;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.objectweb.asm.ClassWriter;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/NestedLoop_EA.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/NestedLoop_EA.java	Mon Nov 04 11:25:55 2019 +0000
@@ -44,7 +44,7 @@
     protected Suites createSuites(OptionValues options) {
         Suites suites = super.createSuites(options);
         ListIterator<BasePhase<? super HighTierContext>> position = suites.getHighTier().findPhase(PartialEscapePhase.class);
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase();
         // incremental canonicalizer of PEA is missing some important canonicalization (TODO?)
         position.add(canonicalizer);
         position.add(new PartialEscapePhase(true, canonicalizer, options));
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/TrichotomyTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/optimize/TrichotomyTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -2527,7 +2527,7 @@
         // test folding
         StructuredGraph graph = self.parseForCompile(self.getResolvedJavaMethod(name));
         HighTierContext context = self.getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = self.createCanonicalizerPhase();
         canonicalizer.apply(graph, context);
         Assert.assertTrue("Too many ConditionalNodes after canonicalization", graph.getNodes().filter(ConditionalNode.class).count() <= 1);
         Assert.assertTrue("Unexpected IfNodes after canonicalization", graph.getNodes().filter(IfNode.class).isEmpty());
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64ArithmeticOp.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.aarch64/src/org/graalvm/compiler/lir/aarch64/AArch64ArithmeticOp.java	Mon Nov 04 11:25:55 2019 +0000
@@ -58,6 +58,13 @@
     DIV,
     SMULH,
     UMULH,
+    SMULL,
+    SMNEGL,
+    MADD,
+    MSUB,
+    FMADD,
+    SMADDL,
+    SMSUBL,
     REM,
     UDIV,
     UREM,
@@ -279,6 +286,12 @@
                 case MNEG:
                     masm.mneg(size, dst, src1, src2);
                     break;
+                case SMULL:
+                    masm.smull(size, dst, src1, src2);
+                    break;
+                case SMNEGL:
+                    masm.smnegl(size, dst, src1, src2);
+                    break;
                 case DIV:
                     masm.sdiv(size, dst, src1, src2);
                     break;
@@ -477,11 +490,10 @@
         @Use(REG) protected AllocatableValue src3;
 
         /**
-         * Computes <code>result = src3 <op> src1 * src2</code>.
+         * Computes <code>result = src3 +/- src1 * src2</code>.
          */
         public MultiplyAddSubOp(AArch64ArithmeticOp op, AllocatableValue result, AllocatableValue src1, AllocatableValue src2, AllocatableValue src3) {
             super(TYPE);
-            assert op == ADD || op == SUB || op == FADD;
             this.op = op;
             this.result = result;
             this.src1 = src1;
@@ -493,15 +505,23 @@
         public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) {
             int size = result.getPlatformKind().getSizeInBytes() * Byte.SIZE;
             switch (op) {
-                case ADD:
+                case MADD:
                     masm.madd(size, asRegister(result), asRegister(src1), asRegister(src2), asRegister(src3));
                     break;
-                case SUB:
+                case MSUB:
                     masm.msub(size, asRegister(result), asRegister(src1), asRegister(src2), asRegister(src3));
                     break;
-                case FADD:
+                case FMADD:
                     masm.fmadd(size, asRegister(result), asRegister(src1), asRegister(src2), asRegister(src3));
                     break;
+                case SMADDL:
+                    assert size == 64;
+                    masm.smaddl(size, asRegister(result), asRegister(src1), asRegister(src2), asRegister(src3));
+                    break;
+                case SMSUBL:
+                    assert size == 64;
+                    masm.smsubl(size, asRegister(result), asRegister(src1), asRegister(src2), asRegister(src3));
+                    break;
                 default:
                     throw GraalError.shouldNotReachHere();
             }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java	Mon Nov 04 11:25:55 2019 +0000
@@ -96,7 +96,7 @@
     public static final class MoveToRegOp extends AbstractMoveOp {
         public static final LIRInstructionClass<MoveToRegOp> TYPE = LIRInstructionClass.create(MoveToRegOp.class);
 
-        @Def({REG, HINT}) protected AllocatableValue result;
+        @Def({REG, STACK, HINT}) protected AllocatableValue result;
         @Use({REG, STACK}) protected AllocatableValue input;
 
         public MoveToRegOp(AMD64Kind moveKind, AllocatableValue result, AllocatableValue input) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64StringLatin1InflateOp.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64StringLatin1InflateOp.java	Mon Nov 04 11:25:55 2019 +0000
@@ -32,6 +32,8 @@
 import static jdk.vm.ci.code.ValueUtil.asRegister;
 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
 
+import java.util.EnumSet;
+
 import org.graalvm.compiler.asm.Label;
 import org.graalvm.compiler.asm.amd64.AMD64Address;
 import org.graalvm.compiler.asm.amd64.AMD64Assembler;
@@ -43,8 +45,10 @@
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 
 import jdk.vm.ci.amd64.AMD64;
+import jdk.vm.ci.amd64.AMD64.CPUFeature;
 import jdk.vm.ci.amd64.AMD64Kind;
 import jdk.vm.ci.code.Register;
+import jdk.vm.ci.code.TargetDescription;
 import jdk.vm.ci.meta.Value;
 
 @Opcode("AMD64_STRING_INFLATE")
@@ -73,7 +77,7 @@
         rdstTemp = rdst = dst;
         rlenTemp = rlen = len;
 
-        vtmp1 = tool.newVariable(LIRKind.value(AMD64Kind.V512_BYTE));
+        vtmp1 = useAVX512ForStringInflateCompress(tool.target()) ? tool.newVariable(LIRKind.value(AMD64Kind.V512_BYTE)) : tool.newVariable(LIRKind.value(AMD64Kind.V128_BYTE));
         rtmp2 = tool.newVariable(LIRKind.value(AMD64Kind.DWORD));
     }
 
@@ -89,6 +93,13 @@
         byteArrayInflate(masm, src, dst, len, tmp1, tmp2);
     }
 
+    public static boolean useAVX512ForStringInflateCompress(TargetDescription target) {
+        EnumSet<CPUFeature> features = ((AMD64) target.arch).getFeatures();
+        return features.contains(AMD64.CPUFeature.AVX512BW) &&
+                        features.contains(AMD64.CPUFeature.AVX512VL) &&
+                        features.contains(AMD64.CPUFeature.BMI2);
+    }
+
     /**
      * Inflate a Latin1 string using a byte[] array representation into a UTF16 string using a
      * char[] array representation.
@@ -110,10 +121,7 @@
         assert dst.number != len.number && dst.number != tmp.number;
         assert len.number != tmp.number;
 
-        if (masm.supports(AMD64.CPUFeature.AVX512BW) &&
-                        masm.supports(AMD64.CPUFeature.AVX512VL) &&
-                        masm.supports(AMD64.CPUFeature.BMI2)) {
-
+        if (useAVX512ForStringInflateCompress(masm.target)) {
             // If the length of the string is less than 16, we chose not to use the
             // AVX512 instructions.
             masm.testl(len, -16);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64StringUTF16CompressOp.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64StringUTF16CompressOp.java	Mon Nov 04 11:25:55 2019 +0000
@@ -34,6 +34,7 @@
 import static jdk.vm.ci.amd64.AMD64.rsp;
 import static jdk.vm.ci.code.ValueUtil.asRegister;
 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
+import static org.graalvm.compiler.lir.amd64.AMD64StringLatin1InflateOp.useAVX512ForStringInflateCompress;
 
 import org.graalvm.compiler.asm.Label;
 import org.graalvm.compiler.asm.amd64.AMD64Address;
@@ -82,7 +83,7 @@
         rdstTemp = rdst = dst;
         rlenTemp = rlen = len;
 
-        LIRKind vkind = LIRKind.value(AMD64Kind.V512_BYTE);
+        LIRKind vkind = useAVX512ForStringInflateCompress(tool.target()) ? LIRKind.value(AMD64Kind.V512_BYTE) : LIRKind.value(AMD64Kind.V128_BYTE);
 
         vtmp1 = tool.newVariable(vkind);
         vtmp2 = tool.newVariable(vkind);
@@ -139,10 +140,7 @@
 
         masm.push(len);      // Save length for return.
 
-        if (masm.supports(AMD64.CPUFeature.AVX512BW) &&
-                        masm.supports(AMD64.CPUFeature.AVX512VL) &&
-                        masm.supports(AMD64.CPUFeature.BMI2)) {
-
+        if (useAVX512ForStringInflateCompress(masm.target)) {
             Label labelRestoreK1ReturnZero = new Label();
             Label labelAvxPostAlignment = new Label();
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/vector/AMD64VectorMove.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/vector/AMD64VectorMove.java	Mon Nov 04 11:25:55 2019 +0000
@@ -74,7 +74,7 @@
     public static final class MoveToRegOp extends AMD64LIRInstruction implements ValueMoveOp {
         public static final LIRInstructionClass<MoveToRegOp> TYPE = LIRInstructionClass.create(MoveToRegOp.class);
 
-        @Def({REG, HINT}) protected AllocatableValue result;
+        @Def({REG, STACK, HINT}) protected AllocatableValue result;
         @Use({REG, STACK}) protected AllocatableValue input;
 
         public MoveToRegOp(AllocatableValue result, AllocatableValue input) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -214,6 +214,23 @@
         }
     }
 
+    private static boolean verifyAssignment(LIRInstruction inst, Value newValue, EnumSet<OperandFlag> flags) {
+        Class<?> type = newValue.getClass();
+        if (!flags.contains(REG)) {
+            assert !type.isAssignableFrom(REGISTER_VALUE_CLASS) && !type.isAssignableFrom(VARIABLE_CLASS) : "Cannot assign RegisterValue / Variable to field without REG flag: " + inst + " newValue=" +
+                            newValue;
+        }
+        if (!flags.contains(STACK)) {
+            assert !type.isAssignableFrom(STACK_SLOT_CLASS) : "Cannot assign StackSlot to field without STACK flag: " + inst + " newValue=" +
+                            newValue;
+        }
+        if (!flags.contains(CONST)) {
+            assert !type.isAssignableFrom(CONSTANT_VALUE_CLASS) : "Cannot assign Constant to field without CONST flag: " + inst + " newValue=" +
+                            newValue;
+        }
+        return true;
+    }
+
     protected static void forEach(LIRInstruction inst, Values values, OperandMode mode, InstructionValueProcedure proc) {
         for (int i = 0; i < values.getCount(); i++) {
             assert LIRInstruction.ALLOWED_FLAGS.get(mode).containsAll(values.getFlags(i));
@@ -228,6 +245,9 @@
                     newValue = proc.doValue(inst, value, mode, values.getFlags(i));
                 }
                 if (!value.identityEquals(newValue)) {
+                    if (!(value instanceof CompositeValue)) {
+                        assert verifyAssignment(inst, newValue, values.getFlags(i));
+                    }
                     values.setValue(inst, i, newValue);
                 }
             } else {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -881,7 +881,12 @@
                     }
                 }
             }
-            return move.getConstant();
+            Constant constant = move.getConstant();
+            if (!(constant instanceof JavaConstant)) {
+                // Other kinds of constants might not be supported by the generic move operation.
+                return null;
+            }
+            return constant;
         }
         return null;
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/ConvertDeoptimizeToGuardPhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/ConvertDeoptimizeToGuardPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -217,7 +217,7 @@
 
                             Node newGuard = guard;
                             if (survivingSuccessor instanceof LoopExitNode) {
-                                newGuard = ProxyNode.forGuard(guard, (LoopExitNode) survivingSuccessor, graph);
+                                newGuard = ProxyNode.forGuard(guard, (LoopExitNode) survivingSuccessor);
                             }
                             survivingSuccessor.replaceAtUsages(InputType.Guard, newGuard);
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPeelingPhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopPeelingPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -24,6 +24,7 @@
 
 package org.graalvm.compiler.loop.phases;
 
+import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.loop.LoopEx;
 import org.graalvm.compiler.loop.LoopPolicies;
@@ -33,6 +34,8 @@
 
 public class LoopPeelingPhase extends LoopPhase<LoopPolicies> {
 
+    public static final CounterKey PEELED = DebugContext.counter("Peeled");
+
     public LoopPeelingPhase(LoopPolicies policies) {
         super(policies);
     }
@@ -45,10 +48,13 @@
             LoopsData data = new LoopsData(graph);
             try (DebugContext.Scope s = debug.scope("peeling", data.getCFG())) {
                 for (LoopEx loop : data.outerFirst()) {
-                    if (getPolicies().shouldPeel(loop, data.getCFG(), context.getMetaAccess())) {
-                        debug.log("Peeling %s", loop);
-                        LoopTransformations.peel(loop);
-                        debug.dump(DebugContext.DETAILED_LEVEL, graph, "Peeling %s", loop);
+                    if (loop.canDuplicateLoop() && loop.loopBegin().getLoopEndCount() > 0) {
+                        if (LoopPolicies.Options.PeelALot.getValue(graph.getOptions()) || getPolicies().shouldPeel(loop, data.getCFG(), context.getMetaAccess())) {
+                            debug.log("Peeling %s", loop);
+                            PEELED.add(debug, 1);
+                            LoopTransformations.peel(loop);
+                            debug.dump(DebugContext.DETAILED_LEVEL, graph, "Peeling %s", loop);
+                        }
                     }
                 }
                 data.deleteUnusedNodes();
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopTransformations.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.phases/src/org/graalvm/compiler/loop/phases/LoopTransformations.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -35,9 +35,13 @@
 import org.graalvm.compiler.core.common.calc.CanonicalCondition;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Graph.Mark;
+import org.graalvm.compiler.graph.Graph.NodeEventScope;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.Position;
+import org.graalvm.compiler.graph.spi.Simplifiable;
+import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.loop.CountedLoopInfo;
+import org.graalvm.compiler.loop.DefaultLoopPolicies;
 import org.graalvm.compiler.loop.InductionVariable.Direction;
 import org.graalvm.compiler.loop.LoopEx;
 import org.graalvm.compiler.loop.LoopFragmentInside;
@@ -49,6 +53,7 @@
 import org.graalvm.compiler.nodes.BeginNode;
 import org.graalvm.compiler.nodes.ControlSplitNode;
 import org.graalvm.compiler.nodes.EndNode;
+import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.IfNode;
@@ -65,8 +70,10 @@
 import org.graalvm.compiler.nodes.extended.OpaqueNode;
 import org.graalvm.compiler.nodes.extended.SwitchNode;
 import org.graalvm.compiler.nodes.spi.CoreProviders;
+import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.nodes.util.IntegerHelper;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.util.EconomicSetNodeEventListener;
 
 public abstract class LoopTransformations {
 
@@ -75,24 +82,63 @@
     }
 
     public static void peel(LoopEx loop) {
+        loop.detectCounted();
         loop.inside().duplicate().insertBefore(loop);
-        loop.loopBegin().setLoopFrequency(Math.max(0.0, loop.loopBegin().loopFrequency() - 1));
+        if (loop.isCounted()) {
+            // For counted loops we assume that we have an effect on the loop frequency.
+            loop.loopBegin().setLoopFrequency(Math.max(1.0, loop.loopBegin().loopFrequency() - 1));
+        }
     }
 
+    @SuppressWarnings("try")
     public static void fullUnroll(LoopEx loop, CoreProviders context, CanonicalizerPhase canonicalizer) {
         // assert loop.isCounted(); //TODO (gd) strengthen : counted with known trip count
         LoopBeginNode loopBegin = loop.loopBegin();
         StructuredGraph graph = loopBegin.graph();
         int initialNodeCount = graph.getNodeCount();
-        while (!loopBegin.isDeleted()) {
-            Mark mark = graph.getMark();
-            peel(loop);
-            canonicalizer.applyIncremental(graph, context, mark);
-            loop.invalidateFragments();
-            if (graph.getNodeCount() > initialNodeCount + MaximumDesiredSize.getValue(graph.getOptions()) * 2) {
-                throw new RetryableBailoutException("FullUnroll : Graph seems to grow out of proportion");
+        SimplifierTool defaultSimplifier = GraphUtil.getDefaultSimplifier(context.getMetaAccess(), context.getConstantReflection(), context.getConstantFieldProvider(),
+                        canonicalizer.getCanonicalizeReads(), graph.getAssumptions(), graph.getOptions());
+        /*
+         * IMPORTANT: Canonicalizations inside the body of the remaining loop can introduce new
+         * control flow that is not automatically picked up by the control flow graph computation of
+         * the original LoopEx data structure, thus we disable simplification and manually simplify
+         * conditions in the peeled iteration to simplify the exit path.
+         */
+        CanonicalizerPhase c = canonicalizer.copyWithoutSimplification();
+        EconomicSetNodeEventListener l = new EconomicSetNodeEventListener();
+        int peelings = 0;
+        try (NodeEventScope ev = graph.trackNodeEvents(l)) {
+            while (!loopBegin.isDeleted()) {
+                Mark newNodes = graph.getMark();
+                /*
+                 * Mark is not enough for the canonicalization of the floating nodes in the unrolled
+                 * code since pre-existing constants are not new nodes. Therefore, we canonicalize
+                 * (without simplification) all floating nodes changed during peeling but only
+                 * simplify new (in the peeled iteration) ones.
+                 */
+                EconomicSetNodeEventListener peeledListener = new EconomicSetNodeEventListener();
+                try (NodeEventScope peeledScope = graph.trackNodeEvents(peeledListener)) {
+                    LoopTransformations.peel(loop);
+                }
+                graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After peeling loop %s", loop);
+                c.applyIncremental(graph, context, peeledListener.getNodes());
+                loop.invalidateFragments();
+                for (Node n : graph.getNewNodes(newNodes)) {
+                    if (n.isAlive() && (n instanceof IfNode || n instanceof SwitchNode || n instanceof FixedGuardNode || n instanceof BeginNode)) {
+                        Simplifiable s = (Simplifiable) n;
+                        s.simplify(defaultSimplifier);
+                        graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After simplifying if %s", s);
+                    }
+                }
+                if (graph.getNodeCount() > initialNodeCount + MaximumDesiredSize.getValue(graph.getOptions()) * 2 ||
+                                peelings > DefaultLoopPolicies.Options.FullUnrollMaxIterations.getValue(graph.getOptions())) {
+                    throw new RetryableBailoutException("FullUnroll : Graph seems to grow out of proportion");
+                }
+                peelings++;
             }
         }
+        // Canonicalize with the original canonicalizer to capture all simplifications
+        canonicalizer.applyIncremental(graph, context, l.getNodes());
     }
 
     public static void unswitch(LoopEx loop, List<ControlSplitNode> controlSplitNodeSet) {
@@ -280,9 +326,9 @@
 
         // Change the preLoop to execute one iteration for now
         updatePreLoopLimit(preCounted);
-        preLoopBegin.setLoopFrequency(1);
-        mainLoopBegin.setLoopFrequency(Math.max(0.0, mainLoopBegin.loopFrequency() - 2));
-        postLoopBegin.setLoopFrequency(Math.max(0.0, postLoopBegin.loopFrequency() - 1));
+        preLoopBegin.setLoopFrequency(1.0);
+        mainLoopBegin.setLoopFrequency(Math.max(1.0, mainLoopBegin.loopFrequency() - 2));
+        postLoopBegin.setLoopFrequency(Math.max(1.0, postLoopBegin.loopFrequency() - 1));
 
         // The pre and post loops don't require safepoints at all
         for (SafepointNode safepoint : preLoop.nodes().filter(SafepointNode.class)) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop.test/src/org/graalvm/compiler/loop/test/LoopPartialUnrollTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -302,7 +302,7 @@
         try (DebugContext.Scope buildScope = graph.getDebug().scope(name, method, graph)) {
             MidTierContext context = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, null);
 
-            CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+            CanonicalizerPhase canonicalizer = this.createCanonicalizerPhase();
             canonicalizer.apply(graph, context);
             new RemoveValueProxyPhase().apply(graph);
             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
@@ -338,7 +338,7 @@
 
         StructuredGraph referenceGraph = buildGraph(reference, false);
         StructuredGraph testGraph = buildGraph(test, true);
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         canonicalizer.apply(testGraph, getDefaultMidTierContext());
         canonicalizer.apply(referenceGraph, getDefaultMidTierContext());
         assertEquals(referenceGraph, testGraph);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -122,18 +122,18 @@
         }
         ValueNode range = sub(max, min);
 
-        ConstantNode one = ConstantNode.forIntegerStamp(stamp, 1);
+        ConstantNode one = ConstantNode.forIntegerStamp(stamp, 1, graph);
         if (oneOff) {
             range = add(range, one);
         }
         // round-away-from-zero divison: (range + stride -/+ 1) / stride
-        ValueNode denominator = add(range, sub(absStride, one));
+        ValueNode denominator = add(graph, range, sub(absStride, one), NodeView.DEFAULT);
         ValueNode div = unsignedDivBefore(graph, loop.entryPoint(), denominator, absStride, null);
 
         if (assumeLoopEntered) {
             return graph.addOrUniqueWithInputs(div);
         }
-        ConstantNode zero = ConstantNode.forIntegerStamp(stamp, 0);
+        ConstantNode zero = ConstantNode.forIntegerStamp(stamp, 0, graph);
         // This check is "wide": it looks like min <= max
         // That's OK even if the loop is strict (`!isLimitIncluded()`)
         // because in this case, `div` will be zero when min == max
@@ -142,6 +142,40 @@
     }
 
     /**
+     * Determine if the loop might be entered. Returns {@code false} if we can tell statically that
+     * the loop cannot be entered; returns {@code true} if the loop might possibly be entered,
+     * including in the case where we cannot be sure statically.
+     *
+     * @return false if the loop can definitely not be entered, true otherwise
+     */
+    public boolean loopMightBeEntered() {
+        Stamp stamp = iv.valueNode().stamp(NodeView.DEFAULT);
+
+        ValueNode max;
+        ValueNode min;
+        if (iv.direction() == Direction.Up) {
+            max = end;
+            min = iv.initNode();
+        } else {
+            assert iv.direction() == Direction.Down;
+            max = iv.initNode();
+            min = end;
+        }
+        if (oneOff) {
+            max = add(max, ConstantNode.forIntegerStamp(stamp, 1));
+        }
+
+        LogicNode entryCheck = getCounterIntegerHelper().createCompareNode(min, max, NodeView.DEFAULT);
+        if (entryCheck.isContradiction()) {
+            // We can definitely not enter this loop.
+            return false;
+        } else {
+            // We don't know for sure that the loop can't be entered, so assume it can.
+            return true;
+        }
+    }
+
+    /**
      * @return true if the loop has constant bounds.
      */
     public boolean isConstantMaxTripCount() {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DefaultLoopPolicies.java	Mon Nov 04 11:25:55 2019 +0000
@@ -26,7 +26,7 @@
 
 import static org.graalvm.compiler.core.common.GraalOptions.LoopMaxUnswitch;
 import static org.graalvm.compiler.core.common.GraalOptions.MaximumDesiredSize;
-import static org.graalvm.compiler.core.common.GraalOptions.MinimumPeelProbability;
+import static org.graalvm.compiler.core.common.GraalOptions.MinimumPeelFrequency;
 
 import java.util.List;
 
@@ -38,9 +38,6 @@
 import org.graalvm.compiler.graph.NodeBitMap;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.ControlSplitNode;
-import org.graalvm.compiler.nodes.DeoptimizeNode;
-import org.graalvm.compiler.nodes.FixedNode;
-import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.InvokeNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.MergeNode;
@@ -51,7 +48,6 @@
 import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
 import org.graalvm.compiler.nodes.debug.ControlFlowAnchorNode;
-import org.graalvm.compiler.nodes.java.TypeSwitchNode;
 import org.graalvm.compiler.options.Option;
 import org.graalvm.compiler.options.OptionKey;
 import org.graalvm.compiler.options.OptionType;
@@ -79,13 +75,32 @@
     public boolean shouldPeel(LoopEx loop, ControlFlowGraph cfg, MetaAccessProvider metaAccess) {
         LoopBeginNode loopBegin = loop.loopBegin();
         double entryProbability = cfg.blockFor(loopBegin.forwardEnd()).getRelativeFrequency();
-        OptionValues options = cfg.graph.getOptions();
-        if (entryProbability > MinimumPeelProbability.getValue(options) && loop.size() + loopBegin.graph().getNodeCount() < MaximumDesiredSize.getValue(options)) {
-            // check whether we're allowed to peel this loop
-            return loop.canDuplicateLoop();
-        } else {
+        StructuredGraph graph = cfg.graph;
+        OptionValues options = graph.getOptions();
+
+        if (entryProbability < MinimumPeelFrequency.getValue(options)) {
             return false;
         }
+
+        if (loop.parent() != null) {
+            if (loop.size() > loop.parent().size() >> 1) {
+                // This loops make up more than half of the parent loop in terms of number of nodes.
+                // There is a risk that this loop unproportionally increases parent loop body size.
+                return false;
+            }
+        }
+
+        if (loop.loop().getChildren().size() > 0) {
+            // This loop has child loops. Loop peeling could explode graph size.
+            return false;
+        }
+
+        if (loop.size() + graph.getNodeCount() > MaximumDesiredSize.getValue(options)) {
+            // We are out of budget for peeling.
+            return false;
+        }
+
+        return true;
     }
 
     @Override
@@ -189,7 +204,7 @@
             return false;
         }
         OptionValues options = loop.entryPoint().getOptions();
-        return loopBegin.unswitches() <= LoopMaxUnswitch.getValue(options);
+        return loopBegin.unswitches() < LoopMaxUnswitch.getValue(options);
     }
 
     private static final class CountingClosure implements VirtualClosure {
@@ -238,19 +253,9 @@
         int loopTotal = loop.size() - loop.loopBegin().phis().count() - stateNodesCount.count - 1;
         int actualDiff = (loopTotal - inBranchTotal);
         ControlSplitNode firstSplit = controlSplits.get(0);
-        if (firstSplit instanceof TypeSwitchNode) {
-            int copies = firstSplit.successors().count() - 1;
-            for (Node succ : firstSplit.successors()) {
-                FixedNode current = (FixedNode) succ;
-                while (current instanceof FixedWithNextNode) {
-                    current = ((FixedWithNextNode) current).next();
-                }
-                if (current instanceof DeoptimizeNode) {
-                    copies--;
-                }
-            }
-            actualDiff = actualDiff * copies;
-        }
+
+        int copies = firstSplit.successors().count() - 1;
+        actualDiff = actualDiff * copies;
 
         debug.log("shouldUnswitch(%s, %s) : delta=%d (%.2f%% inside of branches), max=%d, f=%.2f, phis=%d -> %b", loop, controlSplits, actualDiff, (double) (inBranchTotal) / loopTotal * 100, maxDiff,
                         loopFrequency, phis, actualDiff <= maxDiff);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java	Mon Nov 04 11:25:55 2019 +0000
@@ -81,6 +81,7 @@
     private LoopsData data;
     private EconomicMap<Node, InductionVariable> ivs;
     private boolean countedLoopChecked;
+    private int size = -1;
 
     LoopEx(Loop<Block> loop, LoopsData data) {
         this.loop = loop;
@@ -156,7 +157,10 @@
     }
 
     public int size() {
-        return whole().nodes().count();
+        if (size == -1) {
+            size = whole().nodes().count();
+        }
+        return size;
     }
 
     @Override
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java	Mon Nov 04 11:25:55 2019 +0000
@@ -349,12 +349,11 @@
                         TriState isAnchorInLoop = isLoopNode(anchor, loopNodes, nonLoopNodes);
                         if (isAnchorInLoop != TriState.FALSE) {
                             if (!(anchor instanceof LoopExitNode && ((LoopExitNode) anchor).loopBegin() == loopBeginNode)) {
-                                /*
-                                 * (gd) this is wrong in general, it's completely avoidable while we
-                                 * are doing loop transforms using ValueProxies. If it happens after
-                                 * it could still cause problem.
-                                 */
-                                assert !((GuardNode) current).graph().hasValueProxies();
+                                // It is undecidable whether the node is in the loop or not. This is
+                                // not an issue for getting counted loop information,
+                                // but causes issues when using the information for actual loop
+                                // transformations. This is why a loop transformation must
+                                // not happen while guards are floating.
                                 isLoopNode = true;
                             }
                         } else if (AbstractControlFlowGraph.strictlyDominates(cfg.blockFor(anchor), cfg.blockFor(loopBeginNode))) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java	Mon Nov 04 11:25:55 2019 +0000
@@ -233,7 +233,7 @@
             }
         }
         mainLoopBegin.setUnrollFactor(mainLoopBegin.getUnrollFactor() * 2);
-        mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
+        mainLoopBegin.setLoopFrequency(Math.max(1.0, 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);
@@ -438,7 +438,7 @@
             for (int i = 0; i < phi.valueCount(); i++) {
                 ValueNode v = phi.valueAt(i);
                 if (loopBegin.isPhiAtMerge(v)) {
-                    PhiNode newV = peel.getDuplicatedNode((ValuePhiNode) v);
+                    PhiNode newV = peel.getDuplicatedNode((PhiNode) v);
                     if (newV != null) {
                         phi.setValueAt(i, newV);
                     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopPolicies.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopPolicies.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -28,10 +28,18 @@
 
 import org.graalvm.compiler.nodes.ControlSplitNode;
 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
+import org.graalvm.compiler.options.Option;
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionType;
 
 import jdk.vm.ci.meta.MetaAccessProvider;
 
 public interface LoopPolicies {
+
+    class Options {
+        @Option(help = "", type = OptionType.Expert) public static final OptionKey<Boolean> PeelALot = new OptionKey<>(false);
+    }
+
     boolean shouldPeel(LoopEx loop, ControlFlowGraph cfg, MetaAccessProvider metaAccess);
 
     boolean shouldFullUnroll(LoopEx loop);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -94,7 +94,9 @@
             if (before.predecessor() instanceof FixedBinaryNode) {
                 FixedBinaryNode binaryPredecessor = (FixedBinaryNode) before.predecessor();
                 if (fixedDiv.dataFlowEquals(binaryPredecessor)) {
-                    fixedDiv.safeDelete();
+                    if (fixedDiv.isAlive()) {
+                        fixedDiv.safeDelete();
+                    }
                     return binaryPredecessor;
                 }
             }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/GraalBenchmark.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/GraalBenchmark.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -28,6 +28,8 @@
 import static org.graalvm.compiler.microbenchmarks.graal.GraalBenchmark.Defaults.MEASUREMENT_ITERATIONS;
 import static org.graalvm.compiler.microbenchmarks.graal.GraalBenchmark.Defaults.WARMUP_ITERATIONS;
 
+import org.graalvm.compiler.api.test.ModuleSupport;
+
 import org.openjdk.jmh.annotations.Fork;
 import org.openjdk.jmh.annotations.Measurement;
 import org.openjdk.jmh.annotations.Warmup;
@@ -41,6 +43,9 @@
 @Measurement(iterations = MEASUREMENT_ITERATIONS)
 @Fork(FORKS)
 public class GraalBenchmark {
+    static {
+        ModuleSupport.exportAndOpenAllPackagesToUnnamed("jdk.internal.vm.compiler");
+    }
 
     public static class Defaults {
         public static final int MEASUREMENT_ITERATIONS = 5;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/TestJMHWhitebox.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/graal/TestJMHWhitebox.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -36,7 +36,7 @@
 /**
  * This dummy class is used to verify that the JMH microbenchmarking environment is set up properly.
  */
-public class TestJMHWhitebox {
+public class TestJMHWhitebox extends GraalBenchmark {
 
     @Benchmark
     public void testJMH(@SuppressWarnings("unused") GraalState s) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IfNodeCanonicalizationTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IfNodeCanonicalizationTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -67,19 +67,19 @@
                     for (byte d : testValues) {
                         values[3] = d;
                         value = 2;
-                        super.test("testSnippet1", values, true);
-                        super.test("testSnippet1", values, false);
+                        super.test("testSnippet1", values);
                     }
                 }
             }
         }
     }
 
-    public int testSnippet1(byte[] values, boolean test) {
+    public int testSnippet1(byte[] values) {
         int v = values[0] - values[1];
-        if (test) {
-            v = values[2] - values[3];
+        if (v < 0) {
+            value = 2;
         }
+        v = values[3] - values[2];
         if (v < 0) {
             value = 1;
         }
@@ -156,13 +156,12 @@
         StructuredGraph graph = parseEager(name, AllowAssumptions.YES);
 
         CoreProviders context = getProviders();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         new ConvertDeoptimizeToGuardPhase().apply(graph, context);
         graph.clearAllStateAfter();
         graph.setGuardsStage(StructuredGraph.GuardsStage.AFTER_FSA);
         canonicalizer.apply(graph, context);
 
-        // new DominatorConditionalEliminationPhase(true).apply(graph, context);
         new IterativeConditionalEliminationPhase(canonicalizer, true).apply(graph, context);
         canonicalizer.apply(graph, context);
         canonicalizer.apply(graph, context);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/LoopPhiCanonicalizerTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/LoopPhiCanonicalizerTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -31,7 +31,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.spi.CoreProviders;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -67,7 +66,7 @@
 
         CoreProviders context = getProviders();
         Assert.assertEquals(5, graph.getNodes().filter(loopPhis).count());
-        new CanonicalizerPhase().apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         Assert.assertEquals(2, graph.getNodes().filter(loopPhis).count());
 
         test("loopSnippet");
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ShortCircuitOrNodeTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ShortCircuitOrNodeTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -378,7 +378,7 @@
             String snippet = "testCascadeSnippet" + i;
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES);
             CoreProviders context = getProviders();
-            CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+            CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
             canonicalizer.apply(graph, context);
             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
             new IncrementalCanonicalizerPhase<>(canonicalizer, new FloatingReadPhase()).apply(graph, context);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/FrameState.java	Mon Nov 04 11:25:55 2019 +0000
@@ -306,15 +306,8 @@
     /**
      * Gets a copy of this frame state.
      */
-    public FrameState duplicate(int newBci) {
-        return graph().add(new FrameState(outerFrameState(), code, newBci, values, localsSize, stackSize, rethrowException, duringCall, monitorIds, virtualObjectMappings));
-    }
-
-    /**
-     * Gets a copy of this frame state.
-     */
     public FrameState duplicate() {
-        return duplicate(bci);
+        return graph().add(new FrameState(outerFrameState(), code, bci, values, localsSize, stackSize, rethrowException, duringCall, monitorIds, virtualObjectMappings));
     }
 
     /**
@@ -350,20 +343,6 @@
     }
 
     /**
-     * Creates a copy of this frame state with one stack element of type {@code popKind} popped from
-     * the stack and the values in {@code pushedValues} pushed on the stack. The
-     * {@code pushedValues} will be formatted correctly in slot encoding: a long or double will be
-     * followed by a null slot.
-     */
-    public FrameState duplicateModified(int newBci, boolean newRethrowException, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) {
-        return duplicateModified(graph(), newBci, newRethrowException, duringCall, popKind, pushedSlotKinds, pushedValues);
-    }
-
-    public FrameState duplicateModified(int newBci, boolean newRethrowException, boolean newDuringCall, JavaKind popKind, JavaKind[] pushedSlotKinds, ValueNode[] pushedValues) {
-        return duplicateModified(graph(), newBci, newRethrowException, newDuringCall, popKind, pushedSlotKinds, pushedValues);
-    }
-
-    /**
      * Creates a copy of this frame state with the top of stack replaced with with
      * {@code pushedValue} which must be of type {@code popKind}.
      */
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -1748,7 +1748,7 @@
                                 "Value flowing out of loop, but we are not prepared to insert a ProxyNode");
 
                 ProxyPlaceholder proxyPlaceholder = (ProxyPlaceholder) value;
-                ValueProxyNode proxy = ProxyNode.forValue(proxyPlaceholder.value, loopExit, graph);
+                ValueProxyNode proxy = ProxyNode.forValue(proxyPlaceholder.value, loopExit);
                 proxyPlaceholder.setValue(proxy);
                 newValues.add(proxy);
             }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardProxyNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardProxyNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -25,19 +25,13 @@
 package org.graalvm.compiler.nodes;
 
 import org.graalvm.compiler.core.common.type.StampFactory;
-import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
-import org.graalvm.compiler.graph.spi.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.extended.GuardingNode;
-import org.graalvm.compiler.nodes.spi.LIRLowerable;
-import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
-import org.graalvm.compiler.nodes.spi.Proxy;
 
-@NodeInfo(allowedUsageTypes = {InputType.Guard}, nameTemplate = "Proxy({i#value})")
-public final class GuardProxyNode extends ProxyNode implements GuardingNode, Proxy, LIRLowerable, Canonicalizable {
+@NodeInfo(allowedUsageTypes = {InputType.Guard}, nameTemplate = "GuardProxy({i#value})")
+public final class GuardProxyNode extends ProxyNode implements GuardingNode {
 
     public static final NodeClass<GuardProxyNode> TYPE = NodeClass.create(GuardProxyNode.class);
     @OptionalInput(InputType.Guard) GuardingNode value;
@@ -47,10 +41,6 @@
         this.value = value;
     }
 
-    @Override
-    public void generate(NodeLIRBuilderTool generator) {
-    }
-
     public void setValue(GuardingNode newValue) {
         this.updateUsages(value.asNode(), newValue.asNode());
         this.value = newValue;
@@ -65,17 +55,4 @@
     public PhiNode createPhi(AbstractMergeNode merge) {
         return graph().addWithoutUnique(new GuardPhiNode(merge));
     }
-
-    @Override
-    public Node getOriginalNode() {
-        return (value == null ? null : value.asNode());
-    }
-
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        if (value == null) {
-            return null;
-        }
-        return this;
-    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -54,7 +54,6 @@
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.NodeSourcePosition;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
-import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.graph.spi.Simplifiable;
 import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.nodeinfo.InputType;
@@ -95,9 +94,6 @@
 public final class IfNode extends ControlSplitNode implements Simplifiable, LIRLowerable, IterableNodeType, SwitchFoldable {
     public static final NodeClass<IfNode> TYPE = NodeClass.create(IfNode.class);
 
-    private static final int MAX_USAGE_COLOR_SET_SIZE = 64;
-    private static final int MAX_FRAMESTATE_SEARCH_DEPTH = 4;
-
     private static final CounterKey CORRECTED_PROBABILITIES = DebugContext.counter("CorrectedProbabilities");
 
     @Successor AbstractBeginNode trueSuccessor;
@@ -297,10 +293,6 @@
             return;
         }
 
-        if (splitIfAtPhi(tool)) {
-            return;
-        }
-
         if (conditionalNodeOptimization(tool)) {
             return;
         }
@@ -1185,400 +1177,6 @@
     }
 
     /**
-     * Take an if that is immediately dominated by a merge with a single phi and split off any paths
-     * where the test would be statically decidable creating a new merge below the appropriate side
-     * of the IfNode. Any undecidable tests will continue to use the original IfNode.
-     *
-     * @param tool
-     */
-    @SuppressWarnings("try")
-    private boolean splitIfAtPhi(SimplifierTool tool) {
-        if (!(predecessor() instanceof MergeNode)) {
-            return false;
-        }
-        MergeNode merge = (MergeNode) predecessor();
-        if (merge.forwardEndCount() == 1) {
-            // Don't bother.
-            return false;
-        }
-        if (merge.getUsageCount() != 1 || merge.phis().count() != 1) {
-            // Don't trigger with multiple phis. Would require more rewiring.
-            // Most of the time the additional phis are memory phis that are removed after
-            // fixed read phase.
-            return false;
-        }
-        if (graph().getGuardsStage().areFrameStatesAtSideEffects() && merge.stateAfter() == null) {
-            return false;
-        }
-
-        PhiNode generalPhi = merge.phis().first();
-        if (!(generalPhi instanceof ValuePhiNode)) {
-            return false;
-        }
-
-        if (trueSuccessor().isUsedAsGuardInput() || falseSuccessor().isUsedAsGuardInput()) {
-            return false;
-        }
-
-        ValuePhiNode phi = (ValuePhiNode) generalPhi;
-
-        EconomicMap<Node, NodeColor> coloredNodes = EconomicMap.create(Equivalence.IDENTITY, 8);
-
-        /*
-         * Check that the condition uses the phi and that there is only one user of the condition
-         * expression.
-         */
-        if (!conditionUses(condition(), phi, coloredNodes)) {
-            return false;
-        }
-
-        if (!mayRemoveSplit(merge)) {
-            return false;
-        }
-
-        LogicNode[] results = new LogicNode[merge.forwardEndCount()];
-        boolean success = false;
-        for (int i = 0; i < results.length; ++i) {
-            ValueNode value = phi.valueAt(i);
-            LogicNode curResult = computeCondition(tool, condition, phi, value);
-            if (curResult != condition) {
-                for (Node n : curResult.inputs()) {
-                    if (n instanceof ConstantNode || n instanceof ParameterNode || n instanceof FixedNode) {
-                        // Constant inputs or parameters or fixed nodes are OK.
-                    } else if (n == value) {
-                        // References to the value itself are also OK.
-                    } else {
-                        // Input may cause scheduling issues.
-                        curResult = condition;
-                        break;
-                    }
-                }
-                success = true;
-            }
-            results[i] = curResult;
-        }
-
-        if (!success) {
-            return false;
-        }
-
-        for (Node usage : phi.usages()) {
-            if (usage == merge.stateAfter()) {
-                // This usage can be ignored, because it is directly in the state after.
-            } else {
-                NodeColor color = colorUsage(coloredNodes, usage, merge, this.trueSuccessor(), this.falseSuccessor());
-                if (color == NodeColor.MIXED) {
-                    return false;
-                }
-            }
-        }
-
-        /*
-         * We could additionally filter for the case that at least some of the Phi inputs or one of
-         * the condition inputs are constants but there are cases where a non-constant is
-         * simplifiable, usually where the stamp allows the question to be answered.
-         */
-
-        /* Each successor of the if gets a new merge if needed. */
-        MergeNode trueMerge = null;
-        MergeNode falseMerge = null;
-        int i = 0;
-        for (EndNode end : merge.forwardEnds().snapshot()) {
-            ValueNode value = phi.valueAt(end);
-            LogicNode result = results[i++];
-            if (result instanceof LogicConstantNode) {
-                if (((LogicConstantNode) result).getValue()) {
-                    if (trueMerge == null) {
-                        trueMerge = insertMerge(trueSuccessor(), phi, merge.stateAfter(), tool);
-                        replaceNodesInBranch(coloredNodes, NodeColor.TRUE_BRANCH, phi, trueMerge.phis().first());
-                    }
-                    trueMerge.phis().first().addInput(value);
-                    trueMerge.addForwardEnd(end);
-                } else {
-                    if (falseMerge == null) {
-                        falseMerge = insertMerge(falseSuccessor(), phi, merge.stateAfter(), tool);
-                        replaceNodesInBranch(coloredNodes, NodeColor.FALSE_BRANCH, phi, falseMerge.phis().first());
-                    }
-                    falseMerge.phis().first().addInput(value);
-                    falseMerge.addForwardEnd(end);
-                }
-                merge.removeEnd(end);
-            } else if (result != condition) {
-                // Build a new IfNode using the new condition
-                BeginNode trueBegin = graph().add(new BeginNode());
-                trueBegin.setNodeSourcePosition(trueSuccessor().getNodeSourcePosition());
-                BeginNode falseBegin = graph().add(new BeginNode());
-                falseBegin.setNodeSourcePosition(falseSuccessor().getNodeSourcePosition());
-
-                if (result.graph() == null) {
-                    result = graph().addOrUniqueWithInputs(result);
-                    result.setNodeSourcePosition(condition.getNodeSourcePosition());
-                }
-                IfNode newIfNode = graph().add(new IfNode(result, trueBegin, falseBegin, trueSuccessorProbability));
-                newIfNode.setNodeSourcePosition(getNodeSourcePosition());
-
-                if (trueMerge == null) {
-                    trueMerge = insertMerge(trueSuccessor(), phi, merge.stateAfter(), tool);
-                    replaceNodesInBranch(coloredNodes, NodeColor.TRUE_BRANCH, phi, trueMerge.phis().first());
-                }
-                trueMerge.phis().first().addInput(value);
-                trueBegin.setNext(graph().add(new EndNode()));
-                trueMerge.addForwardEnd((EndNode) trueBegin.next());
-
-                if (falseMerge == null) {
-                    falseMerge = insertMerge(falseSuccessor(), phi, merge.stateAfter(), tool);
-                    replaceNodesInBranch(coloredNodes, NodeColor.FALSE_BRANCH, phi, falseMerge.phis().first());
-                }
-                falseMerge.phis().first().addInput(value);
-                falseBegin.setNext(graph().add(new EndNode()));
-                falseMerge.addForwardEnd((EndNode) falseBegin.next());
-
-                merge.removeEnd(end);
-                ((FixedWithNextNode) end.predecessor()).setNext(newIfNode);
-                end.safeDelete();
-            }
-        }
-
-        cleanupMerge(merge);
-        cleanupMerge(trueMerge);
-        cleanupMerge(falseMerge);
-
-        return true;
-    }
-
-    private static void replaceNodesInBranch(EconomicMap<Node, NodeColor> coloredNodes, NodeColor branch, ValuePhiNode phi, ValueNode newValue) {
-        for (Node n : phi.usages().snapshot()) {
-            if (coloredNodes.get(n) == branch) {
-                n.replaceAllInputs(phi, newValue);
-            } else if (coloredNodes.get(n) == NodeColor.PHI_MIXED) {
-                assert n instanceof PhiNode;
-                PhiNode phiNode = (PhiNode) n;
-                AbstractMergeNode merge = phiNode.merge();
-                for (int i = 0; i < merge.forwardEndCount(); ++i) {
-                    if (phiNode.valueAt(i) == phi && coloredNodes.get(merge.forwardEndAt(i)) == branch) {
-                        phiNode.setValueAt(i, newValue);
-                    }
-                }
-            }
-        }
-    }
-
-    private NodeColor colorUsage(EconomicMap<Node, NodeColor> coloredNodes, Node node, MergeNode merge, AbstractBeginNode trueSucc, AbstractBeginNode falseSucc) {
-        NodeColor color = coloredNodes.get(node);
-        if (color == null) {
-
-            if (coloredNodes.size() >= MAX_USAGE_COLOR_SET_SIZE) {
-                return NodeColor.MIXED;
-            }
-
-            coloredNodes.put(node, NodeColor.MIXED);
-
-            if (node == merge) {
-                color = NodeColor.MIXED;
-            } else if (node == trueSucc) {
-                color = NodeColor.TRUE_BRANCH;
-            } else if (node == falseSucc) {
-                color = NodeColor.FALSE_BRANCH;
-            } else {
-                if (node instanceof AbstractMergeNode) {
-                    AbstractMergeNode mergeNode = (AbstractMergeNode) node;
-                    NodeColor combinedColor = null;
-                    for (int i = 0; i < mergeNode.forwardEndCount(); ++i) {
-                        NodeColor curColor = colorUsage(coloredNodes, mergeNode.forwardEndAt(i), merge, trueSucc, falseSucc);
-                        if (combinedColor == null) {
-                            combinedColor = curColor;
-                        } else if (combinedColor != curColor) {
-                            combinedColor = NodeColor.MIXED;
-                            break;
-                        }
-                    }
-                    color = combinedColor;
-                } else if (node instanceof StartNode) {
-                    color = NodeColor.MIXED;
-                } else if (node instanceof FixedNode) {
-                    FixedNode fixedNode = (FixedNode) node;
-                    Node predecessor = fixedNode.predecessor();
-                    assert predecessor != null : fixedNode;
-                    color = colorUsage(coloredNodes, predecessor, merge, trueSucc, falseSucc);
-                } else if (node instanceof PhiNode) {
-                    PhiNode phiNode = (PhiNode) node;
-                    AbstractMergeNode phiMerge = phiNode.merge();
-
-                    if (phiMerge instanceof LoopBeginNode) {
-                        color = colorUsage(coloredNodes, phiMerge, merge, trueSucc, falseSucc);
-                    } else {
-
-                        for (int i = 0; i < phiMerge.forwardEndCount(); ++i) {
-                            NodeColor curColor = colorUsage(coloredNodes, phiMerge.forwardEndAt(i), merge, trueSucc, falseSucc);
-                            if (curColor != NodeColor.TRUE_BRANCH && curColor != NodeColor.FALSE_BRANCH) {
-                                color = NodeColor.MIXED;
-                                break;
-                            }
-                        }
-
-                        if (color == null) {
-                            // Each of the inputs to the phi are either coming unambigously from
-                            // true or false branch.
-                            color = NodeColor.PHI_MIXED;
-                            assert node instanceof PhiNode;
-                        }
-                    }
-                } else {
-                    NodeColor combinedColor = null;
-                    for (Node n : node.usages()) {
-                        if (n != node) {
-                            NodeColor curColor = colorUsage(coloredNodes, n, merge, trueSucc, falseSucc);
-                            if (combinedColor == null) {
-                                combinedColor = curColor;
-                            } else if (combinedColor != curColor) {
-                                combinedColor = NodeColor.MIXED;
-                                break;
-                            }
-                        }
-                    }
-                    if (combinedColor == NodeColor.PHI_MIXED) {
-                        combinedColor = NodeColor.MIXED;
-                    }
-                    if (combinedColor == null) {
-                        // Floating node without usages => association unclear.
-                        combinedColor = NodeColor.MIXED;
-                    }
-                    color = combinedColor;
-                }
-            }
-
-            assert color != null : node;
-            coloredNodes.put(node, color);
-        }
-        return color;
-    }
-
-    /**
-     * @param condition
-     * @param phi
-     * @param coloredNodes
-     * @return true if the passed in {@code condition} uses {@code phi} and the condition is only
-     *         used once. Since the phi will go dead the condition using it will also have to be
-     *         dead after the optimization.
-     */
-    private static boolean conditionUses(LogicNode condition, PhiNode phi, EconomicMap<Node, NodeColor> coloredNodes) {
-        if (!condition.hasExactlyOneUsage()) {
-            return false;
-        }
-        if (condition instanceof ShortCircuitOrNode) {
-            if (condition.graph().getGuardsStage().areDeoptsFixed()) {
-                /*
-                 * It can be unsafe to simplify a ShortCircuitOr before deopts are fixed because
-                 * conversion to guards assumes that all the required conditions are being tested.
-                 * Simplfying the condition based on context before this happens may lose a
-                 * condition.
-                 */
-                ShortCircuitOrNode orNode = (ShortCircuitOrNode) condition;
-                return (conditionUses(orNode.x, phi, coloredNodes) || conditionUses(orNode.y, phi, coloredNodes));
-            }
-        } else if (condition instanceof Canonicalizable.Unary<?>) {
-            Canonicalizable.Unary<?> unary = (Canonicalizable.Unary<?>) condition;
-            if (unary.getValue() == phi) {
-                coloredNodes.put(condition, NodeColor.CONDITION_USAGE);
-                return true;
-            }
-        } else if (condition instanceof Canonicalizable.Binary<?>) {
-            Canonicalizable.Binary<?> binary = (Canonicalizable.Binary<?>) condition;
-            if (binary.getX() == phi || binary.getY() == phi) {
-                coloredNodes.put(condition, NodeColor.CONDITION_USAGE);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Canonicalize {@code} condition using {@code value} in place of {@code phi}.
-     *
-     * @param tool
-     * @param condition
-     * @param phi
-     * @param value
-     * @return an improved LogicNode or the original condition
-     */
-    @SuppressWarnings("unchecked")
-    private static LogicNode computeCondition(SimplifierTool tool, LogicNode condition, PhiNode phi, Node value) {
-        if (condition instanceof ShortCircuitOrNode) {
-            if (condition.graph().getGuardsStage().areDeoptsFixed() && !condition.graph().isAfterExpandLogic()) {
-                ShortCircuitOrNode orNode = (ShortCircuitOrNode) condition;
-                LogicNode resultX = computeCondition(tool, orNode.x, phi, value);
-                LogicNode resultY = computeCondition(tool, orNode.y, phi, value);
-                if (resultX != orNode.x || resultY != orNode.y) {
-                    LogicNode result = orNode.canonical(tool, resultX, resultY);
-                    if (result != orNode) {
-                        return result;
-                    }
-                    /*
-                     * Create a new node to carry the optimized inputs.
-                     */
-                    ShortCircuitOrNode newOr = new ShortCircuitOrNode(resultX, orNode.xNegated, resultY,
-                                    orNode.yNegated, orNode.getShortCircuitProbability());
-                    return newOr.canonical(tool);
-                }
-                return orNode;
-            }
-        } else if (condition instanceof Canonicalizable.Binary<?>) {
-            Canonicalizable.Binary<Node> compare = (Canonicalizable.Binary<Node>) condition;
-            if (compare.getX() == phi || compare.getY() == phi) {
-                return (LogicNode) compare.canonical(tool, compare.getX() == phi ? value : compare.getX(), compare.getY() == phi ? value : compare.getY());
-            }
-        } else if (condition instanceof Canonicalizable.Unary<?>) {
-            Canonicalizable.Unary<Node> compare = (Canonicalizable.Unary<Node>) condition;
-            if (compare.getValue() == phi) {
-                return (LogicNode) compare.canonical(tool, value);
-            }
-        }
-        if (condition instanceof Canonicalizable) {
-            return (LogicNode) ((Canonicalizable) condition).canonical(tool);
-        }
-        return condition;
-    }
-
-    private void cleanupMerge(MergeNode merge) {
-        if (merge != null && merge.isAlive()) {
-            if (merge.forwardEndCount() == 0) {
-                GraphUtil.killCFG(merge);
-            } else if (merge.forwardEndCount() == 1) {
-                graph().reduceTrivialMerge(merge);
-            }
-        }
-    }
-
-    @SuppressWarnings("try")
-    private MergeNode insertMerge(AbstractBeginNode begin, ValuePhiNode oldPhi, FrameState stateAfter, SimplifierTool tool) {
-        MergeNode merge = graph().add(new MergeNode());
-
-        AbstractBeginNode newBegin;
-        try (DebugCloseable position = begin.withNodeSourcePosition()) {
-            newBegin = graph().add(new BeginNode());
-            begin.replaceAtPredecessor(newBegin);
-            newBegin.setNext(begin);
-        }
-
-        FixedNode next = newBegin.next();
-        next.replaceAtPredecessor(merge);
-        newBegin.setNext(graph().add(new EndNode()));
-        merge.addForwardEnd((EndNode) newBegin.next());
-
-        ValuePhiNode phi = begin.graph().addOrUnique(new ValuePhiNode(oldPhi.stamp(NodeView.DEFAULT), merge));
-        phi.addInput(oldPhi);
-
-        if (stateAfter != null) {
-            FrameState newState = stateAfter.duplicate();
-            newState.replaceAllInputs(oldPhi, phi);
-            merge.setStateAfter(newState);
-        }
-        merge.setNext(next);
-        tool.addToWorkList(begin);
-        return merge;
-    }
-
-    /**
      * Tries to connect code that initializes a variable directly with the successors of an if
      * construct that switches on the variable. For example, the pseudo code below:
      *
@@ -1709,7 +1307,7 @@
             return false;
         }
 
-        if (!mayRemoveSplit(merge)) {
+        if (merge.stateAfter() != null && !GraphUtil.mayRemoveSplit(this)) {
             return false;
         }
 
@@ -1771,15 +1369,6 @@
         return true;
     }
 
-    private boolean mayRemoveSplit(AbstractMergeNode merge) {
-
-        if (merge.stateAfter() != null && (!checkFrameState(trueSuccessor, MAX_FRAMESTATE_SEARCH_DEPTH) || !checkFrameState(trueSuccessor, MAX_FRAMESTATE_SEARCH_DEPTH))) {
-            return false;
-        }
-
-        return true;
-    }
-
     private static void propagateZeroProbability(FixedNode startNode) {
         Node prev = null;
         for (FixedNode node : GraphUtil.predecessorIterable(startNode)) {
@@ -1817,53 +1406,6 @@
     }
 
     /**
-     * Snippet lowerings may produce patterns without a frame state on the merge. We need to take
-     * extra care when optimizing these patterns.
-     */
-    private static boolean checkFrameState(FixedNode start, int maxDepth) {
-        if (maxDepth == 0) {
-            return false;
-        }
-        FixedNode node = start;
-        while (true) {
-            if (node instanceof AbstractMergeNode) {
-                AbstractMergeNode mergeNode = (AbstractMergeNode) node;
-                if (mergeNode.stateAfter() == null) {
-                    return false;
-                } else {
-                    return true;
-                }
-            } else if (node instanceof StateSplit) {
-                StateSplit stateSplitNode = (StateSplit) node;
-                if (stateSplitNode.stateAfter() != null) {
-                    return true;
-                }
-            }
-
-            if (node instanceof ControlSplitNode) {
-                ControlSplitNode controlSplitNode = (ControlSplitNode) node;
-                for (Node succ : controlSplitNode.cfgSuccessors()) {
-                    if (checkFrameState((FixedNode) succ, maxDepth - 1)) {
-                        return true;
-                    }
-                }
-                return false;
-            } else if (node instanceof FixedWithNextNode) {
-                FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) node;
-                node = fixedWithNextNode.next();
-            } else if (node instanceof AbstractEndNode) {
-                AbstractEndNode endNode = (AbstractEndNode) node;
-                node = endNode.merge();
-            } else if (node instanceof ControlSinkNode) {
-                return true;
-            } else {
-                assert false : "unexpected node";
-                return false;
-            }
-        }
-    }
-
-    /**
      * Connects a set of ends to a given successor, inserting a merge node if there is more than one
      * end. If {@code ends} is not empty, then {@code successor} is added to {@code tool}'s
      * {@linkplain SimplifierTool#addToWorkList(org.graalvm.compiler.graph.Node) work list}.
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -109,7 +109,7 @@
         this.bci = invoke.bci;
         this.polymorphic = invoke.polymorphic;
         this.useForInlining = invoke.useForInlining;
-        this.identity = invoke.getLocationIdentity();
+        this.identity = invoke.getKilledLocationIdentity();
     }
 
     @Override
@@ -181,7 +181,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return identity;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeWithExceptionNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InvokeWithExceptionNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -159,7 +159,7 @@
     @Override
     public void setNext(FixedNode x) {
         if (x != null) {
-            this.setNext(KillingBeginNode.begin(x, getLocationIdentity()));
+            this.setNext(KillingBeginNode.begin(x, this.getKilledLocationIdentity()));
         } else {
             this.setNext(null);
         }
@@ -192,7 +192,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
@@ -290,7 +290,7 @@
      * code.
      */
     public InvokeNode replaceWithInvoke() {
-        InvokeNode newInvoke = graph().add(new InvokeNode(callTarget, bci, stamp, getLocationIdentity()));
+        InvokeNode newInvoke = graph().add(new InvokeNode(callTarget, bci, stamp, this.getKilledLocationIdentity()));
         newInvoke.setStateAfter(stateAfter);
         newInvoke.setStateDuring(stateDuring);
         AbstractBeginNode oldException = this.exceptionEdge;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/KillingBeginNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/KillingBeginNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -58,7 +58,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return locationIdentity;
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -141,7 +141,7 @@
     }
 
     public void setLoopFrequency(double loopFrequency) {
-        assert loopFrequency >= 0;
+        assert loopFrequency >= 1.0;
         this.loopFrequency = loopFrequency;
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/MemoryProxyNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019, 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;
+
+import org.graalvm.compiler.core.common.type.StampFactory;
+import org.graalvm.compiler.graph.NodeClass;
+import org.graalvm.compiler.nodeinfo.InputType;
+import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.memory.MemoryNode;
+import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
+import jdk.internal.vm.compiler.word.LocationIdentity;
+
+@NodeInfo(allowedUsageTypes = {InputType.Memory}, nameTemplate = "MemoryProxy({i#value})")
+public final class MemoryProxyNode extends ProxyNode implements MemoryNode {
+
+    public static final NodeClass<MemoryProxyNode> TYPE = NodeClass.create(MemoryProxyNode.class);
+    @OptionalInput(InputType.Memory) MemoryNode value;
+    protected final LocationIdentity locationIdentity;
+
+    public MemoryProxyNode(MemoryNode value, LoopExitNode proxyPoint, LocationIdentity locationIdentity) {
+        super(TYPE, StampFactory.forVoid(), proxyPoint);
+        this.value = value;
+        this.locationIdentity = locationIdentity;
+    }
+
+    public void setValue(MemoryNode newValue) {
+        this.updateUsages(value.asNode(), newValue.asNode());
+        this.value = newValue;
+    }
+
+    @Override
+    public ValueNode value() {
+        return (value == null ? null : value.asNode());
+    }
+
+    @Override
+    public PhiNode createPhi(AbstractMergeNode merge) {
+        return graph().addWithoutUnique(new MemoryPhiNode(merge, locationIdentity));
+    }
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ProxyNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ProxyNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,18 +29,24 @@
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0;
 
 import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.Node.ValueNumberable;
+import org.graalvm.compiler.graph.spi.Canonicalizable;
+import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.extended.GuardingNode;
+import org.graalvm.compiler.nodes.memory.MemoryNode;
+import org.graalvm.compiler.nodes.spi.Proxy;
+import jdk.internal.vm.compiler.word.LocationIdentity;
 
 /**
  * A proxy is inserted at loop exits for any value that is created inside the loop (i.e. was not
  * live on entry to the loop) and is (potentially) used after the loop.
  */
 @NodeInfo(cycles = CYCLES_0, size = SIZE_0)
-public abstract class ProxyNode extends FloatingNode implements ValueNumberable {
+public abstract class ProxyNode extends FloatingNode implements Proxy, ValueNumberable, Canonicalizable {
 
     public static final NodeClass<ProxyNode> TYPE = NodeClass.create(ProxyNode.class);
     @Input(Association) LoopExitNode loopExit;
@@ -63,17 +69,34 @@
     }
 
     @Override
+    public ValueNode getOriginalNode() {
+        return value();
+    }
+
+    @Override
     public boolean verify() {
         assert !(value() instanceof ProxyNode) || ((ProxyNode) value()).loopExit != loopExit;
         return super.verify();
     }
 
-    public static ValueProxyNode forValue(ValueNode value, LoopExitNode exit, StructuredGraph graph) {
-        return graph.unique(new ValueProxyNode(value, exit));
+    public static ValueProxyNode forValue(ValueNode value, LoopExitNode exit) {
+        return exit.graph().unique(new ValueProxyNode(value, exit));
+    }
+
+    public static GuardProxyNode forGuard(GuardingNode value, LoopExitNode exit) {
+        return exit.graph().unique(new GuardProxyNode(value, exit));
     }
 
-    public static GuardProxyNode forGuard(GuardingNode value, LoopExitNode exit, StructuredGraph graph) {
-        return graph.unique(new GuardProxyNode(value, exit));
+    public static MemoryProxyNode forMemory(MemoryNode value, LoopExitNode exit, LocationIdentity locationIdentity) {
+        return exit.graph().unique(new MemoryProxyNode(value, exit, locationIdentity));
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (value() == null) {
+            return null;
+        }
+        return this;
     }
 
     public abstract PhiNode createPhi(AbstractMergeNode merge);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StartNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StartNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -49,7 +49,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java	Mon Nov 04 11:25:55 2019 +0000
@@ -125,6 +125,10 @@
         public boolean areDeoptsFixed() {
             return this.ordinal() >= FIXED_DEOPTS.ordinal();
         }
+
+        public boolean requiresValueProxies() {
+            return this != AFTER_FSA;
+        }
     }
 
     /**
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -35,7 +35,7 @@
 import org.graalvm.compiler.nodes.spi.VirtualizerTool;
 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
 
-@NodeInfo(nameTemplate = "Proxy({i#value})")
+@NodeInfo(nameTemplate = "ValueProxy({i#value})")
 public final class ValueProxyNode extends ProxyNode implements Canonicalizable, Virtualizable, ValueProxy {
 
     public static final NodeClass<ValueProxyNode> TYPE = NodeClass.create(ValueProxyNode.class);
@@ -65,8 +65,13 @@
 
     @Override
     public Node canonical(CanonicalizerTool tool) {
+        Node result = super.canonical(tool);
+        if (result != this) {
+            return result;
+        }
+
         ValueNode curValue = value;
-        if (curValue.isConstant()) {
+        if (curValue.getNodeClass().isLeafNode()) {
             return curValue;
         }
         if (loopPhiProxy && !loopExit.loopBegin().isPhiAtMerge(curValue)) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -28,7 +28,6 @@
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp;
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
-import org.graalvm.compiler.core.common.type.PrimitiveStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.Canonicalizable.BinaryCommutative;
@@ -41,7 +40,6 @@
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 
-import jdk.vm.ci.code.CodeUtil;
 import jdk.vm.ci.meta.Constant;
 import jdk.vm.ci.meta.PrimitiveConstant;
 
@@ -61,7 +59,7 @@
         if (tryConstantFold != null) {
             return tryConstantFold;
         }
-        return canonical(null, op, stamp, x, y, view);
+        return canonical(null, op, x, y, view);
     }
 
     @Override
@@ -77,16 +75,29 @@
         }
 
         NodeView view = NodeView.from(tool);
-        return canonical(this, getOp(forX, forY), stamp(view), forX, forY, view);
+        return canonical(this, getOp(forX, forY), forX, forY, view);
     }
 
-    private static ValueNode canonical(AndNode self, BinaryOp<And> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
+    private static ValueNode canonical(AndNode self, BinaryOp<And> op, ValueNode forX, ValueNode forY, NodeView view) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return forX;
         }
         if (forX.isConstant() && !forY.isConstant()) {
             return new AndNode(forY, forX);
         }
+
+        Stamp rawXStamp = forX.stamp(view);
+        Stamp rawYStamp = forY.stamp(view);
+        if (rawXStamp instanceof IntegerStamp && rawYStamp instanceof IntegerStamp) {
+            IntegerStamp xStamp = (IntegerStamp) rawXStamp;
+            IntegerStamp yStamp = (IntegerStamp) rawYStamp;
+            if (((~xStamp.downMask()) & yStamp.upMask()) == 0) {
+                return forY;
+            } else if (((~yStamp.downMask()) & xStamp.upMask()) == 0) {
+                return forX;
+            }
+        }
+
         if (forY.isConstant()) {
             Constant c = forY.asConstant();
             if (op.isNeutral(c)) {
@@ -95,21 +106,12 @@
 
             if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
                 long rawY = ((PrimitiveConstant) c).asLong();
-                long mask = CodeUtil.mask(PrimitiveStamp.getBits(stamp));
-                if ((rawY & mask) == 0) {
-                    return ConstantNode.forIntegerStamp(stamp, 0);
-                }
                 if (forX instanceof SignExtendNode) {
                     SignExtendNode ext = (SignExtendNode) forX;
                     if (rawY == ((1L << ext.getInputBits()) - 1)) {
                         return new ZeroExtendNode(ext.getValue(), ext.getResultBits());
                     }
                 }
-                IntegerStamp xStamp = (IntegerStamp) forX.stamp(view);
-                if (((xStamp.upMask() | xStamp.downMask()) & ~rawY) == 0) {
-                    // No bits are set which are outside the mask, so the mask will have no effect.
-                    return forX;
-                }
             }
 
             return reassociate(self != null ? self : (AndNode) new AndNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -59,6 +59,10 @@
         super(c, opForStampComputation.foldStamp(x.stamp(NodeView.DEFAULT), y.stamp(NodeView.DEFAULT)), x, y);
     }
 
+    protected BinaryArithmeticNode(NodeClass<? extends BinaryArithmeticNode<OP>> c, Stamp stamp, ValueNode x, ValueNode y) {
+        super(c, stamp, x, y);
+    }
+
     public static ArithmeticOpTable getArithmeticOpTable(ValueNode forValue) {
         return ArithmeticOpTable.forStamp(forValue.stamp(NodeView.DEFAULT));
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -33,8 +33,11 @@
 import org.graalvm.compiler.core.common.type.AbstractObjectStamp;
 import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
+import org.graalvm.compiler.graph.Position;
 import org.graalvm.compiler.graph.spi.Canonicalizable;
+import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.BinaryOpLogicNode;
 import org.graalvm.compiler.nodes.ConstantNode;
@@ -44,6 +47,7 @@
 import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.memory.VolatileReadNode;
 import org.graalvm.compiler.options.OptionValues;
 
 import jdk.vm.ci.meta.Constant;
@@ -189,6 +193,19 @@
             } else if (nonConstant instanceof ConvertNode) {
                 ConvertNode convert = (ConvertNode) nonConstant;
                 boolean multiUsage = (convert.asNode().hasMoreThanOneUsage() && convert.getValue().hasExactlyOneUsage());
+                if (!multiUsage && convert.asNode().hasMoreThanOneUsage() && convert.getValue() instanceof VolatileReadNode) {
+                    // Only account for data usages
+                    VolatileReadNode read = (VolatileReadNode) convert.getValue();
+                    int nonMemoryEdges = 0;
+                    for (Node u : read.usages()) {
+                        for (Position pos : u.inputPositions()) {
+                            if (pos.get(u) == read && pos.getInputType() != InputType.Memory) {
+                                nonMemoryEdges++;
+                            }
+                        }
+                    }
+                    multiUsage = nonMemoryEdges == 1;
+                }
                 if (convert instanceof IntegerConvertNode && multiUsage) {
                     // Do not perform for integer convers if it could introduce
                     // new live values.
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -27,7 +27,7 @@
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp;
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Or;
-import org.graalvm.compiler.core.common.type.PrimitiveStamp;
+import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.Canonicalizable.BinaryCommutative;
@@ -40,9 +40,7 @@
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 
-import jdk.vm.ci.code.CodeUtil;
 import jdk.vm.ci.meta.Constant;
-import jdk.vm.ci.meta.PrimitiveConstant;
 
 @NodeInfo(shortName = "|")
 public final class OrNode extends BinaryArithmeticNode<Or> implements BinaryCommutative<ValueNode>, NarrowableArithmeticNode {
@@ -53,6 +51,18 @@
         super(TYPE, getArithmeticOpTable(x).getOr(), x, y);
     }
 
+    private OrNode(ValueNode x, ValueNode y, Stamp forcedStamp) {
+        super(TYPE, forcedStamp, x, y);
+    }
+
+    /**
+     * Create a new XorNode with a forced stamp, without eager folding. This should only be used in
+     * snippet code, where native-image may assign wrong stamps during graph generation.
+     */
+    public static ValueNode createForSnippet(ValueNode x, ValueNode y, Stamp forcedStamp) {
+        return new OrNode(x, y, forcedStamp);
+    }
+
     public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
         BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp(view)).getOr();
         Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view));
@@ -60,7 +70,7 @@
         if (tryConstantFold != null) {
             return tryConstantFold;
         }
-        return canonical(null, op, stamp, x, y, view);
+        return canonical(null, op, x, y, view);
     }
 
     @Override
@@ -76,34 +86,42 @@
             return ret;
         }
 
-        return canonical(this, getOp(forX, forY), stamp(view), forX, forY, view);
+        return canonical(this, getOp(forX, forY), forX, forY, view);
     }
 
-    private static ValueNode canonical(OrNode self, BinaryOp<Or> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
+    private static ValueNode canonical(OrNode self, BinaryOp<Or> op, ValueNode forX, ValueNode forY, NodeView view) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return forX;
         }
         if (forX.isConstant() && !forY.isConstant()) {
             return new OrNode(forY, forX);
         }
+
+        Stamp rawXStamp = forX.stamp(view);
+        Stamp rawYStamp = forY.stamp(view);
+        if (rawXStamp instanceof IntegerStamp && rawYStamp instanceof IntegerStamp) {
+            IntegerStamp xStamp = (IntegerStamp) rawXStamp;
+            IntegerStamp yStamp = (IntegerStamp) rawYStamp;
+            if (((~xStamp.downMask()) & yStamp.upMask()) == 0) {
+                return forX;
+            } else if (((~yStamp.downMask()) & xStamp.upMask()) == 0) {
+                return forY;
+            }
+        }
+
         if (forY.isConstant()) {
             Constant c = forY.asConstant();
             if (op.isNeutral(c)) {
                 return forX;
             }
 
-            if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
-                long rawY = ((PrimitiveConstant) c).asLong();
-                long mask = CodeUtil.mask(PrimitiveStamp.getBits(stamp));
-                if ((rawY & mask) == mask) {
-                    return ConstantNode.forIntegerStamp(stamp, mask);
-                }
-            }
             return reassociate(self != null ? self : (OrNode) new OrNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view);
         }
+
         if (forX instanceof NotNode && forY instanceof NotNode) {
             return new NotNode(AndNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue(), view));
         }
+
         return self != null ? self : new OrNode(forX, forY).maybeCommuteInputs();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -54,6 +54,18 @@
         assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT));
     }
 
+    private XorNode(ValueNode x, ValueNode y, Stamp forcedStamp) {
+        super(TYPE, forcedStamp, x, y);
+    }
+
+    /**
+     * Create a new XorNode with a forced stamp, without eager folding. This should only be used in
+     * snippet code, where native-image may assign wrong stamps during graph generation.
+     */
+    public static ValueNode createForSnippet(ValueNode x, ValueNode y, Stamp forcedStamp) {
+        return new XorNode(x, y, forcedStamp);
+    }
+
     public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
         BinaryOp<Xor> op = ArithmeticOpTable.forStamp(x.stamp(view)).getXor();
         Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view));
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/Block.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/Block.java	Mon Nov 04 11:25:55 2019 +0000
@@ -260,10 +260,10 @@
         LocationSet result = new LocationSet();
         for (FixedNode node : this.getNodes()) {
             if (node instanceof MemoryCheckpoint.Single) {
-                LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity();
+                LocationIdentity identity = ((MemoryCheckpoint.Single) node).getKilledLocationIdentity();
                 result.add(identity);
             } else if (node instanceof MemoryCheckpoint.Multi) {
-                for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) {
+                for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getKilledLocationIdentities()) {
                     result.add(identity);
                 }
             }
@@ -365,4 +365,26 @@
     protected void setPostDominator(Block postdominator) {
         this.postdominator = postdominator;
     }
+
+    /**
+     * Checks whether {@code this} block is in the same loop or an outer loop of the block given as
+     * parameter.
+     */
+    public boolean isInSameOrOuterLoopOf(Block block) {
+
+        if (this.loop == null) {
+            // We are in no loop, so this holds true for every other block.
+            return true;
+        }
+
+        Loop<Block> l = block.loop;
+        while (l != null) {
+            if (l == this.loop) {
+                return true;
+            }
+            l = l.getParent();
+        }
+
+        return false;
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/cfg/ControlFlowGraph.java	Mon Nov 04 11:25:55 2019 +0000
@@ -38,6 +38,7 @@
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeMap;
+import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.AbstractEndNode;
 import org.graalvm.compiler.nodes.ControlSinkNode;
@@ -416,6 +417,15 @@
         return nodeToBlock.get(node);
     }
 
+    public Block commonDominatorFor(NodeIterable<? extends Node> nodes) {
+        Block commonDom = null;
+        for (Node n : nodes) {
+            Block b = blockFor(n);
+            commonDom = (Block) AbstractControlFlowGraph.commonDominator(commonDom, b);
+        }
+        return commonDom;
+    }
+
     @Override
     public List<Loop<Block>> getLoops() {
         return loops;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/StringToBytesNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/StringToBytesNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -66,7 +66,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return NamedLocationIdentity.getArrayLocation(JavaKind.Byte);
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BytecodeExceptionNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BytecodeExceptionNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -100,7 +100,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/ForeignCallNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/ForeignCallNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -159,7 +159,7 @@
     }
 
     @Override
-    public LocationIdentity[] getLocationIdentities() {
+    public LocationIdentity[] getKilledLocationIdentities() {
         return foreignCalls.getKilledLocations(descriptor);
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/JavaWriteNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/JavaWriteNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -79,4 +79,9 @@
     public Stamp getAccessStamp() {
         return StampFactory.forKind(writeKind);
     }
+
+    @Override
+    public LocationIdentity getKilledLocationIdentity() {
+        return getLocationIdentity();
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/MembarNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/MembarNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -58,7 +58,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return location;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -161,7 +161,7 @@
 
     @Override
     protected ValueNode cloneAsFieldAccess(Assumptions assumptions, ResolvedJavaField field, boolean volatileAccess) {
-        return LoadFieldNode.create(assumptions, object(), field, volatileAccess);
+        return LoadFieldNode.create(assumptions, field.isStatic() ? null : object(), field, volatileAccess);
     }
 
     @Override
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawStoreNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawStoreNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -134,7 +134,7 @@
 
     @Override
     protected ValueNode cloneAsFieldAccess(Assumptions assumptions, ResolvedJavaField field, boolean volatileAccess) {
-        return new StoreFieldNode(object(), field, value(), stateAfter(), volatileAccess);
+        return new StoreFieldNode(field.isStatic() ? null : object(), field, value(), stateAfter(), volatileAccess);
     }
 
     @Override
@@ -145,4 +145,9 @@
     public FrameState getState() {
         return stateAfter;
     }
+
+    @Override
+    public LocationIdentity getKilledLocationIdentity() {
+        return getLocationIdentity();
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -290,4 +290,7 @@
         }
     }
 
+    public int[] getKeySuccessors() {
+        return keySuccessors.clone();
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/UnsafeAccessNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/UnsafeAccessNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -27,6 +27,8 @@
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
 
+import java.nio.ByteOrder;
+
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -40,6 +42,8 @@
 import jdk.internal.vm.compiler.word.LocationIdentity;
 
 import jdk.vm.ci.meta.Assumptions;
+import jdk.vm.ci.meta.ConstantReflectionProvider;
+import jdk.vm.ci.meta.JavaConstant;
 import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.ResolvedJavaField;
 import jdk.vm.ci.meta.ResolvedJavaType;
@@ -95,10 +99,14 @@
                 // Try to canonicalize to a field access.
                 ResolvedJavaType receiverType = StampTool.typeOrNull(object());
                 if (receiverType != null) {
-                    ResolvedJavaField field = receiverType.findInstanceFieldWithOffset(constantOffset, accessKind());
-                    // No need for checking that the receiver is non-null. The field access includes
-                    // the null check and if a field is found, the offset is so small that this is
-                    // never a valid access of an arbitrary address.
+                    ResolvedJavaField field = getStaticFieldUnsafeAccess(tool.getConstantReflection());
+                    if (field == null) {
+                        field = receiverType.findInstanceFieldWithOffset(constantOffset, accessKind());
+                    }
+
+                    // No need for checking that the receiver is non-null. The field access
+                    // includes the null check and if a field is found, the offset is so small that
+                    // this is never a valid access of an arbitrary address.
                     if (field != null && field.getJavaKind() == this.accessKind()) {
                         assert !graph().isAfterFloatingReadPhase() : "cannot add more precise memory location after floating read phase";
                         // Unsafe accesses never have volatile semantics.
@@ -128,4 +136,58 @@
     protected abstract ValueNode cloneAsFieldAccess(Assumptions assumptions, ResolvedJavaField field, boolean volatileAccess);
 
     protected abstract ValueNode cloneAsArrayAccess(ValueNode location, LocationIdentity identity);
+
+    /**
+     * In this method we check if the unsafe access is to a static field. This is the case when
+     * {@code object} is a constant of type {@link Class} (static field's declaring class) and
+     * {@code offset} is a constant (HotSpot-specific field offset from the declaring class).
+     *
+     * @return the static field, if any, that this node is reading
+     */
+    private ResolvedJavaField getStaticFieldUnsafeAccess(ConstantReflectionProvider constantReflection) {
+        if (!object().isJavaConstant() || !offset().isJavaConstant() ||
+                        object().isNullConstant() || offset().isNullConstant()) {
+            return null;
+        }
+        JavaConstant objectConstant = object().asJavaConstant();
+        JavaConstant offsetConstant = offset().asJavaConstant();
+        assert objectConstant != null && offsetConstant != null : "Verified by the check at the beginning.";
+        ResolvedJavaType staticReceiverType = constantReflection.asJavaType(objectConstant);
+        if (staticReceiverType == null) {
+            // object is not of type Class so it is not a static field
+            return null;
+        }
+        return findStaticFieldWithOffset(staticReceiverType, offsetConstant.asLong(), accessKind);
+    }
+
+    private static ResolvedJavaField findStaticFieldWithOffset(ResolvedJavaType type, long offset, JavaKind expectedEntryKind) {
+        try {
+            ResolvedJavaField[] declaredFields = type.getStaticFields();
+            return findFieldWithOffset(offset, expectedEntryKind, declaredFields);
+        } catch (UnsupportedOperationException e) {
+            return null;
+        }
+    }
+
+    /**
+     * NOTE GR-18873: this is a copy-paste implementation derived from
+     * {@code jdk.vm.ci.hotspot.HotSpotResolvedObjectTypeImpl#findStaticFieldWithOffset}.
+     */
+    private static ResolvedJavaField findFieldWithOffset(long offset, JavaKind expectedEntryKind, ResolvedJavaField[] declaredFields) {
+        for (ResolvedJavaField field : declaredFields) {
+            long resolvedFieldOffset = field.getOffset();
+            if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN &&
+                            expectedEntryKind.isPrimitive() &&
+                            !expectedEntryKind.equals(JavaKind.Void) &&
+                            field.getJavaKind().isPrimitive()) {
+                resolvedFieldOffset += field.getJavaKind().getByteCount() -
+                                Math.min(field.getJavaKind().getByteCount(), 4 + expectedEntryKind.getByteCount());
+            }
+            if (resolvedFieldOffset == offset) {
+                return field;
+            }
+        }
+        return null;
+    }
+
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/UnsafeMemoryStoreNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/UnsafeMemoryStoreNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -77,7 +77,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return locationIdentity;
     }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderConfiguration.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderConfiguration.java	Mon Nov 04 11:25:55 2019 +0000
@@ -203,6 +203,7 @@
     private final boolean trackNodeSourcePosition;
     private final boolean retainLocalVariables;
     private final Plugins plugins;
+    private final boolean replaceLocalsWithConstants;
 
     public enum BytecodeExceptionMode {
         /**
@@ -231,6 +232,7 @@
                     boolean insertFullInfopoints,
                     boolean trackNodeSourcePosition,
                     boolean retainLocalVariables,
+                    boolean replaceLocalsWithConstants,
                     List<ResolvedJavaType> skippedExceptionTypes,
                     Plugins plugins) {
         this.eagerResolving = eagerResolving;
@@ -240,6 +242,7 @@
         this.insertFullInfopoints = insertFullInfopoints;
         this.trackNodeSourcePosition = trackNodeSourcePosition;
         this.retainLocalVariables = retainLocalVariables;
+        this.replaceLocalsWithConstants = replaceLocalsWithConstants;
         this.skippedExceptionTypes = skippedExceptionTypes;
         this.plugins = plugins;
     }
@@ -259,6 +262,7 @@
                         insertFullInfopoints,
                         trackNodeSourcePosition,
                         retainLocalVariables,
+                        replaceLocalsWithConstants,
                         skippedExceptionTypes,
                         newPlugins);
         return result;
@@ -279,6 +283,7 @@
                         insertFullInfopoints,
                         trackNodeSourcePosition,
                         retainLocalVariables,
+                        replaceLocalsWithConstants,
                         skippedExceptionTypes,
                         plugins);
     }
@@ -292,6 +297,7 @@
                         insertFullInfopoints,
                         trackNodeSourcePosition,
                         retainLocalVariables,
+                        replaceLocalsWithConstants,
                         skippedExceptionTypes,
                         plugins);
     }
@@ -305,6 +311,7 @@
                         insertFullInfopoints,
                         trackNodeSourcePosition,
                         retainLocalVariables,
+                        replaceLocalsWithConstants,
                         Collections.unmodifiableList(Arrays.asList(newSkippedExceptionTypes)),
                         plugins);
     }
@@ -317,6 +324,7 @@
                         insertFullInfopoints,
                         trackNodeSourcePosition,
                         retainLocalVariables,
+                        replaceLocalsWithConstants,
                         skippedExceptionTypes,
                         plugins);
     }
@@ -330,6 +338,7 @@
                         insertFullInfopoints,
                         trackNodeSourcePosition,
                         retainLocalVariables,
+                        replaceLocalsWithConstants,
                         skippedExceptionTypes,
                         plugins);
     }
@@ -343,6 +352,7 @@
                         newInsertFullInfopoints,
                         trackNodeSourcePosition,
                         retainLocalVariables,
+                        replaceLocalsWithConstants,
                         skippedExceptionTypes,
                         plugins);
     }
@@ -356,6 +366,7 @@
                         insertFullInfopoints,
                         newTrackNodeSourcePosition,
                         retainLocalVariables,
+                        replaceLocalsWithConstants,
                         skippedExceptionTypes,
                         plugins);
     }
@@ -369,6 +380,21 @@
                         insertFullInfopoints,
                         trackNodeSourcePosition,
                         newRetainLocalVariables,
+                        replaceLocalsWithConstants,
+                        skippedExceptionTypes,
+                        plugins);
+    }
+
+    public GraphBuilderConfiguration withReplaceLocalsWithConstants(boolean newReplaceLocalsWithConstants) {
+        return new GraphBuilderConfiguration(
+                        eagerResolving,
+                        unresolvedIsError,
+                        bytecodeExceptionMode,
+                        omitAssertions,
+                        insertFullInfopoints,
+                        trackNodeSourcePosition,
+                        retainLocalVariables,
+                        newReplaceLocalsWithConstants,
                         skippedExceptionTypes,
                         plugins);
     }
@@ -401,6 +427,10 @@
         return insertFullInfopoints;
     }
 
+    public boolean replaceLocalsWithConstants() {
+        return this.replaceLocalsWithConstants;
+    }
+
     public static GraphBuilderConfiguration getDefault(Plugins plugins) {
         return new GraphBuilderConfiguration(
                         /* eagerResolving: */ false,
@@ -410,6 +440,7 @@
                         /* insertFullInfopoints: */ false,
                         /* trackNodeSourcePosition: */ false,
                         /* retainLocalVariables */ false,
+                        /* replaceLocalsWithConstants */ false,
                         Collections.emptyList(),
                         plugins);
     }
@@ -423,6 +454,7 @@
                         /* insertFullInfopoints: */ false,
                         /* trackNodeSourcePosition: */ false,
                         /* retainLocalVariables */ false,
+                        /* replaceLocalsWithConstants */ false,
                         Collections.emptyList(),
                         plugins);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java	Mon Nov 04 11:25:55 2019 +0000
@@ -46,7 +46,6 @@
 import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap;
 import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor;
 import org.graalvm.compiler.api.replacements.MethodSubstitution;
-import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry;
 import org.graalvm.compiler.bytecode.BytecodeProvider;
 import org.graalvm.compiler.core.common.SuppressFBWarnings;
 import org.graalvm.compiler.debug.Assertions;
@@ -55,6 +54,7 @@
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
+import org.graalvm.compiler.nodes.spi.Replacements;
 
 import jdk.vm.ci.meta.MetaUtil;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -175,21 +175,25 @@
     }
 
     /**
-     * Utility for {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...)
-     * registration} of invocation plugins.
+     * Utility for {@linkplain InvocationPlugins#register registration} of invocation plugins.
      */
-    public static class Registration implements MethodSubstitutionRegistry {
+    public static class Registration {
 
         private final InvocationPlugins plugins;
+
         private final Type declaringType;
-        private final BytecodeProvider methodSubstitutionBytecodeProvider;
+        private final Replacements replacements;
+        private final BytecodeProvider bytecodeProvider;
         private boolean allowOverwrite;
 
-        @Override
         public Class<?> getReceiverType() {
             return Receiver.class;
         }
 
+        public Type getDeclaringType() {
+            return declaringType;
+        }
+
         /**
          * Creates an object for registering {@link InvocationPlugin}s for methods declared by a
          * given class.
@@ -201,7 +205,8 @@
         public Registration(InvocationPlugins plugins, Type declaringType) {
             this.plugins = plugins;
             this.declaringType = declaringType;
-            this.methodSubstitutionBytecodeProvider = null;
+            this.replacements = null;
+            this.bytecodeProvider = null;
         }
 
         /**
@@ -211,13 +216,29 @@
          * @param plugins where to register the plugins
          * @param declaringType the class declaring the methods for which plugins will be registered
          *            via this object
-         * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for
-         *            method substitutions
+         * @param replacements the current Replacements provider
          */
-        public Registration(InvocationPlugins plugins, Type declaringType, BytecodeProvider methodSubstitutionBytecodeProvider) {
+        public Registration(InvocationPlugins plugins, Type declaringType, Replacements replacements) {
             this.plugins = plugins;
             this.declaringType = declaringType;
-            this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider;
+            this.replacements = replacements;
+            this.bytecodeProvider = replacements != null ? replacements.getDefaultReplacementBytecodeProvider() : null;
+        }
+
+        /**
+         * Creates an object for registering {@link InvocationPlugin}s for methods declared by a
+         * given class.
+         *
+         * @param plugins where to register the plugins
+         * @param declaringType the class declaring the methods for which plugins will be registered
+         *            via this object
+         * @param replacements the current Replacements provider
+         */
+        public Registration(InvocationPlugins plugins, Type declaringType, Replacements replacements, BytecodeProvider bytecodeProvider) {
+            this.plugins = plugins;
+            this.declaringType = declaringType;
+            this.replacements = replacements;
+            this.bytecodeProvider = bytecodeProvider;
         }
 
         /**
@@ -231,7 +252,8 @@
         public Registration(InvocationPlugins plugins, String declaringClassName) {
             this.plugins = plugins;
             this.declaringType = new OptionalLazySymbol(declaringClassName);
-            this.methodSubstitutionBytecodeProvider = null;
+            this.replacements = null;
+            this.bytecodeProvider = null;
         }
 
         /**
@@ -241,13 +263,13 @@
          * @param plugins where to register the plugins
          * @param declaringClassName the name of the class class declaring the methods for which
          *            plugins will be registered via this object
-         * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for
-         *            method substitutions
+         * @param replacements the current Replacements provider
          */
-        public Registration(InvocationPlugins plugins, String declaringClassName, BytecodeProvider methodSubstitutionBytecodeProvider) {
+        public Registration(InvocationPlugins plugins, String declaringClassName, Replacements replacements) {
             this.plugins = plugins;
             this.declaringType = new OptionalLazySymbol(declaringClassName);
-            this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider;
+            this.replacements = replacements;
+            this.bytecodeProvider = replacements != null ? replacements.getDefaultReplacementBytecodeProvider() : null;
         }
 
         /**
@@ -339,6 +361,118 @@
         }
 
         /**
+         * Registers a plugin for a method with no arguments that is conditionally enabled. This
+         * ensures that {@code Replacements} is aware of this plugin.
+         *
+         * @param name the name of the method
+         * @param plugin the plugin to be registered
+         */
+        public void registerConditional0(boolean isEnabled, String name, InvocationPlugin plugin) {
+            replacements.registerConditionalPlugin(plugin);
+            if (isEnabled) {
+                plugins.register(plugin, false, allowOverwrite, declaringType, name);
+            }
+        }
+
+        /**
+         * Registers a plugin for a method with 1 argument that is conditionally enabled. This
+         * ensures that {@code Replacements} is aware of this plugin.
+         *
+         * @param name the name of the method
+         * @param plugin the plugin to be registered
+         */
+        public void registerConditional1(boolean isEnabled, String name, Type arg, InvocationPlugin plugin) {
+            replacements.registerConditionalPlugin(plugin);
+            if (isEnabled) {
+                plugins.register(plugin, false, allowOverwrite, declaringType, name, arg);
+            }
+        }
+
+        /**
+         * Registers a plugin for a method with 2 arguments that is conditionally enabled. This
+         * ensures that {@code Replacements} is aware of this plugin.
+         *
+         * @param name the name of the method
+         * @param plugin the plugin to be registered
+         */
+        public void registerConditional2(boolean isEnabled, String name, Type arg1, Type arg2, InvocationPlugin plugin) {
+            replacements.registerConditionalPlugin(plugin);
+            if (isEnabled) {
+                plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2);
+            }
+        }
+
+        /**
+         * Registers a plugin for a method with 3 arguments that is conditionally enabled. This
+         * ensures that {@code Replacements} is aware of this plugin.
+         *
+         * @param name the name of the method
+         * @param plugin the plugin to be registered
+         */
+        public void registerConditional3(boolean isEnabled, String name, Type arg1, Type arg2, Type arg3, InvocationPlugin plugin) {
+            replacements.registerConditionalPlugin(plugin);
+            if (isEnabled) {
+                plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3);
+            }
+        }
+
+        /**
+         * Registers a plugin for a method with 4 arguments that is conditionally enabled. This
+         * ensures that {@code Replacements} is aware of this plugin.
+         *
+         * @param name the name of the method
+         * @param plugin the plugin to be registered
+         */
+        public void registerConditional4(boolean isEnabled, String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) {
+            replacements.registerConditionalPlugin(plugin);
+            if (isEnabled) {
+                plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4);
+            }
+        }
+
+        /**
+         * Registers a plugin for a method with 5 arguments that is conditionally enabled. This
+         * ensures that {@code Replacements} is aware of this plugin.
+         *
+         * @param name the name of the method
+         * @param plugin the plugin to be registered
+         */
+        public void registerConditional5(boolean isEnabled, String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, InvocationPlugin plugin) {
+            replacements.registerConditionalPlugin(plugin);
+            if (isEnabled) {
+                plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5);
+            }
+        }
+
+        /**
+         * Registers a plugin for a method with 6 arguments that is conditionally enabled. This
+         * ensures that {@code Replacements} is aware of this plugin.
+         *
+         * @param name the name of the method
+         * @param plugin the plugin to be registered
+         */
+        public void registerConditional6(boolean isEnabled, String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, InvocationPlugin plugin) {
+            replacements.registerConditionalPlugin(plugin);
+            if (isEnabled) {
+                plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6);
+            }
+        }
+
+        /**
+         * Registers a plugin for a method with 7 arguments that is conditionally enabled. This
+         * ensures that {@code Replacements} is aware of this plugin.
+         *
+         * @param name the name of the method
+         * @param plugin the plugin to be registered
+         */
+        public void registerConditional7(boolean isEnabled, String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, Type arg7, InvocationPlugin plugin) {
+            replacements.registerConditionalPlugin(plugin);
+            if (isEnabled) {
+                plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+            }
+        }
+
+        /**
          * Registers a plugin for an optional method with no arguments.
          *
          * @param name the name of the method
@@ -398,7 +532,6 @@
          *            is non-static. Upon returning, element 0 will have been rewritten to
          *            {@code declaringClass}
          */
-        @Override
         public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Type... argumentTypes) {
             registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes);
         }
@@ -407,25 +540,63 @@
          * Registers a plugin that implements a method based on the bytecode of a substitute method.
          *
          * @param substituteDeclaringClass the class declaring the substitute method
-         * @param name the name of both the original method
+         * @param name the name of the original method
          * @param substituteName the name of the substitute method
          * @param argumentTypes the argument types of the method. Element 0 of this array must be
          *            the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
          *            is non-static. Upon returning, element 0 will have been rewritten to
          *            {@code declaringClass}
          */
-        @Override
         public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) {
-            MethodSubstitutionPlugin plugin = createMethodSubstitution(substituteDeclaringClass, substituteName, argumentTypes);
-            plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes);
+            doMethodSubstitutionRegistration(false, true, substituteDeclaringClass, name, substituteName, argumentTypes);
+        }
+
+        /**
+         * Registers a plugin that implements a method based on the bytecode of a substitute method
+         * that is conditinally enabled. This ensures that {@code Replacements} is aware of this
+         * plugin.
+         *
+         * @param isEnabled whether the plugin is enabled in the current compiler
+         * @param substituteDeclaringClass the class declaring the substitute method
+         * @param name the name of both the original and substitute method
+         * @param argumentTypes the argument types of the method. Element 0 of this array must be
+         *            the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
+         *            is non-static. Upon returning, element 0 will have been rewritten to
+         *            {@code declaringClass}
+         */
+        public void registerConditionalMethodSubstitution(boolean isEnabled, Class<?> substituteDeclaringClass, String name, Type... argumentTypes) {
+            registerConditionalMethodSubstitution(isEnabled, substituteDeclaringClass, name, name, argumentTypes);
         }
 
-        public MethodSubstitutionPlugin createMethodSubstitution(Class<?> substituteDeclaringClass, String substituteName, Type... argumentTypes) {
-            assert methodSubstitutionBytecodeProvider != null : "Registration used for method substitutions requires a non-null methodSubstitutionBytecodeProvider";
-            MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(methodSubstitutionBytecodeProvider, substituteDeclaringClass, substituteName, argumentTypes);
-            return plugin;
+        /**
+         * Registers a plugin that implements a method based on the bytecode of a substitute method
+         * that is conditinally enabled. This ensures that {@code Replacements} is aware of this
+         * plugin.
+         *
+         * @param isEnabled whether the plugin is enabled in the current compiler
+         * @param substituteDeclaringClass the class declaring the substitute method
+         * @param name the name of the original method
+         * @param substituteName the name of the substitute method
+         * @param argumentTypes the argument types of the method. Element 0 of this array must be
+         *            the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
+         *            is non-static. Upon returning, element 0 will have been rewritten to
+         *            {@code declaringClass}
+         */
+        public void registerConditionalMethodSubstitution(boolean isEnabled, Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) {
+            doMethodSubstitutionRegistration(true, isEnabled, substituteDeclaringClass, name, substituteName, argumentTypes);
         }
 
+        private void doMethodSubstitutionRegistration(boolean isConditional, boolean isEnabled, Class<?> substituteDeclaringClass, String name, String substituteName, Type[] argumentTypes) {
+            MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(this, bytecodeProvider, name, substituteDeclaringClass, substituteName, argumentTypes);
+            replacements.registerMethodSubstitution(plugin);
+            if (isConditional) {
+                // Notify Replacements about the plugin even if it's not current enabled
+                replacements.registerConditionalPlugin(plugin);
+            }
+            if (isEnabled) {
+                plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes);
+            }
+        }
     }
 
     /**
@@ -519,13 +690,7 @@
             this.plugin = data;
             this.isStatic = isStatic;
             this.name = name;
-            StringBuilder buf = new StringBuilder();
-            buf.append('(');
-            for (int i = isStatic ? 0 : 1; i < argumentTypes.length; i++) {
-                buf.append(MetaUtil.toInternalName(argumentTypes[i].getTypeName()));
-            }
-            buf.append(')');
-            this.argumentsDescriptor = buf.toString();
+            this.argumentsDescriptor = toArgumentDescriptor(isStatic, argumentTypes);
             assert !name.equals("<init>") || !isStatic : this;
         }
 
@@ -546,6 +711,16 @@
         }
     }
 
+    static String toArgumentDescriptor(boolean isStatic, Type[] argumentTypes) {
+        StringBuilder buf = new StringBuilder();
+        buf.append('(');
+        for (int i = isStatic ? 0 : 1; i < argumentTypes.length; i++) {
+            buf.append(MetaUtil.toInternalName(argumentTypes[i].getTypeName()));
+        }
+        buf.append(')');
+        return buf.toString();
+    }
+
     /**
      * Plugin registrations for already resolved methods. If non-null, then {@link #registrations}
      * is null and no further registrations can be made.
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/MethodSubstitutionPlugin.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/MethodSubstitutionPlugin.java	Mon Nov 04 11:25:55 2019 +0000
@@ -42,6 +42,7 @@
 
 import jdk.vm.ci.meta.MetaAccessProvider;
 import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
 
 /**
  * An {@link InvocationPlugin} for a method where the implementation of the method is provided by a
@@ -56,6 +57,8 @@
  */
 public final class MethodSubstitutionPlugin implements InvocationPlugin {
 
+    private InvocationPlugins.Registration registration;
+
     private ResolvedJavaMethod cachedSubstitute;
 
     /**
@@ -64,9 +67,14 @@
     private final Class<?> declaringClass;
 
     /**
-     * The name of the original and substitute method.
+     * The name of the substitute method.
      */
-    private final String name;
+    private final String substituteName;
+
+    /**
+     * The name of the original method.
+     */
+    private final String originalName;
 
     /**
      * The parameter types of the substitute method.
@@ -81,16 +89,21 @@
      * Creates a method substitution plugin.
      *
      * @param bytecodeProvider used to get the bytecodes to parse for the substitute method
+     * @param originalName the name of the original method
      * @param declaringClass the class in which the substitute method is declared
-     * @param name the name of the substitute method
+     * @param substituteName the name of the substitute method
      * @param parameters the parameter types of the substitute method. If the original method is not
      *            static, then {@code parameters[0]} must be the {@link Class} value denoting
      *            {@link InvocationPlugin.Receiver}
      */
-    public MethodSubstitutionPlugin(BytecodeProvider bytecodeProvider, Class<?> declaringClass, String name, Type... parameters) {
+    public MethodSubstitutionPlugin(InvocationPlugins.Registration registration, BytecodeProvider bytecodeProvider, String originalName, Class<?> declaringClass, String substituteName,
+                    Type... parameters) {
+        assert bytecodeProvider != null : "Requires a non-null methodSubstitutionBytecodeProvider";
+        this.registration = registration;
         this.bytecodeProvider = bytecodeProvider;
+        this.originalName = originalName;
         this.declaringClass = declaringClass;
-        this.name = name;
+        this.substituteName = substituteName;
         this.parameters = parameters;
         this.originalIsStatic = parameters.length == 0 || parameters[0] != InvocationPlugin.Receiver.class;
     }
@@ -144,7 +157,7 @@
      * Determines if a given method is the substitute method of this plugin.
      */
     private boolean isSubstitute(Method m) {
-        if (Modifier.isStatic(m.getModifiers()) && m.getName().equals(name)) {
+        if (Modifier.isStatic(m.getModifiers()) && m.getName().equals(substituteName)) {
             if (parameters.length == m.getParameterCount()) {
                 Class<?>[] mparams = m.getParameterTypes();
                 int start = 0;
@@ -189,9 +202,6 @@
     @Override
     public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) {
         if (IS_IN_NATIVE_IMAGE || (UseEncodedGraphs.getValue(b.getOptions()) && !b.parsingIntrinsic())) {
-            if (!IS_IN_NATIVE_IMAGE && UseEncodedGraphs.getValue(b.getOptions())) {
-                b.getReplacements().registerMethodSubstitution(this, targetMethod, INLINE_AFTER_PARSING, b.getOptions());
-            }
             StructuredGraph subst = b.getReplacements().getMethodSubstitution(this,
                             targetMethod,
                             INLINE_AFTER_PARSING,
@@ -220,7 +230,27 @@
 
     @Override
     public String toString() {
-        return String.format("%s[%s.%s(%s)]", getClass().getSimpleName(), declaringClass.getName(), name,
+        return String.format("%s[%s.%s(%s)]", getClass().getSimpleName(), declaringClass.getName(), substituteName,
                         Arrays.asList(parameters).stream().map(c -> c.getTypeName()).collect(Collectors.joining(", ")));
     }
+
+    public String originalMethodAsString() {
+        return String.format("%s.%s(%s)", declaringClass.getName(), substituteName, Arrays.asList(parameters).stream().map(c -> c.getTypeName()).collect(Collectors.joining(", ")));
+    }
+
+    public ResolvedJavaMethod getOriginalMethod(MetaAccessProvider metaAccess) {
+        Class<?> clazz = resolveType(registration.getDeclaringType(), false);
+        if (clazz == null) {
+            throw new GraalError("Can't find original class for " + this + " with class " + registration.getDeclaringType());
+        }
+        ResolvedJavaType type = metaAccess.lookupJavaType(clazz);
+        String argumentsDescriptor = InvocationPlugins.toArgumentDescriptor(originalIsStatic, this.parameters);
+        for (ResolvedJavaMethod declared : type.getDeclaredMethods()) {
+            if (declared.getName().equals(originalName) && declared.isStatic() == originalIsStatic &&
+                            declared.getSignature().toMethodDescriptor().startsWith(argumentsDescriptor)) {
+                return declared;
+            }
+        }
+        throw new GraalError("Can't find original method for " + this + " with class " + registration.getDeclaringType());
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -93,4 +93,9 @@
     public Stamp getAccessStamp() {
         return expectedValue.stamp(NodeView.DEFAULT).meet(newValue.stamp(NodeView.DEFAULT)).unrestricted();
     }
+
+    @Override
+    public LocationIdentity getKilledLocationIdentity() {
+        return getLocationIdentity();
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractUnsafeCompareAndSwapNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractUnsafeCompareAndSwapNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -97,7 +97,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return locationIdentity;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AtomicReadAndAddNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AtomicReadAndAddNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return locationIdentity;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AtomicReadAndWriteNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AtomicReadAndWriteNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -80,7 +80,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return locationIdentity;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -60,7 +60,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
@@ -80,7 +80,7 @@
              * Now the lowering to BeginNode+LoadExceptionNode can be performed, since no more
              * deopts can float in between the begin node and the load exception node.
              */
-            LocationIdentity locationsKilledByInvoke = ((InvokeWithExceptionNode) predecessor()).getLocationIdentity();
+            LocationIdentity locationsKilledByInvoke = ((InvokeWithExceptionNode) predecessor()).getKilledLocationIdentity();
             AbstractBeginNode entry = graph().add(KillingBeginNode.create(locationsKilledByInvoke));
             LoadExceptionObjectNode loadException = graph().add(new LoadExceptionObjectNode(stamp(NodeView.DEFAULT)));
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -103,4 +103,9 @@
     public Stamp getAccessStamp() {
         return stamp(NodeView.DEFAULT);
     }
+
+    @Override
+    public LocationIdentity getKilledLocationIdentity() {
+        return getLocationIdentity();
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MonitorEnterNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MonitorEnterNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -57,7 +57,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MonitorExitNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MonitorExitNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -70,7 +70,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -64,7 +64,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -25,6 +25,7 @@
 package org.graalvm.compiler.nodes.memory;
 
 import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.graph.IterableNodeType;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
@@ -39,7 +40,7 @@
  * does not include a null check on the object.
  */
 @NodeInfo
-public abstract class FixedAccessNode extends DeoptimizingFixedWithNextNode implements Access {
+public abstract class FixedAccessNode extends DeoptimizingFixedWithNextNode implements Access, IterableNodeType {
     public static final NodeClass<FixedAccessNode> TYPE = NodeClass.create(FixedAccessNode.class);
 
     @OptionalInput(InputType.Guard) protected GuardingNode guard;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/MemoryCheckpoint.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/MemoryCheckpoint.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -44,8 +44,7 @@
          *
          * @return the identity of the location killed by this node.
          */
-        LocationIdentity getLocationIdentity();
-
+        LocationIdentity getKilledLocationIdentity();
     }
 
     interface Multi extends MemoryCheckpoint {
@@ -57,7 +56,7 @@
          *
          * @return the identities of all locations killed by this node.
          */
-        LocationIdentity[] getLocationIdentities();
+        LocationIdentity[] getKilledLocationIdentities();
 
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/VolatileReadNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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.nodes.memory;
+
+import org.graalvm.compiler.core.common.GraalOptions;
+import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.graph.NodeClass;
+import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.memory.address.AddressNode;
+import jdk.internal.vm.compiler.word.LocationIdentity;
+
+import static org.graalvm.compiler.nodeinfo.InputType.Memory;
+import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
+import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
+
+@NodeInfo(nameTemplate = "Read#{p#location/s}", allowedUsageTypes = Memory, cycles = CYCLES_2, size = SIZE_1)
+public class VolatileReadNode extends ReadNode implements MemoryCheckpoint.Single {
+    public static final NodeClass<VolatileReadNode> TYPE = NodeClass.create(VolatileReadNode.class);
+
+    public VolatileReadNode(AddressNode address, LocationIdentity location, Stamp stamp, BarrierType barrierType) {
+        super(TYPE, address, location, stamp, null, barrierType, false, null);
+        assert GraalOptions.LateMembars.getValue(address.getOptions());
+    }
+
+    @SuppressWarnings("try")
+    @Override
+    public FloatingAccessNode asFloatingNode() {
+        throw new RuntimeException();
+    }
+
+    @Override
+    public boolean canFloat() {
+        return false;
+    }
+
+    @Override
+    public LocationIdentity getKilledLocationIdentity() {
+        return LocationIdentity.any();
+    }
+
+    @Override
+    public boolean canNullCheck() {
+        return false;
+    }
+
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -44,13 +44,16 @@
 public class WriteNode extends AbstractWriteNode implements LIRLowerableAccess, Canonicalizable {
 
     public static final NodeClass<WriteNode> TYPE = NodeClass.create(WriteNode.class);
+    private final boolean volatileAccess;
 
-    public WriteNode(AddressNode address, LocationIdentity location, ValueNode value, BarrierType barrierType) {
+    public WriteNode(AddressNode address, LocationIdentity location, ValueNode value, BarrierType barrierType, boolean volatileAccess) {
         super(TYPE, address, location, value, barrierType);
+        this.volatileAccess = volatileAccess;
     }
 
     protected WriteNode(NodeClass<? extends WriteNode> c, AddressNode address, LocationIdentity location, ValueNode value, BarrierType barrierType) {
         super(c, address, location, value, barrierType);
+        this.volatileAccess = false;
     }
 
     @Override
@@ -61,7 +64,7 @@
 
     @Override
     public boolean canNullCheck() {
-        return true;
+        return !isVolatile();
     }
 
     @Override
@@ -73,11 +76,23 @@
     public Node canonical(CanonicalizerTool tool) {
         if (tool.canonicalizeReads() && hasExactlyOneUsage() && next() instanceof WriteNode) {
             WriteNode write = (WriteNode) next();
-            if (write.lastLocationAccess == this && write.getAddress() == getAddress() && getAccessStamp().isCompatible(write.getAccessStamp())) {
+            if (write.lastLocationAccess == this && write.getAddress() == getAddress() && getAccessStamp().isCompatible(write.getAccessStamp()) && !isVolatile()) {
                 write.setLastLocationAccess(getLastLocationAccess());
                 return write;
             }
         }
         return this;
     }
+
+    @Override
+    public LocationIdentity getKilledLocationIdentity() {
+        if (isVolatile()) {
+            return LocationIdentity.any();
+        }
+        return getLocationIdentity();
+    }
+
+    public boolean isVolatile() {
+        return volatileAccess;
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/DelegatingReplacements.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/DelegatingReplacements.java	Mon Nov 04 11:25:55 2019 +0000
@@ -34,6 +34,7 @@
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderPlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
+import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.MethodSubstitutionPlugin;
 import org.graalvm.compiler.options.OptionValues;
 
@@ -82,8 +83,13 @@
     }
 
     @Override
-    public void registerMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context, OptionValues options) {
-        delegate.registerMethodSubstitution(plugin, original, context, options);
+    public void registerMethodSubstitution(MethodSubstitutionPlugin plugin) {
+        delegate.registerMethodSubstitution(plugin);
+    }
+
+    @Override
+    public void registerConditionalPlugin(InvocationPlugin plugin) {
+        delegate.registerConditionalPlugin(plugin);
     }
 
     @Override
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/Replacements.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/spi/Replacements.java	Mon Nov 04 11:25:55 2019 +0000
@@ -97,7 +97,14 @@
     /**
      * Registers a plugin as a substitution.
      */
-    void registerMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context, OptionValues options);
+    void registerMethodSubstitution(MethodSubstitutionPlugin plugin);
+
+    /**
+     * Marks a plugin as conditionally applied. In the contenxt of libgraal conditional plugins
+     * can't be used in during graph encoding for snippets and method substitutions and this is used
+     * to detect violations of this restriction.
+     */
+    void registerConditionalPlugin(InvocationPlugin plugin);
 
     /**
      * Gets a graph that is a substitution for a given method.
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java	Mon Nov 04 11:25:55 2019 +0000
@@ -54,11 +54,13 @@
 import org.graalvm.compiler.nodes.AbstractEndNode;
 import org.graalvm.compiler.nodes.AbstractMergeNode;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.ControlSinkNode;
 import org.graalvm.compiler.nodes.ControlSplitNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.GuardNode;
+import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.LoopEndNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
@@ -105,6 +107,8 @@
         public static final OptionKey<Boolean> VerifyKillCFGUnusedNodes = new OptionKey<>(false);
     }
 
+    public static final int MAX_FRAMESTATE_SEARCH_DEPTH = 4;
+
     private static void killCFGInner(FixedNode node) {
         EconomicSet<Node> markedNodes = EconomicSet.create();
         EconomicMap<AbstractMergeNode, List<AbstractEndNode>> unmarkedMerges = EconomicMap.create();
@@ -1110,4 +1114,55 @@
         tool.createVirtualObject(newVirtualArray, newEntryState, Collections.<MonitorIdNode> emptyList(), false);
         tool.replaceWithVirtual(newVirtualArray);
     }
+
+    /**
+     * Snippet lowerings may produce patterns without a frame state on the merge. We need to take
+     * extra care when optimizing these patterns.
+     */
+    public static boolean checkFrameState(FixedNode start, int maxDepth) {
+        if (maxDepth == 0) {
+            return false;
+        }
+        FixedNode node = start;
+        while (true) {
+            if (node instanceof AbstractMergeNode) {
+                AbstractMergeNode mergeNode = (AbstractMergeNode) node;
+                if (mergeNode.stateAfter() == null) {
+                    return false;
+                } else {
+                    return true;
+                }
+            } else if (node instanceof StateSplit) {
+                StateSplit stateSplitNode = (StateSplit) node;
+                if (stateSplitNode.stateAfter() != null) {
+                    return true;
+                }
+            }
+
+            if (node instanceof ControlSplitNode) {
+                ControlSplitNode controlSplitNode = (ControlSplitNode) node;
+                for (Node succ : controlSplitNode.cfgSuccessors()) {
+                    if (checkFrameState((FixedNode) succ, maxDepth - 1)) {
+                        return true;
+                    }
+                }
+                return false;
+            } else if (node instanceof FixedWithNextNode) {
+                FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) node;
+                node = fixedWithNextNode.next();
+            } else if (node instanceof AbstractEndNode) {
+                AbstractEndNode endNode = (AbstractEndNode) node;
+                node = endNode.merge();
+            } else if (node instanceof ControlSinkNode) {
+                return true;
+            } else {
+                assert false : "unexpected node";
+                return false;
+            }
+        }
+    }
+
+    public static boolean mayRemoveSplit(IfNode ifNode) {
+        return GraphUtil.checkFrameState(ifNode.trueSuccessor(), MAX_FRAMESTATE_SEARCH_DEPTH) && GraphUtil.checkFrameState(ifNode.falseSuccessor(), MAX_FRAMESTATE_SEARCH_DEPTH);
+    }
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/AllocatedObjectNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/AllocatedObjectNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -36,7 +36,7 @@
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.spi.ArrayLengthProvider;
-import org.graalvm.compiler.nodes.spi.Virtualizable;
+import org.graalvm.compiler.nodes.spi.VirtualizableAllocation;
 import org.graalvm.compiler.nodes.spi.VirtualizerTool;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 
@@ -45,7 +45,7 @@
  * {@link VirtualObjectNode}.
  */
 @NodeInfo(cycles = CYCLES_0, size = SIZE_0)
-public final class AllocatedObjectNode extends FloatingNode implements Virtualizable, ArrayLengthProvider {
+public final class AllocatedObjectNode extends FloatingNode implements VirtualizableAllocation, ArrayLengthProvider {
 
     public static final NodeClass<AllocatedObjectNode> TYPE = NodeClass.create(AllocatedObjectNode.class);
     @Input VirtualObjectNode virtualObject;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -121,7 +121,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return locks.isEmpty() ? LocationIdentity.init() : LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -24,6 +24,12 @@
 
 package org.graalvm.compiler.phases.common;
 
+import static org.graalvm.compiler.phases.common.CanonicalizerPhase.CanonicalizerFeature.CFG_SIMPLIFICATION;
+import static org.graalvm.compiler.phases.common.CanonicalizerPhase.CanonicalizerFeature.GVN;
+import static org.graalvm.compiler.phases.common.CanonicalizerPhase.CanonicalizerFeature.READ_CANONICALIZATION;
+
+import java.util.EnumSet;
+
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.debug.CounterKey;
@@ -66,6 +72,12 @@
 
 public class CanonicalizerPhase extends BasePhase<CoreProviders> {
 
+    public enum CanonicalizerFeature {
+        READ_CANONICALIZATION,
+        CFG_SIMPLIFICATION,
+        GVN
+    }
+
     private static final int MAX_ITERATION_PER_NODE = 10;
     private static final CounterKey COUNTER_CANONICALIZED_NODES = DebugContext.counter("CanonicalizedNodes");
     private static final CounterKey COUNTER_PROCESSED_NODES = DebugContext.counter("ProcessedNodes");
@@ -75,40 +87,79 @@
     private static final CounterKey COUNTER_SIMPLIFICATION_CONSIDERED_NODES = DebugContext.counter("SimplificationConsideredNodes");
     private static final CounterKey COUNTER_GLOBAL_VALUE_NUMBERING_HITS = DebugContext.counter("GlobalValueNumberingHits");
 
-    private boolean globalValueNumber = true;
-    private boolean canonicalizeReads = true;
-    private boolean simplify = true;
-    private final CustomCanonicalizer customCanonicalizer;
+    private final EnumSet<CanonicalizerFeature> features;
+    private final CustomCanonicalization customCanonicalization;
+    private final CustomSimplification customSimplification;
 
-    public abstract static class CustomCanonicalizer {
+    public interface CustomCanonicalization {
+        /**
+         * @param node the node to be canonicalized
+         * @return the same node if no action should be taken, {@code null} if the node should be
+         *         deleted, or a new node that should replace the given node
+         */
+        Node canonicalize(Node node);
+    }
 
-        public Node canonicalize(Node node) {
-            return node;
-        }
+    public interface CustomSimplification {
+        /**
+         * @param node the node to be simplified
+         * @param tool utility available during the simplification process
+         */
+        void simplify(Node node, SimplifierTool tool);
+    }
 
-        @SuppressWarnings("unused")
-        public void simplify(Node node, SimplifierTool tool) {
-        }
+    protected CanonicalizerPhase(EnumSet<CanonicalizerFeature> features) {
+        this(null, null, features);
+    }
+
+    protected CanonicalizerPhase() {
+        this(null, null, EnumSet.allOf(CanonicalizerFeature.class));
+    }
+
+    protected CanonicalizerPhase(CustomCanonicalization customCanonicalization, CustomSimplification customSimplification) {
+        this(customCanonicalization, customSimplification, EnumSet.allOf(CanonicalizerFeature.class));
     }
 
-    public CanonicalizerPhase() {
-        this(null);
+    protected CanonicalizerPhase(CustomCanonicalization customCanonicalization, CustomSimplification customSimplification, EnumSet<CanonicalizerFeature> features) {
+        this.customCanonicalization = customCanonicalization;
+        this.customSimplification = customSimplification;
+        this.features = features;
     }
 
-    public CanonicalizerPhase(CustomCanonicalizer customCanonicalizer) {
-        this.customCanonicalizer = customCanonicalizer;
+    public CanonicalizerPhase copyWithCustomCanonicalization(CustomCanonicalization newCanonicalization) {
+        return new CanonicalizerPhase(newCanonicalization, customSimplification, features);
+    }
+
+    public CanonicalizerPhase copyWithCustomSimplification(CustomSimplification newSimplification) {
+        return new CanonicalizerPhase(customCanonicalization, newSimplification, features);
+    }
+
+    public CanonicalizerPhase copyWithoutGVN() {
+        EnumSet<CanonicalizerFeature> newFeatures = EnumSet.copyOf(features);
+        newFeatures.remove(GVN);
+        return new CanonicalizerPhase(customCanonicalization, customSimplification, newFeatures);
     }
 
-    public void disableGVN() {
-        globalValueNumber = false;
+    public CanonicalizerPhase copyWithoutSimplification() {
+        EnumSet<CanonicalizerFeature> newFeatures = EnumSet.copyOf(features);
+        newFeatures.remove(CFG_SIMPLIFICATION);
+        return new CanonicalizerPhase(customCanonicalization, customSimplification, newFeatures);
+    }
+
+    public static CanonicalizerPhase create() {
+        return new CanonicalizerPhase(null, null, EnumSet.allOf(CanonicalizerFeature.class));
     }
 
-    public void disableReadCanonicalization() {
-        canonicalizeReads = false;
+    public static CanonicalizerPhase createWithoutReadCanonicalization() {
+        return new CanonicalizerPhase(EnumSet.complementOf(EnumSet.of(READ_CANONICALIZATION)));
     }
 
-    public void disableSimplification() {
-        simplify = false;
+    public static CanonicalizerPhase createWithoutGVN() {
+        return new CanonicalizerPhase(EnumSet.complementOf(EnumSet.of(GVN)));
+    }
+
+    public static CanonicalizerPhase createWithoutCFGSimplification() {
+        return new CanonicalizerPhase(EnumSet.complementOf(EnumSet.of(CFG_SIMPLIFICATION)));
     }
 
     @Override
@@ -274,7 +325,7 @@
             if (tryCanonicalize(node, nodeClass)) {
                 return true;
             }
-            if (globalValueNumber && tryGlobalValueNumbering(node, nodeClass)) {
+            if (features.contains(GVN) && tryGlobalValueNumbering(node, nodeClass)) {
                 return true;
             }
             if (node instanceof ValueNode) {
@@ -329,24 +380,18 @@
         @SuppressWarnings("try")
         public boolean tryCanonicalize(final Node node, NodeClass<?> nodeClass) {
             try (DebugCloseable position = node.withNodeSourcePosition(); DebugContext.Scope scope = debug.withContext(node)) {
-                if (customCanonicalizer != null) {
-                    Node canonical = customCanonicalizer.canonicalize(node);
-                    if (performReplacement(node, canonical)) {
-                        return true;
-                    } else {
-                        customCanonicalizer.simplify(node, tool);
-                        if (node.isDeleted()) {
-                            return true;
-                        }
-                    }
-                }
                 if (nodeClass.isCanonicalizable()) {
                     COUNTER_CANONICALIZATION_CONSIDERED_NODES.increment(debug);
-                    Node canonical;
+                    Node canonical = node;
                     try (AutoCloseable verify = getCanonicalizeableContractAssertion(node)) {
-                        canonical = ((Canonicalizable) node).canonical(tool);
-                        if (canonical == node && nodeClass.isCommutative()) {
-                            canonical = ((BinaryCommutative<?>) node).maybeCommuteInputs();
+                        if (customCanonicalization != null) {
+                            canonical = customCanonicalization.canonicalize(node);
+                        }
+                        if (canonical == node) {
+                            canonical = ((Canonicalizable) node).canonical(tool);
+                            if (canonical == node && nodeClass.isCommutative()) {
+                                canonical = ((BinaryCommutative<?>) node).maybeCommuteInputs();
+                            }
                         }
                     } catch (Throwable e) {
                         throw new GraalGraphError(e).addContext(node);
@@ -356,12 +401,17 @@
                     }
                 }
 
-                if (nodeClass.isSimplifiable() && simplify) {
+                if (features.contains(CFG_SIMPLIFICATION) && nodeClass.isSimplifiable()) {
                     debug.log(DebugContext.VERBOSE_LEVEL, "Canonicalizer: simplifying %s", node);
                     COUNTER_SIMPLIFICATION_CONSIDERED_NODES.increment(debug);
-                    node.simplify(tool);
-                    if (node.isDeleted()) {
-                        debug.log("Canonicalizer: simplified %s", node);
+                    if (customSimplification != null) {
+                        customSimplification.simplify(node, tool);
+                    }
+                    if (node.isAlive()) {
+                        node.simplify(tool);
+                        if (node.isDeleted()) {
+                            debug.log("Canonicalizer: simplified %s", node);
+                        }
                     }
                     return node.isDeleted();
                 }
@@ -518,7 +568,7 @@
 
             @Override
             public boolean canonicalizeReads() {
-                return canonicalizeReads;
+                return features.contains(READ_CANONICALIZATION);
             }
 
             @Override
@@ -549,7 +599,7 @@
     }
 
     public boolean getCanonicalizeReads() {
-        return canonicalizeReads;
+        return features.contains(READ_CANONICALIZATION);
     }
 
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -186,6 +186,8 @@
 
                 // Check if we can move guards upwards.
                 AbstractBeginNode trueSuccessor = node.trueSuccessor();
+                AbstractBeginNode falseSuccessor = node.falseSuccessor();
+
                 EconomicMap<LogicNode, GuardNode> trueGuards = EconomicMap.create(Equivalence.IDENTITY);
                 for (GuardNode guard : trueSuccessor.guards()) {
                     LogicNode condition = guard.getCondition();
@@ -195,7 +197,7 @@
                 }
 
                 if (!trueGuards.isEmpty()) {
-                    for (GuardNode guard : node.falseSuccessor().guards().snapshot()) {
+                    for (GuardNode guard : falseSuccessor.guards().snapshot()) {
                         GuardNode otherGuard = trueGuards.get(guard.getCondition());
                         if (otherGuard != null && guard.isNegated() == otherGuard.isNegated()) {
                             Speculation speculation = otherGuard.getSpeculation();
@@ -210,9 +212,17 @@
                                                 guard.getNoDeoptSuccessorPosition());
                                 GuardNode newGuard = node.graph().unique(newlyCreatedGuard);
                                 if (otherGuard.isAlive()) {
-                                    otherGuard.replaceAndDelete(newGuard);
+                                    if (trueSuccessor instanceof LoopExitNode && beginNode.graph().hasValueProxies()) {
+                                        otherGuard.replaceAndDelete(ProxyNode.forGuard(newGuard, (LoopExitNode) trueSuccessor));
+                                    } else {
+                                        otherGuard.replaceAndDelete(newGuard);
+                                    }
                                 }
-                                guard.replaceAndDelete(newGuard);
+                                if (falseSuccessor instanceof LoopExitNode && beginNode.graph().hasValueProxies()) {
+                                    guard.replaceAndDelete(ProxyNode.forGuard(newGuard, (LoopExitNode) falseSuccessor));
+                                } else {
+                                    guard.replaceAndDelete(newGuard);
+                                }
                             }
                         }
                     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FloatingReadPhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FloatingReadPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -48,6 +48,7 @@
 import org.graalvm.compiler.nodes.LoopEndNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
 import org.graalvm.compiler.nodes.PhiNode;
+import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StartNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -166,9 +167,9 @@
 
     protected void processNode(FixedNode node, EconomicSet<LocationIdentity> currentState) {
         if (node instanceof MemoryCheckpoint.Single) {
-            processIdentity(currentState, ((MemoryCheckpoint.Single) node).getLocationIdentity());
+            processIdentity(currentState, ((MemoryCheckpoint.Single) node).getKilledLocationIdentity());
         } else if (node instanceof MemoryCheckpoint.Multi) {
-            for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) {
+            for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getKilledLocationIdentities()) {
                 processIdentity(currentState, identity);
             }
         }
@@ -274,7 +275,7 @@
                 }
                 mergedStatesCount++;
             }
-            newState.lastMemorySnapshot.put(key, merged);
+            newState.getMap().put(key, merged);
         }
         return newState;
 
@@ -301,6 +302,16 @@
 
         @Override
         protected MemoryMapImpl processNode(FixedNode node, MemoryMapImpl state) {
+
+            if (node instanceof LoopExitNode) {
+                final LoopExitNode loopExitNode = (LoopExitNode) node;
+                final EconomicSet<LocationIdentity> modifiedInLoop = modifiedInLoops.get(loopExitNode.loopBegin());
+                final boolean anyModified = modifiedInLoop.contains(LocationIdentity.any());
+                state.getMap().replaceAll((locationIdentity, memoryNode) -> (anyModified || modifiedInLoop.contains(locationIdentity))
+                                ? ProxyNode.forMemory(memoryNode, loopExitNode, locationIdentity)
+                                : memoryNode);
+            }
+
             if (node instanceof MemoryAnchorNode) {
                 processAnchor((MemoryAnchorNode) node, state);
                 return state;
@@ -312,7 +323,8 @@
 
             if (createFloatingReads && node instanceof FloatableAccessNode) {
                 processFloatable((FloatableAccessNode) node, state);
-            } else if (node instanceof MemoryCheckpoint.Single) {
+            }
+            if (node instanceof MemoryCheckpoint.Single) {
                 processCheckpoint((MemoryCheckpoint.Single) node, state);
             } else if (node instanceof MemoryCheckpoint.Multi) {
                 processCheckpoint((MemoryCheckpoint.Multi) node, state);
@@ -320,7 +332,7 @@
             assert MemoryCheckpoint.TypeAssertion.correctType(node) : node;
 
             if (createMemoryMapNodes && node instanceof ReturnNode) {
-                ((ReturnNode) node).setMemoryMap(node.graph().unique(new MemoryMapNode(state.lastMemorySnapshot)));
+                ((ReturnNode) node).setMemoryMap(node.graph().unique(new MemoryMapNode(state.getMap())));
             }
             return state;
         }
@@ -355,21 +367,21 @@
         }
 
         private static void processCheckpoint(MemoryCheckpoint.Single checkpoint, MemoryMapImpl state) {
-            processIdentity(checkpoint.getLocationIdentity(), checkpoint, state);
+            processIdentity(checkpoint.getKilledLocationIdentity(), checkpoint, state);
         }
 
         private static void processCheckpoint(MemoryCheckpoint.Multi checkpoint, MemoryMapImpl state) {
-            for (LocationIdentity identity : checkpoint.getLocationIdentities()) {
+            for (LocationIdentity identity : checkpoint.getKilledLocationIdentities()) {
                 processIdentity(identity, checkpoint, state);
             }
         }
 
         private static void processIdentity(LocationIdentity identity, MemoryCheckpoint checkpoint, MemoryMapImpl state) {
             if (identity.isAny()) {
-                state.lastMemorySnapshot.clear();
+                state.getMap().clear();
             }
             if (identity.isMutable()) {
-                state.lastMemorySnapshot.put(identity, checkpoint);
+                state.getMap().put(identity, checkpoint);
             }
         }
 
@@ -405,7 +417,7 @@
                  * side it needs to choose by putting in the location identity on both successors.
                  */
                 InvokeWithExceptionNode invoke = (InvokeWithExceptionNode) node.predecessor();
-                result.lastMemorySnapshot.put(invoke.getLocationIdentity(), (MemoryCheckpoint) node);
+                result.getMap().put(invoke.getKilledLocationIdentity(), (MemoryCheckpoint) node);
             }
             return result;
         }
@@ -417,13 +429,13 @@
             if (modifiedLocations.contains(LocationIdentity.any())) {
                 // create phis for all locations if ANY is modified in the loop
                 modifiedLocations = EconomicSet.create(Equivalence.DEFAULT, modifiedLocations);
-                modifiedLocations.addAll(initialState.lastMemorySnapshot.getKeys());
+                modifiedLocations.addAll(initialState.getMap().getKeys());
             }
 
             for (LocationIdentity location : modifiedLocations) {
                 createMemoryPhi(loop, initialState, phis, location);
             }
-            initialState.lastMemorySnapshot.putAll(phis);
+            initialState.getMap().putAll(phis);
 
             LoopInfo<MemoryMapImpl> loopInfo = ReentrantNodeIterator.processLoop(this, loop, initialState);
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/GuardLoweringPhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/GuardLoweringPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -24,7 +24,6 @@
 
 package org.graalvm.compiler.phases.common;
 
-import org.graalvm.compiler.core.common.cfg.Loop;
 import org.graalvm.compiler.debug.DebugCloseable;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.Node;
@@ -34,8 +33,6 @@
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.GuardNode;
 import org.graalvm.compiler.nodes.IfNode;
-import org.graalvm.compiler.nodes.LoopBeginNode;
-import org.graalvm.compiler.nodes.LoopExitNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
@@ -62,11 +59,9 @@
 
     private static class LowerGuards extends ScheduledNodeIterator {
 
-        private final Block block;
         private boolean useGuardIdAsDebugId;
 
-        LowerGuards(Block block, boolean useGuardIdAsDebugId) {
-            this.block = block;
+        LowerGuards(boolean useGuardIdAsDebugId) {
             this.useGuardIdAsDebugId = useGuardIdAsDebugId;
         }
 
@@ -95,7 +90,6 @@
                 AbstractBeginNode deoptBranch = BeginNode.begin(deopt);
                 AbstractBeginNode trueSuccessor;
                 AbstractBeginNode falseSuccessor;
-                insertLoopExits(deopt);
                 if (guard.isNegated()) {
                     trueSuccessor = deoptBranch;
                     falseSuccessor = fastPath;
@@ -108,16 +102,6 @@
                 insert(ifNode, fastPath);
             }
         }
-
-        private void insertLoopExits(DeoptimizeNode deopt) {
-            Loop<Block> loop = block.getLoop();
-            StructuredGraph graph = deopt.graph();
-            while (loop != null) {
-                LoopExitNode exit = graph.add(new LoopExitNode((LoopBeginNode) loop.getHeader().getBeginNode()));
-                graph.addBeforeFixed(deopt, exit);
-                loop = loop.getParent();
-            }
-        }
     }
 
     @Override
@@ -143,6 +127,6 @@
 
     private static void processBlock(Block block, ScheduleResult schedule) {
         DebugContext debug = block.getBeginNode().getDebug();
-        new LowerGuards(block, debug.isDumpEnabledForMethod() || debug.isLogEnabledForMethod()).processNodes(block, schedule);
+        new LowerGuards(debug.isDumpEnabledForMethod() || debug.isLogEnabledForMethod()).processNodes(block, schedule);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/InsertMembarsPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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.phases.common;
+
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.extended.MembarNode;
+import org.graalvm.compiler.nodes.memory.FixedAccessNode;
+import org.graalvm.compiler.nodes.memory.ReadNode;
+import org.graalvm.compiler.nodes.memory.VolatileReadNode;
+import org.graalvm.compiler.nodes.memory.WriteNode;
+import org.graalvm.compiler.phases.Phase;
+
+import static jdk.vm.ci.code.MemoryBarriers.JMM_POST_VOLATILE_READ;
+import static jdk.vm.ci.code.MemoryBarriers.JMM_POST_VOLATILE_WRITE;
+import static jdk.vm.ci.code.MemoryBarriers.JMM_PRE_VOLATILE_READ;
+import static jdk.vm.ci.code.MemoryBarriers.JMM_PRE_VOLATILE_WRITE;
+
+public class InsertMembarsPhase extends Phase {
+    @Override
+    protected void run(StructuredGraph graph) {
+        for (FixedAccessNode access : graph.getNodes(FixedAccessNode.TYPE)) {
+            if (access instanceof VolatileReadNode) {
+                ReadNode read = (ReadNode) access;
+                MembarNode preMembar = graph.add(new MembarNode(JMM_PRE_VOLATILE_READ));
+                graph.addBeforeFixed(read, preMembar);
+                MembarNode postMembar = graph.add(new MembarNode(JMM_POST_VOLATILE_READ));
+                graph.addAfterFixed(read, postMembar);
+            } else if (access instanceof WriteNode && ((WriteNode) access).isVolatile()) {
+                WriteNode write = (WriteNode) access;
+                MembarNode preMembar = graph.add(new MembarNode(JMM_PRE_VOLATILE_WRITE));
+                graph.addBeforeFixed(write, preMembar);
+                MembarNode postMembar = graph.add(new MembarNode(JMM_POST_VOLATILE_WRITE));
+                graph.addAfterFixed(write, postMembar);
+            }
+        }
+    }
+
+    @Override
+    public float codeSizeIncrease() {
+        return 3f;
+    }
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/LoweringPhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/LoweringPhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -46,6 +46,7 @@
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeBitMap;
 import org.graalvm.compiler.graph.NodeClass;
+import org.graalvm.compiler.graph.NodeMap;
 import org.graalvm.compiler.graph.NodeSourcePosition;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.nodeinfo.InputType;
@@ -131,12 +132,14 @@
         private final NodeBitMap activeGuards;
         private AnchoringNode guardAnchor;
         private FixedWithNextNode lastFixedNode;
+        private NodeMap<Block> nodeMap;
 
-        LoweringToolImpl(CoreProviders context, AnchoringNode guardAnchor, NodeBitMap activeGuards, FixedWithNextNode lastFixedNode) {
+        LoweringToolImpl(CoreProviders context, AnchoringNode guardAnchor, NodeBitMap activeGuards, FixedWithNextNode lastFixedNode, NodeMap<Block> nodeMap) {
             this.context = context;
             this.guardAnchor = guardAnchor;
             this.activeGuards = activeGuards;
             this.lastFixedNode = lastFixedNode;
+            this.nodeMap = nodeMap;
         }
 
         @Override
@@ -199,7 +202,8 @@
             StructuredGraph graph = before.graph();
             if (OptEliminateGuards.getValue(graph.getOptions())) {
                 for (Node usage : condition.usages()) {
-                    if (!activeGuards.isNew(usage) && activeGuards.isMarked(usage) && ((GuardNode) usage).isNegated() == negated) {
+                    if (!activeGuards.isNew(usage) && activeGuards.isMarked(usage) && ((GuardNode) usage).isNegated() == negated &&
+                                    (!before.graph().hasValueProxies() || nodeMap.get(((GuardNode) usage).getAnchor().asNode()).isInSameOrOuterLoopOf(nodeMap.get(before)))) {
                         return (GuardNode) usage;
                     }
                 }
@@ -310,9 +314,9 @@
                  */
                 boolean isAny = false;
                 if (n instanceof MemoryCheckpoint.Single) {
-                    isAny = ((MemoryCheckpoint.Single) n).getLocationIdentity().isAny();
+                    isAny = ((MemoryCheckpoint.Single) n).getKilledLocationIdentity().isAny();
                 } else {
-                    for (LocationIdentity ident : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
+                    for (LocationIdentity ident : ((MemoryCheckpoint.Multi) n).getKilledLocationIdentities()) {
                         if (ident.isAny()) {
                             isAny = true;
                         }
@@ -447,7 +451,7 @@
         @SuppressWarnings("try")
         private AnchoringNode process(final Block b, final NodeBitMap activeGuards, final AnchoringNode startAnchor) {
 
-            final LoweringToolImpl loweringTool = new LoweringToolImpl(context, startAnchor, activeGuards, b.getBeginNode());
+            final LoweringToolImpl loweringTool = new LoweringToolImpl(context, startAnchor, activeGuards, b.getBeginNode(), this.schedule.getNodeToBlockMap());
 
             // Lower the instructions of this block.
             List<Node> nodes = schedule.nodesFor(b);
@@ -613,69 +617,6 @@
         }
     }
 
-    public static void processBlockBounded(final Frame<?> rootFrame) {
-        ProcessBlockState state = ST_PROCESS;
-        Frame<?> f = rootFrame;
-        while (f != null) {
-            ProcessBlockState nextState;
-            if (state == ST_PROCESS || state == ST_PROCESS_ALWAYS_REACHED) {
-                f.preprocess();
-                nextState = state == ST_PROCESS_ALWAYS_REACHED ? ST_ENTER : ST_ENTER_ALWAYS_REACHED;
-            } else if (state == ST_ENTER_ALWAYS_REACHED) {
-                if (f.alwaysReachedBlock != null && f.alwaysReachedBlock.getDominator() == f.block) {
-                    Frame<?> continueRecur = f.enterAlwaysReached(f.alwaysReachedBlock);
-                    if (continueRecur == null) {
-                        // stop recursion here
-                        f.postprocess();
-                        f = f.parent;
-                        state = ST_ENTER;
-                        continue;
-                    }
-                    f = continueRecur;
-                    nextState = ST_PROCESS;
-                } else {
-                    nextState = ST_ENTER;
-                }
-            } else if (state == ST_ENTER) {
-                if (f.dominated != null) {
-                    Block n = f.dominated;
-                    f.dominated = n.getDominatedSibling();
-                    if (n == f.alwaysReachedBlock) {
-                        if (f.dominated != null) {
-                            n = f.dominated;
-                            f.dominated = n.getDominatedSibling();
-                        } else {
-                            n = null;
-                        }
-                    }
-                    if (n == null) {
-                        nextState = ST_LEAVE;
-                    } else {
-                        Frame<?> continueRecur = f.enter(n);
-                        if (continueRecur == null) {
-                            // stop recursion here
-                            f.postprocess();
-                            f = f.parent;
-                            state = ST_ENTER;
-                            continue;
-                        }
-                        f = continueRecur;
-                        nextState = ST_PROCESS;
-                    }
-                } else {
-                    nextState = ST_LEAVE;
-                }
-            } else if (state == ST_LEAVE) {
-                f.postprocess();
-                f = f.parent;
-                nextState = ST_ENTER;
-            } else {
-                throw GraalError.shouldNotReachHere();
-            }
-            state = nextState;
-        }
-    }
-
     public abstract static class Frame<T extends Frame<?>> {
         protected final Block block;
         final T parent;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java	Mon Nov 04 11:25:55 2019 +0000
@@ -56,6 +56,7 @@
 import org.graalvm.compiler.graph.NodeMap;
 import org.graalvm.compiler.graph.NodeSourcePosition;
 import org.graalvm.compiler.graph.NodeWorkList;
+import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodeinfo.Verbosity;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.AbstractEndNode;
@@ -87,6 +88,7 @@
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.IsNullNode;
 import org.graalvm.compiler.nodes.extended.ForeignCallNode;
+import org.graalvm.compiler.nodes.extended.GuardedNode;
 import org.graalvm.compiler.nodes.extended.GuardingNode;
 import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
@@ -526,7 +528,17 @@
                 assert unwindNode.predecessor() != null;
                 assert invokeWithException.exceptionEdge().successors().count() == 1;
                 ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge();
-                obj.replaceAtUsages(unwindNode.exception());
+                /*
+                 * The exception object node is a begin node, i.e., it can be used as an anchor for
+                 * other nodes, thus we need to re-route them to a valid anchor, i.e. the begin node
+                 * of the unwind block.
+                 */
+                assert obj.usages().filter(x -> x instanceof GuardedNode && ((GuardedNode) x).getGuard() == obj).count() == 0 : "Must not have guards attached to an exception object node";
+                AbstractBeginNode replacementAnchor = AbstractBeginNode.prevBegin(unwindNode);
+                assert replacementAnchor != null;
+                obj.replaceAtUsages(InputType.Anchor, replacementAnchor);
+                obj.replaceAtUsages(InputType.Value, unwindNode.exception());
+
                 Node n = obj.next();
                 obj.setNext(null);
                 unwindNode.replaceAndDelete(n);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -208,8 +208,10 @@
             FixedNode exceptionSux = exceptionEdge.next();
             graph.addBeforeFixed(exceptionSux, exceptionMerge);
             exceptionObjectPhi = graph.addWithoutUnique(new ValuePhiNode(StampFactory.forKind(JavaKind.Object), exceptionMerge));
-            exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, JavaKind.Object, new JavaKind[]{JavaKind.Object},
-                            new ValueNode[]{exceptionObjectPhi}));
+
+            assert exceptionEdge.stateAfter().bci == invoke.bci();
+            assert exceptionEdge.stateAfter().rethrowException();
+            exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(JavaKind.Object, JavaKind.Object, exceptionObjectPhi));
         }
 
         // create one separate block for each invoked method
@@ -396,7 +398,7 @@
         JavaKind kind = invoke.asNode().getStackKind();
         if (kind != JavaKind.Void) {
             FrameState stateAfter = invoke.stateAfter();
-            stateAfter = stateAfter.duplicate(stateAfter.bci);
+            stateAfter = stateAfter.duplicate();
             stateAfter.replaceFirstInput(invoke.asNode(), result.asNode());
             result.setStateAfter(stateAfter);
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/MemoryScheduleVerification.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, 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.phases.schedule;
-
-import java.util.List;
-
-import jdk.internal.vm.compiler.collections.EconomicSet;
-import jdk.internal.vm.compiler.collections.Equivalence;
-import org.graalvm.compiler.core.common.cfg.BlockMap;
-import org.graalvm.compiler.core.common.cfg.Loop;
-import org.graalvm.compiler.debug.DebugContext;
-import org.graalvm.compiler.graph.Node;
-import org.graalvm.compiler.nodes.AbstractBeginNode;
-import org.graalvm.compiler.nodes.AbstractMergeNode;
-import org.graalvm.compiler.nodes.LoopBeginNode;
-import org.graalvm.compiler.nodes.PhiNode;
-import org.graalvm.compiler.nodes.cfg.Block;
-import org.graalvm.compiler.nodes.cfg.HIRLoop;
-import org.graalvm.compiler.nodes.memory.FloatingReadNode;
-import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
-import org.graalvm.compiler.nodes.memory.MemoryNode;
-import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
-import org.graalvm.compiler.phases.graph.ReentrantBlockIterator;
-import org.graalvm.compiler.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
-import jdk.internal.vm.compiler.word.LocationIdentity;
-
-public final class MemoryScheduleVerification extends BlockIteratorClosure<EconomicSet<FloatingReadNode>> {
-
-    private final BlockMap<List<Node>> blockToNodesMap;
-
-    public static boolean check(Block startBlock, BlockMap<List<Node>> blockToNodesMap) {
-        ReentrantBlockIterator.apply(new MemoryScheduleVerification(blockToNodesMap), startBlock);
-        return true;
-    }
-
-    private MemoryScheduleVerification(BlockMap<List<Node>> blockToNodesMap) {
-        this.blockToNodesMap = blockToNodesMap;
-    }
-
-    @Override
-    protected EconomicSet<FloatingReadNode> getInitialState() {
-        return EconomicSet.create(Equivalence.IDENTITY);
-    }
-
-    @Override
-    protected EconomicSet<FloatingReadNode> processBlock(Block block, EconomicSet<FloatingReadNode> currentState) {
-        AbstractBeginNode beginNode = block.getBeginNode();
-        if (beginNode instanceof AbstractMergeNode) {
-            AbstractMergeNode abstractMergeNode = (AbstractMergeNode) beginNode;
-            for (PhiNode phi : abstractMergeNode.phis()) {
-                if (phi instanceof MemoryPhiNode) {
-                    MemoryPhiNode memoryPhiNode = (MemoryPhiNode) phi;
-                    addFloatingReadUsages(currentState, memoryPhiNode);
-                }
-            }
-        }
-        for (Node n : blockToNodesMap.get(block)) {
-            if (n instanceof MemoryCheckpoint) {
-                if (n instanceof MemoryCheckpoint.Single) {
-                    MemoryCheckpoint.Single single = (MemoryCheckpoint.Single) n;
-                    processLocation(n, single.getLocationIdentity(), currentState);
-                } else if (n instanceof MemoryCheckpoint.Multi) {
-                    MemoryCheckpoint.Multi multi = (MemoryCheckpoint.Multi) n;
-                    for (LocationIdentity location : multi.getLocationIdentities()) {
-                        processLocation(n, location, currentState);
-                    }
-                }
-
-                addFloatingReadUsages(currentState, n);
-            } else if (n instanceof MemoryNode) {
-                addFloatingReadUsages(currentState, n);
-            } else if (n instanceof FloatingReadNode) {
-                FloatingReadNode floatingReadNode = (FloatingReadNode) n;
-                if (floatingReadNode.getLastLocationAccess() != null && floatingReadNode.getLocationIdentity().isMutable()) {
-                    if (currentState.contains(floatingReadNode)) {
-                        // Floating read was found in the state.
-                        currentState.remove(floatingReadNode);
-                    } else {
-                        throw new RuntimeException("Floating read node " + n + " was not found in the state, i.e., it was killed by a memory check point before its place in the schedule. Block=" +
-                                        block + ", block begin: " + block.getBeginNode() + " block loop: " + block.getLoop() + ", " + blockToNodesMap.get(block).get(0));
-                    }
-                }
-
-            }
-        }
-        return currentState;
-    }
-
-    private static void addFloatingReadUsages(EconomicSet<FloatingReadNode> currentState, Node n) {
-        for (FloatingReadNode read : n.usages().filter(FloatingReadNode.class)) {
-            if (read.getLastLocationAccess() == n && read.getLocationIdentity().isMutable()) {
-                currentState.add(read);
-            }
-        }
-    }
-
-    private void processLocation(Node n, LocationIdentity location, EconomicSet<FloatingReadNode> currentState) {
-        assert n != null;
-        if (location.isImmutable()) {
-            return;
-        }
-
-        for (FloatingReadNode r : cloneState(currentState)) {
-            if (r.getLocationIdentity().overlaps(location)) {
-                // This read is killed by this location.
-                r.getDebug().log(DebugContext.VERBOSE_LEVEL, "%s removing %s from state", n, r);
-                currentState.remove(r);
-            }
-        }
-    }
-
-    @Override
-    protected EconomicSet<FloatingReadNode> merge(Block merge, List<EconomicSet<FloatingReadNode>> states) {
-        EconomicSet<FloatingReadNode> result = states.get(0);
-        for (int i = 1; i < states.size(); ++i) {
-            result.retainAll(states.get(i));
-        }
-        return result;
-    }
-
-    @Override
-    protected EconomicSet<FloatingReadNode> cloneState(EconomicSet<FloatingReadNode> oldState) {
-        EconomicSet<FloatingReadNode> result = EconomicSet.create(Equivalence.IDENTITY);
-        if (oldState != null) {
-            result.addAll(oldState);
-        }
-        return result;
-    }
-
-    @Override
-    protected List<EconomicSet<FloatingReadNode>> processLoop(Loop<Block> loop, EconomicSet<FloatingReadNode> initialState) {
-        HIRLoop l = (HIRLoop) loop;
-        for (MemoryPhiNode memoryPhi : ((LoopBeginNode) l.getHeader().getBeginNode()).memoryPhis()) {
-            for (FloatingReadNode r : cloneState(initialState)) {
-                if (r.getLocationIdentity().overlaps(memoryPhi.getLocationIdentity())) {
-                    initialState.remove(r);
-                }
-            }
-        }
-        return ReentrantBlockIterator.processLoop(this, loop, initialState).exitStates;
-    }
-}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/SchedulePhase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -206,7 +206,8 @@
                 sortNodesLatestWithinBlock(cfg, earliestBlockToNodesMap, latestBlockToNodesMap, currentNodeMap, watchListMap, visited);
 
                 assert verifySchedule(cfg, latestBlockToNodesMap, currentNodeMap);
-                assert (!Assertions.detailedAssertionsEnabled(graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), latestBlockToNodesMap);
+                assert (!Assertions.detailedAssertionsEnabled(graph.getOptions())) ||
+                                ScheduleVerification.check(cfg.getStartBlock(), latestBlockToNodesMap, currentNodeMap);
 
                 this.blockToNodesMap = latestBlockToNodesMap;
 
@@ -358,7 +359,7 @@
             }
 
             if (lastBlock.getBeginNode() instanceof KillingBeginNode) {
-                LocationIdentity locationIdentity = ((KillingBeginNode) lastBlock.getBeginNode()).getLocationIdentity();
+                LocationIdentity locationIdentity = ((KillingBeginNode) lastBlock.getBeginNode()).getKilledLocationIdentity();
                 if ((locationIdentity.isAny() || locationIdentity.equals(location)) && lastBlock != earliestBlock) {
                     // The begin of this block kills the location, so we *have* to schedule the node
                     // in the dominating block.
@@ -374,13 +375,13 @@
                 for (Node n : subList) {
                     // Check if this node kills a node in the watch list.
                     if (n instanceof MemoryCheckpoint.Single) {
-                        LocationIdentity identity = ((MemoryCheckpoint.Single) n).getLocationIdentity();
+                        LocationIdentity identity = ((MemoryCheckpoint.Single) n).getKilledLocationIdentity();
                         killed.add(identity);
                         if (killed.isAny()) {
                             return;
                         }
                     } else if (n instanceof MemoryCheckpoint.Multi) {
-                        for (LocationIdentity identity : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
+                        for (LocationIdentity identity : ((MemoryCheckpoint.Multi) n).getKilledLocationIdentities()) {
                             killed.add(identity);
                             if (killed.isAny()) {
                                 return;
@@ -471,10 +472,10 @@
             if (watchList != null && !watchList.isEmpty()) {
                 // Check if this node kills a node in the watch list.
                 if (n instanceof MemoryCheckpoint.Single) {
-                    LocationIdentity identity = ((MemoryCheckpoint.Single) n).getLocationIdentity();
+                    LocationIdentity identity = ((MemoryCheckpoint.Single) n).getKilledLocationIdentity();
                     checkWatchList(watchList, identity, b, result, nodeMap, unprocessed);
                 } else if (n instanceof MemoryCheckpoint.Multi) {
-                    for (LocationIdentity identity : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
+                    for (LocationIdentity identity : ((MemoryCheckpoint.Multi) n).getKilledLocationIdentities()) {
                         checkWatchList(watchList, identity, b, result, nodeMap, unprocessed);
                     }
                 }
@@ -896,7 +897,7 @@
                 }
             }
 
-            assert (!Assertions.detailedAssertionsEnabled(cfg.graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), blockToNodes);
+            assert (!Assertions.detailedAssertionsEnabled(cfg.graph.getOptions())) || ScheduleVerification.check(cfg.getStartBlock(), blockToNodes, nodeToBlock);
         }
 
         private static void processNodes(NodeBitMap visited, NodeMap<MicroBlock> entries, NodeStack stack, MicroBlock startBlock, Iterable<? extends Node> nodes) {
@@ -1183,10 +1184,10 @@
             Formatter buf = new Formatter();
             buf.format("%s", n);
             if (n instanceof MemoryCheckpoint.Single) {
-                buf.format(" // kills %s", ((MemoryCheckpoint.Single) n).getLocationIdentity());
+                buf.format(" // kills %s", ((MemoryCheckpoint.Single) n).getKilledLocationIdentity());
             } else if (n instanceof MemoryCheckpoint.Multi) {
                 buf.format(" // kills ");
-                for (LocationIdentity locid : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
+                for (LocationIdentity locid : ((MemoryCheckpoint.Multi) n).getKilledLocationIdentities()) {
                     buf.format("%s, ", locid);
                 }
             } else if (n instanceof FloatingReadNode) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/schedule/ScheduleVerification.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2015, 2019, 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.phases.schedule;
+
+import java.util.List;
+
+import jdk.internal.vm.compiler.collections.EconomicSet;
+import jdk.internal.vm.compiler.collections.Equivalence;
+import org.graalvm.compiler.core.common.cfg.BlockMap;
+import org.graalvm.compiler.core.common.cfg.Loop;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.graph.NodeMap;
+import org.graalvm.compiler.nodes.AbstractBeginNode;
+import org.graalvm.compiler.nodes.AbstractMergeNode;
+import org.graalvm.compiler.nodes.LoopBeginNode;
+import org.graalvm.compiler.nodes.LoopExitNode;
+import org.graalvm.compiler.nodes.MemoryProxyNode;
+import org.graalvm.compiler.nodes.PhiNode;
+import org.graalvm.compiler.nodes.ProxyNode;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.VirtualState;
+import org.graalvm.compiler.nodes.cfg.Block;
+import org.graalvm.compiler.nodes.cfg.HIRLoop;
+import org.graalvm.compiler.nodes.memory.FloatingReadNode;
+import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
+import org.graalvm.compiler.nodes.memory.MemoryNode;
+import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
+import org.graalvm.compiler.phases.graph.ReentrantBlockIterator;
+import org.graalvm.compiler.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
+import jdk.internal.vm.compiler.word.LocationIdentity;
+
+/**
+ * Verifies that the schedule of the graph is correct. Checks that floating reads are not killed
+ * between definition and usage. Also checks that there are no usages spanning loop exits without a
+ * proper proxy node.
+ */
+public final class ScheduleVerification extends BlockIteratorClosure<EconomicSet<FloatingReadNode>> {
+
+    private final BlockMap<List<Node>> blockToNodesMap;
+    private final NodeMap<Block> nodeMap;
+    private final StructuredGraph graph;
+
+    public static boolean check(Block startBlock, BlockMap<List<Node>> blockToNodesMap, NodeMap<Block> nodeMap) {
+        ReentrantBlockIterator.apply(new ScheduleVerification(blockToNodesMap, nodeMap, startBlock.getBeginNode().graph()), startBlock);
+        return true;
+    }
+
+    private ScheduleVerification(BlockMap<List<Node>> blockToNodesMap, NodeMap<Block> nodeMap, StructuredGraph graph) {
+        this.blockToNodesMap = blockToNodesMap;
+        this.nodeMap = nodeMap;
+        this.graph = graph;
+    }
+
+    @Override
+    protected EconomicSet<FloatingReadNode> getInitialState() {
+        return EconomicSet.create(Equivalence.IDENTITY);
+    }
+
+    @Override
+    protected EconomicSet<FloatingReadNode> processBlock(Block block, EconomicSet<FloatingReadNode> currentState) {
+        AbstractBeginNode beginNode = block.getBeginNode();
+        if (beginNode instanceof AbstractMergeNode) {
+            AbstractMergeNode abstractMergeNode = (AbstractMergeNode) beginNode;
+            for (PhiNode phi : abstractMergeNode.phis()) {
+                if (phi instanceof MemoryPhiNode) {
+                    MemoryPhiNode memoryPhiNode = (MemoryPhiNode) phi;
+                    addFloatingReadUsages(currentState, memoryPhiNode);
+                }
+            }
+        }
+        if (beginNode instanceof LoopExitNode) {
+            LoopExitNode loopExitNode = (LoopExitNode) beginNode;
+            for (ProxyNode proxy : loopExitNode.proxies()) {
+                if (proxy instanceof MemoryProxyNode) {
+                    MemoryProxyNode memoryProxyNode = (MemoryProxyNode) proxy;
+                    addFloatingReadUsages(currentState, memoryProxyNode);
+                }
+            }
+        }
+        for (Node n : blockToNodesMap.get(block)) {
+            if (n instanceof MemoryCheckpoint) {
+                if (n instanceof MemoryCheckpoint.Single) {
+                    MemoryCheckpoint.Single single = (MemoryCheckpoint.Single) n;
+                    processLocation(n, single.getKilledLocationIdentity(), currentState);
+                } else if (n instanceof MemoryCheckpoint.Multi) {
+                    MemoryCheckpoint.Multi multi = (MemoryCheckpoint.Multi) n;
+                    for (LocationIdentity location : multi.getKilledLocationIdentities()) {
+                        processLocation(n, location, currentState);
+                    }
+                }
+
+                addFloatingReadUsages(currentState, n);
+            } else if (n instanceof MemoryNode) {
+                addFloatingReadUsages(currentState, n);
+            } else if (n instanceof FloatingReadNode) {
+                FloatingReadNode floatingReadNode = (FloatingReadNode) n;
+                if (floatingReadNode.getLastLocationAccess() != null && floatingReadNode.getLocationIdentity().isMutable()) {
+                    if (currentState.contains(floatingReadNode)) {
+                        // Floating read was found in the state.
+                        currentState.remove(floatingReadNode);
+                    } else {
+                        throw new RuntimeException("Floating read node " + n + " was not found in the state, i.e., it was killed by a memory check point before its place in the schedule. Block=" +
+                                        block + ", block begin: " + block.getBeginNode() + " block loop: " + block.getLoop() + ", " + blockToNodesMap.get(block).get(0));
+                    }
+                }
+            }
+            assert nodeMap.get(n) == block;
+            if (graph.hasValueProxies() && block.getLoop() != null && !(n instanceof VirtualState)) {
+                for (Node usage : n.usages()) {
+                    Node usageNode = usage;
+
+                    if (usageNode instanceof PhiNode) {
+                        PhiNode phiNode = (PhiNode) usage;
+                        usageNode = phiNode.merge();
+                    }
+
+                    if (usageNode instanceof LoopExitNode) {
+                        LoopExitNode loopExitNode = (LoopExitNode) usageNode;
+                        if (loopExitNode.loopBegin() == n || loopExitNode.stateAfter() == n) {
+                            continue;
+                        }
+                    }
+                    Block usageBlock = nodeMap.get(usageNode);
+
+                    Loop<Block> usageLoop = null;
+                    if (usageNode instanceof ProxyNode) {
+                        ProxyNode proxyNode = (ProxyNode) usageNode;
+                        usageLoop = nodeMap.get(proxyNode.proxyPoint().loopBegin()).getLoop();
+                    } else {
+                        if (usageBlock.getBeginNode() instanceof LoopExitNode) {
+                            // For nodes in the loop exit node block, we don't know for sure
+                            // whether they are "in the loop" or not. It depends on whether
+                            // one of their transient usages is a loop proxy node.
+                            // For now, let's just assume those nodes are OK, i.e., "in the loop".
+                            LoopExitNode loopExitNode = (LoopExitNode) usageBlock.getBeginNode();
+                            usageLoop = nodeMap.get(loopExitNode.loopBegin()).getLoop();
+                        } else {
+                            usageLoop = usageBlock.getLoop();
+                        }
+                    }
+
+                    assert usageLoop != null : n + ", " + nodeMap.get(n) + " / " + usageNode + ", " + nodeMap.get(usageNode);
+                    while (usageLoop != block.getLoop() && usageLoop != null) {
+                        usageLoop = usageLoop.getParent();
+                    }
+                    assert usageLoop != null : n + ", " + usageNode + ", " + usageBlock + ", " + usageBlock.getLoop() + ", " + block + ", " + block.getLoop();
+                }
+            }
+        }
+        return currentState;
+    }
+
+    private static void addFloatingReadUsages(EconomicSet<FloatingReadNode> currentState, Node n) {
+        for (FloatingReadNode read : n.usages().filter(FloatingReadNode.class)) {
+            if (read.getLastLocationAccess() == n && read.getLocationIdentity().isMutable()) {
+                currentState.add(read);
+            }
+        }
+    }
+
+    private void processLocation(Node n, LocationIdentity location, EconomicSet<FloatingReadNode> currentState) {
+        assert n != null;
+        if (location.isImmutable()) {
+            return;
+        }
+
+        for (FloatingReadNode r : cloneState(currentState)) {
+            if (r.getLocationIdentity().overlaps(location)) {
+                // This read is killed by this location.
+                r.getDebug().log(DebugContext.VERBOSE_LEVEL, "%s removing %s from state", n, r);
+                currentState.remove(r);
+            }
+        }
+    }
+
+    @Override
+    protected EconomicSet<FloatingReadNode> merge(Block merge, List<EconomicSet<FloatingReadNode>> states) {
+        EconomicSet<FloatingReadNode> result = states.get(0);
+        for (int i = 1; i < states.size(); ++i) {
+            result.retainAll(states.get(i));
+        }
+        return result;
+    }
+
+    @Override
+    protected EconomicSet<FloatingReadNode> cloneState(EconomicSet<FloatingReadNode> oldState) {
+        EconomicSet<FloatingReadNode> result = EconomicSet.create(Equivalence.IDENTITY);
+        if (oldState != null) {
+            result.addAll(oldState);
+        }
+        return result;
+    }
+
+    @Override
+    protected List<EconomicSet<FloatingReadNode>> processLoop(Loop<Block> loop, EconomicSet<FloatingReadNode> initialState) {
+        HIRLoop l = (HIRLoop) loop;
+        for (MemoryPhiNode memoryPhi : ((LoopBeginNode) l.getHeader().getBeginNode()).memoryPhis()) {
+            for (FloatingReadNode r : cloneState(initialState)) {
+                if (r.getLocationIdentity().overlaps(memoryPhi.getLocationIdentity())) {
+                    initialState.remove(r);
+                }
+            }
+        }
+        return ReentrantBlockIterator.processLoop(this, loop, initialState).exitStates;
+    }
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64GraphBuilderPlugins.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64GraphBuilderPlugins.java	Mon Nov 04 11:25:55 2019 +0000
@@ -32,7 +32,6 @@
 import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.SIN;
 import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.TAN;
 
-import org.graalvm.compiler.bytecode.BytecodeProvider;
 import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticLIRGeneratorTool.RoundingMode;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
@@ -45,6 +44,7 @@
 import org.graalvm.compiler.nodes.java.AtomicReadAndWriteNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
+import org.graalvm.compiler.nodes.spi.Replacements;
 import org.graalvm.compiler.replacements.TargetGraphBuilderPlugins;
 import org.graalvm.compiler.replacements.nodes.BinaryMathIntrinsicNode;
 import org.graalvm.compiler.replacements.nodes.FusedMultiplyAddNode;
@@ -60,39 +60,39 @@
 
 public class AArch64GraphBuilderPlugins implements TargetGraphBuilderPlugins {
     @Override
-    public void register(Plugins plugins, BytecodeProvider replacementsBytecodeProvider, Architecture arch, boolean explicitUnsafeNullChecks, boolean registerMathPlugins,
+    public void register(Plugins plugins, Replacements replacements, Architecture arch, boolean explicitUnsafeNullChecks, boolean registerMathPlugins,
                     boolean emitJDK9StringSubstitutions, boolean useFMAIntrinsics) {
-        register(plugins, replacementsBytecodeProvider, explicitUnsafeNullChecks, registerMathPlugins, emitJDK9StringSubstitutions, useFMAIntrinsics);
+        register(plugins, replacements, explicitUnsafeNullChecks, registerMathPlugins, emitJDK9StringSubstitutions, useFMAIntrinsics);
     }
 
-    public static void register(Plugins plugins, BytecodeProvider bytecodeProvider, boolean explicitUnsafeNullChecks,
+    public static void register(Plugins plugins, Replacements replacements, boolean explicitUnsafeNullChecks,
                     boolean registerMathPlugins, boolean emitJDK9StringSubstitutions, boolean useFMAIntrinsics) {
         InvocationPlugins invocationPlugins = plugins.getInvocationPlugins();
         invocationPlugins.defer(new Runnable() {
             @Override
             public void run() {
-                registerIntegerLongPlugins(invocationPlugins, JavaKind.Int, bytecodeProvider);
-                registerIntegerLongPlugins(invocationPlugins, JavaKind.Long, bytecodeProvider);
+                registerIntegerLongPlugins(invocationPlugins, JavaKind.Int, replacements);
+                registerIntegerLongPlugins(invocationPlugins, JavaKind.Long, replacements);
                 if (registerMathPlugins) {
                     registerMathPlugins(invocationPlugins, useFMAIntrinsics);
                 }
                 if (emitJDK9StringSubstitutions) {
-                    registerStringLatin1Plugins(invocationPlugins, bytecodeProvider);
-                    registerStringUTF16Plugins(invocationPlugins, bytecodeProvider);
+                    registerStringLatin1Plugins(invocationPlugins, replacements);
+                    registerStringUTF16Plugins(invocationPlugins, replacements);
                 }
-                registerUnsafePlugins(invocationPlugins, bytecodeProvider);
+                registerUnsafePlugins(invocationPlugins, replacements);
                 // This is temporarily disabled until we implement correct emitting of the CAS
                 // instructions of the proper width.
-                registerPlatformSpecificUnsafePlugins(invocationPlugins, bytecodeProvider, explicitUnsafeNullChecks,
+                registerPlatformSpecificUnsafePlugins(invocationPlugins, replacements, explicitUnsafeNullChecks,
                                 new JavaKind[]{JavaKind.Int, JavaKind.Long, JavaKind.Object});
             }
         });
     }
 
-    private static void registerIntegerLongPlugins(InvocationPlugins plugins, JavaKind kind, BytecodeProvider bytecodeProvider) {
+    private static void registerIntegerLongPlugins(InvocationPlugins plugins, JavaKind kind, Replacements replacements) {
         Class<?> declaringClass = kind.toBoxedJavaClass();
         Class<?> type = kind.toJavaClass();
-        Registration r = new Registration(plugins, declaringClass, bytecodeProvider);
+        Registration r = new Registration(plugins, declaringClass, replacements);
         r.register1("numberOfLeadingZeros", type, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
@@ -196,29 +196,29 @@
         });
     }
 
-    private static void registerStringLatin1Plugins(InvocationPlugins plugins, BytecodeProvider replacementsBytecodeProvider) {
+    private static void registerStringLatin1Plugins(InvocationPlugins plugins, Replacements replacements) {
         if (JavaVersionUtil.JAVA_SPEC >= 9) {
-            Registration r = new Registration(plugins, "java.lang.StringLatin1", replacementsBytecodeProvider);
+            Registration r = new Registration(plugins, "java.lang.StringLatin1", replacements);
             r.setAllowOverwrite(true);
             r.registerMethodSubstitution(AArch64StringLatin1Substitutions.class, "compareTo", byte[].class, byte[].class);
             r.registerMethodSubstitution(AArch64StringLatin1Substitutions.class, "compareToUTF16", byte[].class, byte[].class);
         }
     }
 
-    private static void registerStringUTF16Plugins(InvocationPlugins plugins, BytecodeProvider replacementsBytecodeProvider) {
+    private static void registerStringUTF16Plugins(InvocationPlugins plugins, Replacements replacements) {
         if (JavaVersionUtil.JAVA_SPEC >= 9) {
-            Registration r = new Registration(plugins, "java.lang.StringUTF16", replacementsBytecodeProvider);
+            Registration r = new Registration(plugins, "java.lang.StringUTF16", replacements);
             r.setAllowOverwrite(true);
             r.registerMethodSubstitution(AArch64StringUTF16Substitutions.class, "compareTo", byte[].class, byte[].class);
             r.registerMethodSubstitution(AArch64StringUTF16Substitutions.class, "compareToLatin1", byte[].class, byte[].class);
         }
     }
 
-    private static void registerUnsafePlugins(InvocationPlugins plugins, BytecodeProvider replacementsBytecodeProvider) {
+    private static void registerUnsafePlugins(InvocationPlugins plugins, Replacements replacements) {
         registerUnsafePlugins(new Registration(plugins, Unsafe.class),
                         new JavaKind[]{JavaKind.Int, JavaKind.Long, JavaKind.Object}, "Object");
         if (JavaVersionUtil.JAVA_SPEC > 8) {
-            registerUnsafePlugins(new Registration(plugins, "jdk.internal.misc.Unsafe", replacementsBytecodeProvider),
+            registerUnsafePlugins(new Registration(plugins, "jdk.internal.misc.Unsafe", replacements),
                             new JavaKind[]{JavaKind.Int, JavaKind.Long, JavaKind.Object},
                             JavaVersionUtil.JAVA_SPEC <= 11 ? "Object" : "Reference");
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -90,4 +90,10 @@
     public void generate(NodeLIRBuilderTool builder, ArithmeticLIRGeneratorTool gen) {
         builder.setResult(this, ((AMD64ArithmeticLIRGeneratorTool) gen).emitCountLeadingZeros(builder.operand(getValue())));
     }
+
+    @NodeIntrinsic
+    public static native int countLeadingZeros(int i);
+
+    @NodeIntrinsic
+    public static native int countLeadingZeros(long i);
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -90,4 +90,10 @@
     public void generate(NodeLIRBuilderTool builder, ArithmeticLIRGeneratorTool gen) {
         builder.setResult(this, ((AMD64ArithmeticLIRGeneratorTool) gen).emitCountTrailingZeros(builder.operand(getValue())));
     }
+
+    @NodeIntrinsic
+    public static native int countTrailingZeros(int i);
+
+    @NodeIntrinsic
+    public static native int countTrailingZeros(long i);
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64GraphBuilderPlugins.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64GraphBuilderPlugins.java	Mon Nov 04 11:25:55 2019 +0000
@@ -35,7 +35,6 @@
 
 import java.util.Arrays;
 
-import org.graalvm.compiler.bytecode.BytecodeProvider;
 import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool.RoundingMode;
 import org.graalvm.compiler.nodes.PauseNode;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -48,9 +47,8 @@
 import org.graalvm.compiler.nodes.java.AtomicReadAndAddNode;
 import org.graalvm.compiler.nodes.java.AtomicReadAndWriteNode;
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
+import org.graalvm.compiler.nodes.spi.Replacements;
 import org.graalvm.compiler.replacements.ArraysSubstitutions;
-import org.graalvm.compiler.replacements.IntegerSubstitutions;
-import org.graalvm.compiler.replacements.LongSubstitutions;
 import org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.UnsafeAccessPlugin;
 import org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.UnsafeGetPlugin;
 import org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.UnsafePutPlugin;
@@ -72,30 +70,31 @@
 
 public class AMD64GraphBuilderPlugins implements TargetGraphBuilderPlugins {
     @Override
-    public void register(Plugins plugins, BytecodeProvider replacementsBytecodeProvider, Architecture architecture, boolean explicitUnsafeNullChecks,
+    public void register(Plugins plugins, Replacements replacements, Architecture architecture, boolean explicitUnsafeNullChecks,
                     boolean registerMathPlugins, boolean emitJDK9StringSubstitutions, boolean useFMAIntrinsics) {
-        register(plugins, replacementsBytecodeProvider, (AMD64) architecture, explicitUnsafeNullChecks, emitJDK9StringSubstitutions, useFMAIntrinsics);
+        register(plugins, replacements, (AMD64) architecture, explicitUnsafeNullChecks, emitJDK9StringSubstitutions, useFMAIntrinsics);
     }
 
-    public static void register(Plugins plugins, BytecodeProvider replacementsBytecodeProvider, AMD64 arch, boolean explicitUnsafeNullChecks, boolean emitJDK9StringSubstitutions,
+    public static void register(Plugins plugins, Replacements replacements, AMD64 arch, boolean explicitUnsafeNullChecks,
+                    boolean emitJDK9StringSubstitutions,
                     boolean useFMAIntrinsics) {
         InvocationPlugins invocationPlugins = plugins.getInvocationPlugins();
         invocationPlugins.defer(new Runnable() {
             @Override
             public void run() {
                 registerThreadPlugins(invocationPlugins, arch);
-                registerIntegerLongPlugins(invocationPlugins, IntegerSubstitutions.class, JavaKind.Int, arch, replacementsBytecodeProvider);
-                registerIntegerLongPlugins(invocationPlugins, LongSubstitutions.class, JavaKind.Long, arch, replacementsBytecodeProvider);
-                registerPlatformSpecificUnsafePlugins(invocationPlugins, replacementsBytecodeProvider, explicitUnsafeNullChecks,
+                registerIntegerLongPlugins(invocationPlugins, AMD64IntegerSubstitutions.class, JavaKind.Int, arch, replacements);
+                registerIntegerLongPlugins(invocationPlugins, AMD64LongSubstitutions.class, JavaKind.Long, arch, replacements);
+                registerPlatformSpecificUnsafePlugins(invocationPlugins, replacements, explicitUnsafeNullChecks,
                                 new JavaKind[]{JavaKind.Int, JavaKind.Long, JavaKind.Object, JavaKind.Boolean, JavaKind.Byte, JavaKind.Short, JavaKind.Char, JavaKind.Float, JavaKind.Double});
-                registerUnsafePlugins(invocationPlugins, replacementsBytecodeProvider, explicitUnsafeNullChecks);
-                registerStringPlugins(invocationPlugins, replacementsBytecodeProvider);
+                registerUnsafePlugins(invocationPlugins, replacements, explicitUnsafeNullChecks);
+                registerStringPlugins(invocationPlugins, replacements);
                 if (emitJDK9StringSubstitutions) {
-                    registerStringLatin1Plugins(invocationPlugins, replacementsBytecodeProvider);
-                    registerStringUTF16Plugins(invocationPlugins, replacementsBytecodeProvider);
+                    registerStringLatin1Plugins(invocationPlugins, replacements);
+                    registerStringUTF16Plugins(invocationPlugins, replacements);
                 }
-                registerMathPlugins(invocationPlugins, useFMAIntrinsics, arch, replacementsBytecodeProvider);
-                registerArraysEqualsPlugins(invocationPlugins, replacementsBytecodeProvider);
+                registerMathPlugins(invocationPlugins, useFMAIntrinsics, arch, replacements);
+                registerArraysEqualsPlugins(invocationPlugins, replacements);
             }
         });
     }
@@ -103,72 +102,37 @@
     private static void registerThreadPlugins(InvocationPlugins plugins, AMD64 arch) {
         if (JavaVersionUtil.JAVA_SPEC > 8) {
             // Pause instruction introduced with SSE2
-            if (arch.getFeatures().contains(AMD64.CPUFeature.SSE2)) {
-                Registration r = new Registration(plugins, Thread.class);
-                r.register0("onSpinWait", new InvocationPlugin() {
-                    @Override
-                    public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
-                        b.append(new PauseNode());
-                        return true;
-                    }
-                });
-            }
-        }
-    }
-
-    private static void registerIntegerLongPlugins(InvocationPlugins plugins, Class<?> substituteDeclaringClass, JavaKind kind, AMD64 arch, BytecodeProvider bytecodeProvider) {
-        Class<?> declaringClass = kind.toBoxedJavaClass();
-        Class<?> type = kind.toJavaClass();
-        Registration r = new Registration(plugins, declaringClass, bytecodeProvider);
-        r.registerMethodSubstitution(substituteDeclaringClass, "numberOfLeadingZeros", type);
-        if (arch.getFeatures().contains(AMD64.CPUFeature.LZCNT) && arch.getFlags().contains(AMD64.Flag.UseCountLeadingZerosInstruction)) {
-            r.setAllowOverwrite(true);
-            r.register1("numberOfLeadingZeros", type, new InvocationPlugin() {
+            assert (arch.getFeatures().contains(AMD64.CPUFeature.SSE2));
+            Registration r = new Registration(plugins, Thread.class);
+            r.register0("onSpinWait", new InvocationPlugin() {
                 @Override
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-                    ValueNode folded = AMD64CountLeadingZerosNode.tryFold(value);
-                    if (folded != null) {
-                        b.addPush(JavaKind.Int, folded);
-                    } else {
-                        b.addPush(JavaKind.Int, new AMD64CountLeadingZerosNode(value));
-                    }
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
+                    b.append(new PauseNode());
                     return true;
                 }
             });
         }
-
-        r.registerMethodSubstitution(substituteDeclaringClass, "numberOfTrailingZeros", type);
-        if (arch.getFeatures().contains(AMD64.CPUFeature.BMI1) && arch.getFlags().contains(AMD64.Flag.UseCountTrailingZerosInstruction)) {
-            r.setAllowOverwrite(true);
-            r.register1("numberOfTrailingZeros", type, new InvocationPlugin() {
-
-                @Override
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-                    ValueNode folded = AMD64CountTrailingZerosNode.tryFold(value);
-                    if (folded != null) {
-                        b.addPush(JavaKind.Int, folded);
-                    } else {
-                        b.addPush(JavaKind.Int, new AMD64CountTrailingZerosNode(value));
-                    }
-                    return true;
-                }
-            });
-        }
-
-        if (arch.getFeatures().contains(AMD64.CPUFeature.POPCNT)) {
-            r.register1("bitCount", type, new InvocationPlugin() {
-                @Override
-                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-                    b.push(JavaKind.Int, b.append(new BitCountNode(value).canonical(null)));
-                    return true;
-                }
-            });
-        }
-
     }
 
-    private static void registerMathPlugins(InvocationPlugins plugins, boolean useFMAIntrinsics, AMD64 arch, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, Math.class, bytecodeProvider);
+    private static void registerIntegerLongPlugins(InvocationPlugins plugins, Class<?> substituteDeclaringClass, JavaKind kind, AMD64 arch, Replacements replacements) {
+        Class<?> declaringClass = kind.toBoxedJavaClass();
+        Class<?> type = kind.toJavaClass();
+        Registration r = new Registration(plugins, declaringClass, replacements);
+        r.registerMethodSubstitution(substituteDeclaringClass, "numberOfLeadingZeros", type);
+        r.registerMethodSubstitution(substituteDeclaringClass, "numberOfTrailingZeros", type);
+
+        r.registerConditional1(arch.getFeatures().contains(AMD64.CPUFeature.POPCNT),
+                        "bitCount", type, new InvocationPlugin() {
+                            @Override
+                            public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
+                                b.push(JavaKind.Int, b.append(new BitCountNode(value).canonical(null)));
+                                return true;
+                            }
+                        });
+    }
+
+    private static void registerMathPlugins(InvocationPlugins plugins, boolean useFMAIntrinsics, AMD64 arch, Replacements replacements) {
+        Registration r = new Registration(plugins, Math.class, replacements);
         registerUnaryMath(r, "log", LOG);
         registerUnaryMath(r, "log10", LOG10);
         registerUnaryMath(r, "exp", EXP);
@@ -177,19 +141,18 @@
         registerUnaryMath(r, "cos", COS);
         registerUnaryMath(r, "tan", TAN);
 
-        if (arch.getFeatures().contains(CPUFeature.SSE4_1)) {
-            registerRound(r, "rint", RoundingMode.NEAREST);
-            registerRound(r, "ceil", RoundingMode.UP);
-            registerRound(r, "floor", RoundingMode.DOWN);
-        }
+        boolean roundEnabled = arch.getFeatures().contains(CPUFeature.SSE4_1);
+        registerRound(roundEnabled, r, "rint", RoundingMode.NEAREST);
+        registerRound(roundEnabled, r, "ceil", RoundingMode.UP);
+        registerRound(roundEnabled, r, "floor", RoundingMode.DOWN);
 
-        if (useFMAIntrinsics && JavaVersionUtil.JAVA_SPEC > 8 && arch.getFeatures().contains(CPUFeature.FMA)) {
-            registerFMA(r);
+        if (JavaVersionUtil.JAVA_SPEC > 8) {
+            registerFMA(r, useFMAIntrinsics && arch.getFeatures().contains(CPUFeature.FMA));
         }
     }
 
-    private static void registerFMA(Registration r) {
-        r.register3("fma",
+    private static void registerFMA(Registration r, boolean isEnabled) {
+        r.registerConditional3(isEnabled, "fma",
                         Double.TYPE,
                         Double.TYPE,
                         Double.TYPE,
@@ -205,7 +168,7 @@
                                 return true;
                             }
                         });
-        r.register3("fma",
+        r.registerConditional3(isEnabled, "fma",
                         Float.TYPE,
                         Float.TYPE,
                         Float.TYPE,
@@ -243,8 +206,8 @@
         });
     }
 
-    private static void registerRound(Registration r, String name, RoundingMode mode) {
-        r.register1(name, Double.TYPE, new InvocationPlugin() {
+    private static void registerRound(boolean isEnabled, Registration r, String name, RoundingMode mode) {
+        r.registerConditional1(isEnabled, name, Double.TYPE, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
                 b.push(JavaKind.Double, b.append(new AMD64RoundNode(arg, mode)));
@@ -253,10 +216,10 @@
         });
     }
 
-    private static void registerStringPlugins(InvocationPlugins plugins, BytecodeProvider replacementsBytecodeProvider) {
+    private static void registerStringPlugins(InvocationPlugins plugins, Replacements replacements) {
         if (JavaVersionUtil.JAVA_SPEC <= 8) {
             Registration r;
-            r = new Registration(plugins, String.class, replacementsBytecodeProvider);
+            r = new Registration(plugins, String.class, replacements);
             r.setAllowOverwrite(true);
             r.registerMethodSubstitution(AMD64StringSubstitutions.class, "indexOf", char[].class, int.class,
                             int.class, char[].class, int.class, int.class, int.class);
@@ -265,8 +228,8 @@
         }
     }
 
-    private static void registerStringLatin1Plugins(InvocationPlugins plugins, BytecodeProvider replacementsBytecodeProvider) {
-        Registration r = new Registration(plugins, "java.lang.StringLatin1", replacementsBytecodeProvider);
+    private static void registerStringLatin1Plugins(InvocationPlugins plugins, Replacements replacements) {
+        Registration r = new Registration(plugins, "java.lang.StringLatin1", replacements);
         r.setAllowOverwrite(true);
         r.registerMethodSubstitution(AMD64StringLatin1Substitutions.class, "compareTo", byte[].class, byte[].class);
         r.registerMethodSubstitution(AMD64StringLatin1Substitutions.class, "compareToUTF16", byte[].class, byte[].class);
@@ -276,8 +239,8 @@
         r.registerMethodSubstitution(AMD64StringLatin1Substitutions.class, "indexOf", byte[].class, int.class, byte[].class, int.class, int.class);
     }
 
-    private static void registerStringUTF16Plugins(InvocationPlugins plugins, BytecodeProvider replacementsBytecodeProvider) {
-        Registration r = new Registration(plugins, "java.lang.StringUTF16", replacementsBytecodeProvider);
+    private static void registerStringUTF16Plugins(InvocationPlugins plugins, Replacements replacements) {
+        Registration r = new Registration(plugins, "java.lang.StringUTF16", replacements);
         r.setAllowOverwrite(true);
         r.registerMethodSubstitution(AMD64StringUTF16Substitutions.class, "compareTo", byte[].class, byte[].class);
         r.registerMethodSubstitution(AMD64StringUTF16Substitutions.class, "compareToLatin1", byte[].class, byte[].class);
@@ -288,10 +251,10 @@
         r.registerMethodSubstitution(AMD64StringUTF16Substitutions.class, "indexOfLatin1Unsafe", byte[].class, int.class, byte[].class, int.class, int.class);
     }
 
-    private static void registerUnsafePlugins(InvocationPlugins plugins, BytecodeProvider replacementsBytecodeProvider, boolean explicitUnsafeNullChecks) {
+    private static void registerUnsafePlugins(InvocationPlugins plugins, Replacements replacements, boolean explicitUnsafeNullChecks) {
         registerUnsafePlugins(new Registration(plugins, Unsafe.class), explicitUnsafeNullChecks, new JavaKind[]{JavaKind.Int, JavaKind.Long, JavaKind.Object}, true);
         if (JavaVersionUtil.JAVA_SPEC > 8) {
-            registerUnsafePlugins(new Registration(plugins, "jdk.internal.misc.Unsafe", replacementsBytecodeProvider), explicitUnsafeNullChecks,
+            registerUnsafePlugins(new Registration(plugins, "jdk.internal.misc.Unsafe", replacements), explicitUnsafeNullChecks,
                             new JavaKind[]{JavaKind.Boolean, JavaKind.Byte, JavaKind.Char, JavaKind.Short, JavaKind.Int, JavaKind.Long, JavaKind.Object},
                             JavaVersionUtil.JAVA_SPEC <= 11);
         }
@@ -330,8 +293,8 @@
         }
     }
 
-    private static void registerArraysEqualsPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, Arrays.class, bytecodeProvider);
+    private static void registerArraysEqualsPlugins(InvocationPlugins plugins, Replacements replacements) {
+        Registration r = new Registration(plugins, Arrays.class, replacements);
         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", float[].class, float[].class);
         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", double[].class, double[].class);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64IntegerSubstitutions.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019, 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.amd64;
+
+import static org.graalvm.compiler.replacements.NodeIntrinsificationProvider.INJECTED_TARGET;
+
+import org.graalvm.compiler.api.replacements.ClassSubstitution;
+import org.graalvm.compiler.api.replacements.Fold;
+import org.graalvm.compiler.api.replacements.MethodSubstitution;
+import org.graalvm.compiler.core.common.SuppressFBWarnings;
+import org.graalvm.compiler.replacements.nodes.BitScanForwardNode;
+import org.graalvm.compiler.replacements.nodes.BitScanReverseNode;
+
+import jdk.vm.ci.amd64.AMD64;
+import jdk.vm.ci.code.TargetDescription;
+
+@ClassSubstitution(Integer.class)
+public class AMD64IntegerSubstitutions {
+
+    @Fold
+    static boolean lzcnt(@Fold.InjectedParameter TargetDescription target) {
+        AMD64 arch = (AMD64) target.arch;
+        return arch.getFeatures().contains(AMD64.CPUFeature.LZCNT) && arch.getFlags().contains(AMD64.Flag.UseCountLeadingZerosInstruction);
+    }
+
+    @Fold
+    static boolean tzcnt(@Fold.InjectedParameter TargetDescription target) {
+        AMD64 arch = (AMD64) target.arch;
+        return arch.getFeatures().contains(AMD64.CPUFeature.BMI1) && arch.getFlags().contains(AMD64.Flag.UseCountTrailingZerosInstruction);
+    }
+
+    @MethodSubstitution
+    @SuppressFBWarnings(value = "NP_NULL_PARAM_DEREF_NONVIRTUAL", justification = "foldable method parameters are injected")
+    public static int numberOfLeadingZeros(int i) {
+        if (lzcnt(INJECTED_TARGET)) {
+            return AMD64CountLeadingZerosNode.countLeadingZeros(i);
+        }
+        if (i == 0) {
+            return 32;
+        }
+        return 31 - BitScanReverseNode.unsafeScan(i);
+    }
+
+    @MethodSubstitution
+    @SuppressFBWarnings(value = "NP_NULL_PARAM_DEREF_NONVIRTUAL", justification = "foldable method parameters are injected")
+    public static int numberOfTrailingZeros(int i) {
+        if (tzcnt(INJECTED_TARGET)) {
+            return AMD64CountTrailingZerosNode.countTrailingZeros(i);
+        }
+        if (i == 0) {
+            return 32;
+        }
+        return BitScanForwardNode.unsafeScan(i);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64LongSubstitutions.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019, 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.amd64;
+
+// JaCoCo Exclude
+
+import static org.graalvm.compiler.replacements.NodeIntrinsificationProvider.INJECTED_TARGET;
+
+import org.graalvm.compiler.api.replacements.ClassSubstitution;
+import org.graalvm.compiler.api.replacements.Fold;
+import org.graalvm.compiler.api.replacements.MethodSubstitution;
+import org.graalvm.compiler.core.common.SuppressFBWarnings;
+import org.graalvm.compiler.replacements.nodes.BitScanForwardNode;
+import org.graalvm.compiler.replacements.nodes.BitScanReverseNode;
+
+import jdk.vm.ci.amd64.AMD64;
+import jdk.vm.ci.code.TargetDescription;
+
+@ClassSubstitution(Long.class)
+public class AMD64LongSubstitutions {
+
+    @Fold
+    static boolean lzcnt(@Fold.InjectedParameter TargetDescription target) {
+        AMD64 arch = (AMD64) target.arch;
+        return arch.getFeatures().contains(AMD64.CPUFeature.LZCNT) && arch.getFlags().contains(AMD64.Flag.UseCountLeadingZerosInstruction);
+    }
+
+    @Fold
+    static boolean tzcnt(@Fold.InjectedParameter TargetDescription target) {
+        AMD64 arch = (AMD64) target.arch;
+        return arch.getFeatures().contains(AMD64.CPUFeature.BMI1) && arch.getFlags().contains(AMD64.Flag.UseCountTrailingZerosInstruction);
+    }
+
+    @MethodSubstitution
+    @SuppressFBWarnings(value = "NP_NULL_PARAM_DEREF_NONVIRTUAL", justification = "foldable method parameters are injected")
+    public static int numberOfLeadingZeros(long i) {
+        if (lzcnt(INJECTED_TARGET)) {
+            return AMD64CountLeadingZerosNode.countLeadingZeros(i);
+        }
+        if (i == 0) {
+            return 64;
+        }
+        return 63 - BitScanReverseNode.unsafeScan(i);
+    }
+
+    @MethodSubstitution
+    @SuppressFBWarnings(value = "NP_NULL_PARAM_DEREF_NONVIRTUAL", justification = "foldable method parameters are injected")
+    public static int numberOfTrailingZeros(long i) {
+        if (tzcnt(INJECTED_TARGET)) {
+            return AMD64CountTrailingZerosNode.countTrailingZeros(i);
+        }
+
+        if (i == 0) {
+            return 64;
+        }
+        return BitScanForwardNode.unsafeScan(i);
+    }
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64StringLatin1InflateNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64StringLatin1InflateNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -82,7 +82,7 @@
     }
 
     @Override
-    public LocationIdentity[] getLocationIdentities() {
+    public LocationIdentity[] getKilledLocationIdentities() {
         // Model write access via 'dst' using:
         return new LocationIdentity[]{NamedLocationIdentity.getArrayLocation(writeKind)};
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64StringUTF16CompressNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64StringUTF16CompressNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -82,7 +82,7 @@
     }
 
     @Override
-    public LocationIdentity[] getLocationIdentities() {
+    public LocationIdentity[] getKilledLocationIdentities() {
         // Model write access via 'dst' using:
         return new LocationIdentity[]{NamedLocationIdentity.getArrayLocation(JavaKind.Byte)};
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.jdk9.test/src/org/graalvm/compiler/replacements/jdk9/test/VarHandleTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.jdk9.test/src/org/graalvm/compiler/replacements/jdk9/test/VarHandleTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -162,12 +162,12 @@
                 startNodes++;
             } else if (n instanceof MemoryCheckpoint.Single) {
                 MemoryCheckpoint.Single single = (MemoryCheckpoint.Single) n;
-                if (single.getLocationIdentity().isAny()) {
+                if (single.getKilledLocationIdentity().isAny()) {
                     anyKillCount++;
                 }
             } else if (n instanceof MemoryCheckpoint.Multi) {
                 MemoryCheckpoint.Multi multi = (MemoryCheckpoint.Multi) n;
-                for (LocationIdentity loc : multi.getLocationIdentities()) {
+                for (LocationIdentity loc : multi.getKilledLocationIdentities()) {
                     if (loc.isAny()) {
                         anyKillCount++;
                         break;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.sparc/src/org/graalvm/compiler/replacements/sparc/SPARCGraphBuilderPlugins.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.sparc/src/org/graalvm/compiler/replacements/sparc/SPARCGraphBuilderPlugins.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -32,13 +32,13 @@
 import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.SIN;
 import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.TAN;
 
-import org.graalvm.compiler.bytecode.BytecodeProvider;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
+import org.graalvm.compiler.nodes.spi.Replacements;
 import org.graalvm.compiler.replacements.IntegerSubstitutions;
 import org.graalvm.compiler.replacements.LongSubstitutions;
 import org.graalvm.compiler.replacements.nodes.BinaryMathIntrinsicNode;
@@ -51,26 +51,26 @@
 
 public class SPARCGraphBuilderPlugins {
 
-    public static void register(Plugins plugins, BytecodeProvider bytecodeProvider, boolean explicitUnsafeNullChecks) {
+    public static void register(Plugins plugins, Replacements replacements, boolean explicitUnsafeNullChecks) {
         InvocationPlugins invocationPlugins = plugins.getInvocationPlugins();
         invocationPlugins.defer(new Runnable() {
             @Override
             public void run() {
-                registerIntegerLongPlugins(invocationPlugins, IntegerSubstitutions.class, JavaKind.Int, bytecodeProvider);
-                registerIntegerLongPlugins(invocationPlugins, LongSubstitutions.class, JavaKind.Long, bytecodeProvider);
+                registerIntegerLongPlugins(invocationPlugins, IntegerSubstitutions.class, JavaKind.Int, replacements);
+                registerIntegerLongPlugins(invocationPlugins, LongSubstitutions.class, JavaKind.Long, replacements);
                 registerMathPlugins(invocationPlugins);
                 // This is temporarily disabled until we implement correct emitting of the CAS
                 // instructions of the proper width.
-                registerPlatformSpecificUnsafePlugins(invocationPlugins, bytecodeProvider, explicitUnsafeNullChecks,
+                registerPlatformSpecificUnsafePlugins(invocationPlugins, replacements, explicitUnsafeNullChecks,
                                 new JavaKind[]{JavaKind.Int, JavaKind.Long, JavaKind.Object});
             }
         });
     }
 
-    private static void registerIntegerLongPlugins(InvocationPlugins plugins, Class<?> substituteDeclaringClass, JavaKind kind, BytecodeProvider bytecodeProvider) {
+    private static void registerIntegerLongPlugins(InvocationPlugins plugins, Class<?> substituteDeclaringClass, JavaKind kind, Replacements replacements) {
         Class<?> declaringClass = kind.toBoxedJavaClass();
         Class<?> type = kind.toJavaClass();
-        Registration r = new Registration(plugins, declaringClass, bytecodeProvider);
+        Registration r = new Registration(plugins, declaringClass, replacements);
         r.registerMethodSubstitution(substituteDeclaringClass, "numberOfLeadingZeros", type);
         r.registerMethodSubstitution(substituteDeclaringClass, "numberOfTrailingZeros", type);
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ArraysSubstitutionsTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ArraysSubstitutionsTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -31,7 +31,6 @@
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.replacements.nodes.ArrayEqualsNode;
 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
@@ -132,7 +131,7 @@
         StructuredGraph graph = parseEager("testCanonicalLengthSnippet", AllowAssumptions.NO);
         HighTierContext context = new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
         createInliningPhase().apply(graph, context);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
 
         Assert.assertTrue(graph.getNodes(ReturnNode.TYPE).first().result().asJavaConstant().asLong() == 0);
     }
@@ -148,7 +147,7 @@
         StructuredGraph graph = parseEager("testCanonicalEqualSnippet", AllowAssumptions.NO);
         HighTierContext context = new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
         createInliningPhase().apply(graph, context);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
 
         Assert.assertTrue(graph.getNodes(ReturnNode.TYPE).first().result().asJavaConstant().asLong() == 1);
     }
@@ -162,9 +161,9 @@
         StructuredGraph graph = parseEager("testVirtualEqualSnippet", AllowAssumptions.NO);
         HighTierContext context = new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
         createInliningPhase().apply(graph, context);
-        new CanonicalizerPhase().apply(graph, getProviders());
-        new PartialEscapePhase(false, new CanonicalizerPhase(), graph.getOptions()).apply(graph, context);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
+        new PartialEscapePhase(false, this.createCanonicalizerPhase(), graph.getOptions()).apply(graph, context);
+        createCanonicalizerPhase().apply(graph, getProviders());
 
         Assert.assertTrue(graph.getNodes(ReturnNode.TYPE).first().result().asJavaConstant().asLong() == 1);
     }
@@ -180,9 +179,9 @@
         StructuredGraph graph = parseEager("testVirtualNotEqualSnippet", AllowAssumptions.NO);
         HighTierContext context = getDefaultHighTierContext();
         createInliningPhase().apply(graph, context);
-        new CanonicalizerPhase().apply(graph, getProviders());
-        new PartialEscapePhase(false, new CanonicalizerPhase(), graph.getOptions()).apply(graph, context);
-        new CanonicalizerPhase().apply(graph, getProviders());
+        createCanonicalizerPhase().apply(graph, getProviders());
+        new PartialEscapePhase(false, this.createCanonicalizerPhase(), graph.getOptions()).apply(graph, context);
+        createCanonicalizerPhase().apply(graph, getProviders());
 
         Assert.assertTrue(graph.getNodes(ReturnNode.TYPE).first().result().asJavaConstant().asLong() == 0);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -303,7 +303,7 @@
     private ValueNode parseAndInline(String name, Class<? extends ValueNode> expectedClass) {
         StructuredGraph graph = parseEager(name, AllowAssumptions.YES);
         HighTierContext context = getDefaultHighTierContext();
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         canonicalizer.apply(graph, context);
         createInliningPhase(canonicalizer).apply(graph, context);
         canonicalizer.apply(graph, context);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnExceptionTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnExceptionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -35,7 +35,7 @@
 import org.graalvm.compiler.options.OptionValues;
 import org.graalvm.compiler.phases.common.AbstractInliningPhase;
 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
-import org.graalvm.compiler.test.ExportingClassLoader;
+import org.graalvm.compiler.api.test.ExportingClassLoader;
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Test;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/EdgesTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/EdgesTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -45,7 +45,6 @@
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.java.InstanceOfNode;
 import org.graalvm.compiler.options.OptionValues;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
 import org.graalvm.compiler.phases.common.inlining.policy.InlineMethodSubstitutionsPolicy;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
@@ -133,8 +132,8 @@
         ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(method);
         StructuredGraph g = parseProfiled(javaMethod, AllowAssumptions.NO);
         HighTierContext context = getDefaultHighTierContext();
-        new InliningPhase(new InlineMethodSubstitutionsPolicy(), new CanonicalizerPhase()).apply(g, context);
-        new CanonicalizerPhase().apply(g, context);
+        new InliningPhase(new InlineMethodSubstitutionsPolicy(), createCanonicalizerPhase()).apply(g, context);
+        this.createCanonicalizerPhase().apply(g, context);
         Assert.assertTrue(g.getNodes().filter(InstanceOfNode.class).isEmpty());
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/FoldTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/FoldTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -81,10 +81,10 @@
 
     @Override
     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
-        InjectionProvider injection = new NodeIntrinsificationProvider(getMetaAccess(), getSnippetReflection(), getProviders().getForeignCalls(), null);
+        InjectionProvider injection = new NodeIntrinsificationProvider(getMetaAccess(), getSnippetReflection(), getProviders().getForeignCalls(), null, getTarget());
         new PluginFactory_FoldTest().registerPlugins(invocationPlugins, injection);
         BytecodeProvider replacementBytecodeProvider = getSystemClassLoaderBytecodeProvider();
-        Registration r = new Registration(invocationPlugins, TestMethod.class, replacementBytecodeProvider);
+        Registration r = new Registration(invocationPlugins, TestMethod.class, getReplacements(), replacementBytecodeProvider);
         r.registerMethodSubstitution(TestMethodSubstitution.class, "test");
         super.registerInvocationPlugins(invocationPlugins);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -94,7 +94,7 @@
         Node originalNode = graph.getNodes().filter(x -> x instanceof IntegerExactArithmeticNode).first();
         assertNotNull("original node must be in the graph", originalNode);
 
-        new CanonicalizerPhase().apply(graph, getDefaultHighTierContext());
+        createCanonicalizerPhase().apply(graph, getDefaultHighTierContext());
 
         ValueNode node = findNode(graph);
         boolean overflowExpected = node instanceof IntegerExactArithmeticNode;
@@ -109,19 +109,19 @@
 
         Node originalNode = graph.getNodes().filter(x -> x instanceof IntegerExactArithmeticNode).first();
         assertNotNull("original node must be in the graph", originalNode);
-        CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+        CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
         HighTierContext highTierContext = getDefaultHighTierContext();
         new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highTierContext);
         MidTierContext midTierContext = getDefaultMidTierContext();
         new GuardLoweringPhase().apply(graph, midTierContext);
-        new CanonicalizerPhase().apply(graph, midTierContext);
+        createCanonicalizerPhase().apply(graph, midTierContext);
 
         IntegerExactArithmeticSplitNode loweredNode = graph.getNodes().filter(IntegerExactArithmeticSplitNode.class).first();
         assertNotNull("the lowered node must be in the graph", loweredNode);
 
         loweredNode.getX().setStamp(StampFactory.forInteger(bits, lowerBoundA, upperBoundA));
         loweredNode.getY().setStamp(StampFactory.forInteger(bits, lowerBoundB, upperBoundB));
-        new CanonicalizerPhase().apply(graph, midTierContext);
+        createCanonicalizerPhase().apply(graph, midTierContext);
 
         ValueNode node = findNode(graph);
         boolean overflowExpected = node instanceof IntegerExactArithmeticSplitNode;
@@ -144,7 +144,7 @@
         String snippet = "snippetInt" + bits;
         StructuredGraph graph = parseEager(getResolvedJavaMethod(operation.getClass(), snippet), AllowAssumptions.NO);
         HighTierContext context = getDefaultHighTierContext();
-        new CanonicalizerPhase().apply(graph, context);
+        createCanonicalizerPhase().apply(graph, context);
         return graph;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -25,6 +25,7 @@
 package org.graalvm.compiler.replacements.test;
 
 import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
 
 import org.graalvm.compiler.api.replacements.MethodSubstitution;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
@@ -35,7 +36,6 @@
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.common.LoweringPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
@@ -78,14 +78,14 @@
             debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
             createInliningPhase().apply(graph, context);
             debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
-            new CanonicalizerPhase().apply(graph, context);
+            createCanonicalizerPhase().apply(graph, context);
             new DeadCodeEliminationPhase().apply(graph);
             // Try to ensure any macro nodes are lowered to expose any resulting invokes
             if (graph.getNodes().filter(MacroNode.class).isNotEmpty()) {
-                new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+                new LoweringPhase(this.createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
             }
             if (graph.getNodes().filter(MacroNode.class).isNotEmpty()) {
-                new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, context);
+                new LoweringPhase(this.createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, context);
             }
             assertNotInGraph(graph, MacroNode.class);
             if (name != null) {
@@ -153,13 +153,20 @@
         }
     }
 
-    protected static StructuredGraph assertInGraph(StructuredGraph graph, Class<?> clazz) {
+    protected static StructuredGraph assertInGraph(StructuredGraph graph, Class<?>... clazzes) {
         for (Node node : graph.getNodes()) {
-            if (clazz.isInstance(node)) {
-                return graph;
+            for (Class<?> clazz : clazzes) {
+                if (clazz.isInstance(node)) {
+                    return graph;
+                }
             }
         }
-        fail("Graph does not contain a node of class " + clazz.getName());
+        if (clazzes.length == 1) {
+            fail("Graph does not contain a node of class " + clazzes[0].getName());
+        } else {
+            fail("Graph does not contain a node of one these classes class " + Arrays.toString(clazzes));
+
+        }
         return graph;
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -130,7 +130,7 @@
         Assert.assertEquals(graph.getParameter(0), address.getBase());
         Assert.assertEquals(BytecodeFrame.AFTER_BCI, write.stateAfter().bci);
 
-        Assert.assertEquals(locationIdentity, write.getLocationIdentity());
+        Assert.assertEquals(locationIdentity, write.getKilledLocationIdentity());
 
         if (indexConvert) {
             SignExtendNode convert = (SignExtendNode) address.getOffset();
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PEGraphDecoderTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -46,7 +46,6 @@
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
 import org.graalvm.compiler.nodes.spi.CoreProviders;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.replacements.CachingPEGraphDecoder;
 import jdk.internal.vm.compiler.word.LocationIdentity;
 import org.junit.Test;
@@ -146,7 +145,7 @@
             targetGraph.verify();
 
             CoreProviders context = getProviders();
-            new CanonicalizerPhase().apply(targetGraph, context);
+            createCanonicalizerPhase().apply(targetGraph, context);
             targetGraph.verify();
 
         } catch (Throwable ex) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -37,7 +37,6 @@
 import org.graalvm.compiler.nodes.extended.JavaWriteNode;
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
 import org.graalvm.compiler.phases.OptimisticOptimizations;
-import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.tiers.HighTierContext;
 import org.graalvm.compiler.word.Word;
 import org.graalvm.compiler.word.WordCastNode;
@@ -150,7 +149,7 @@
         Assert.assertEquals(graph.getParameter(0), cast.getInput());
         Assert.assertEquals(target.wordJavaKind, cast.stamp(NodeView.DEFAULT).getStackKind());
 
-        Assert.assertEquals(locationIdentity, write.getLocationIdentity());
+        Assert.assertEquals(locationIdentity, write.getKilledLocationIdentity());
 
         if (indexConvert) {
             SignExtendNode convert = (SignExtendNode) address.getOffset();
@@ -409,7 +408,7 @@
         HighTierContext context = new HighTierContext(getProviders(), null, OptimisticOptimizations.ALL);
 
         StructuredGraph graph = parseEager(snippetName, AllowAssumptions.YES);
-        new CanonicalizerPhase().apply(graph, context);
+        this.createCanonicalizerPhase().apply(graph, context);
         Assert.assertEquals(expectedWordCasts, graph.getNodes().filter(WordCastNode.class).count());
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ReplacementsParseTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -318,7 +318,7 @@
     @Override
     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
         BytecodeProvider replacementBytecodeProvider = getSystemClassLoaderBytecodeProvider();
-        Registration r = new Registration(invocationPlugins, TestObject.class, replacementBytecodeProvider);
+        Registration r = new Registration(invocationPlugins, TestObject.class, getReplacements(), replacementBytecodeProvider);
         NodeIntrinsicPluginFactory.InjectionProvider injections = new DummyInjectionProvider();
         new PluginFactory_ReplacementsParseTest().registerPlugins(invocationPlugins, injections);
         r.registerMethodSubstitution(TestObjectSubstitutions.class, "nextAfter", double.class, double.class);
@@ -634,7 +634,7 @@
                 node.remove();
             }
             HighTierContext context = getDefaultHighTierContext();
-            CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
+            CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
             new FloatingReadPhase().apply(graph);
             canonicalizer.apply(graph, context);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/StandardMethodSubstitutionsTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/StandardMethodSubstitutionsTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -31,6 +31,8 @@
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.calc.AbsNode;
 import org.graalvm.compiler.nodes.calc.ReinterpretNode;
+import org.graalvm.compiler.replacements.amd64.AMD64CountLeadingZerosNode;
+import org.graalvm.compiler.replacements.amd64.AMD64CountTrailingZerosNode;
 import org.graalvm.compiler.replacements.nodes.BitCountNode;
 import org.graalvm.compiler.replacements.nodes.BitScanForwardNode;
 import org.graalvm.compiler.replacements.nodes.BitScanReverseNode;
@@ -132,7 +134,7 @@
         return Math.sqrt(value) + Math.log(value) + Math.log10(value) + Math.sin(value) + Math.cos(value) + Math.tan(value);
     }
 
-    public void testSubstitution(String testMethodName, Class<?> intrinsicClass, Class<?> holder, String methodName, boolean optional, Object... args) {
+    public void testSubstitution(String testMethodName, Class<?> holder, String methodName, boolean optional, Object[] args, Class<?>... intrinsicClasses) {
         ResolvedJavaMethod realJavaMethod = getResolvedJavaMethod(holder, methodName);
         ResolvedJavaMethod testJavaMethod = getResolvedJavaMethod(testMethodName);
         StructuredGraph graph = testGraph(testMethodName);
@@ -140,7 +142,7 @@
         // Check to see if the resulting graph contains the expected node
         StructuredGraph replacement = getReplacements().getSubstitution(realJavaMethod, -1, false, null, graph.getOptions());
         if (replacement == null && !optional) {
-            assertInGraph(graph, intrinsicClass);
+            assertInGraph(graph, intrinsicClasses);
         }
 
         for (Object l : args) {
@@ -159,7 +161,7 @@
     public void testCharSubstitutions() {
         Object[] args = new Character[]{Character.MIN_VALUE, (char) -1, (char) 0, (char) 1, Character.MAX_VALUE};
 
-        testSubstitution("charReverseBytes", ReverseBytesNode.class, Character.class, "reverseBytes", false, args);
+        testSubstitution("charReverseBytes", Character.class, "reverseBytes", false, args, ReverseBytesNode.class);
     }
 
     public static char charReverseBytes(char value) {
@@ -183,7 +185,7 @@
     public void testShortSubstitutions() {
         Object[] args = new Short[]{Short.MIN_VALUE, -1, 0, 1, Short.MAX_VALUE};
 
-        testSubstitution("shortReverseBytes", ReverseBytesNode.class, Short.class, "reverseBytes", false, args);
+        testSubstitution("shortReverseBytes", Short.class, "reverseBytes", false, args, ReverseBytesNode.class);
     }
 
     public static short shortReverseBytes(short value) {
@@ -207,10 +209,10 @@
     public void testIntegerSubstitutions() {
         Object[] args = new Object[]{Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE};
 
-        testSubstitution("integerReverseBytes", ReverseBytesNode.class, Integer.class, "reverseBytes", false, args);
-        testSubstitution("integerNumberOfLeadingZeros", BitScanReverseNode.class, Integer.class, "numberOfLeadingZeros", true, args);
-        testSubstitution("integerNumberOfTrailingZeros", BitScanForwardNode.class, Integer.class, "numberOfTrailingZeros", false, args);
-        testSubstitution("integerBitCount", BitCountNode.class, Integer.class, "bitCount", true, args);
+        testSubstitution("integerReverseBytes", Integer.class, "reverseBytes", false, args, ReverseBytesNode.class);
+        testSubstitution("integerNumberOfLeadingZeros", Integer.class, "numberOfLeadingZeros", true, args, BitScanReverseNode.class, AMD64CountLeadingZerosNode.class);
+        testSubstitution("integerNumberOfTrailingZeros", Integer.class, "numberOfTrailingZeros", false, args, BitScanForwardNode.class, AMD64CountTrailingZerosNode.class);
+        testSubstitution("integerBitCount", Integer.class, "bitCount", true, args, BitCountNode.class);
     }
 
     public static int integerReverseBytes(int value) {
@@ -233,10 +235,10 @@
     public void testLongSubstitutions() {
         Object[] args = new Object[]{Long.MIN_VALUE, -1L, 0L, 1L, Long.MAX_VALUE};
 
-        testSubstitution("longReverseBytes", ReverseBytesNode.class, Long.class, "reverseBytes", false, args);
-        testSubstitution("longNumberOfLeadingZeros", BitScanReverseNode.class, Long.class, "numberOfLeadingZeros", true, args);
-        testSubstitution("longNumberOfTrailingZeros", BitScanForwardNode.class, Long.class, "numberOfTrailingZeros", false, args);
-        testSubstitution("longBitCount", BitCountNode.class, Long.class, "bitCount", true, args);
+        testSubstitution("longReverseBytes", Long.class, "reverseBytes", false, args, ReverseBytesNode.class);
+        testSubstitution("longNumberOfLeadingZeros", Long.class, "numberOfLeadingZeros", true, args, BitScanReverseNode.class, AMD64CountLeadingZerosNode.class);
+        testSubstitution("longNumberOfTrailingZeros", Long.class, "numberOfTrailingZeros", false, args, BitScanForwardNode.class, AMD64CountTrailingZerosNode.class);
+        testSubstitution("longBitCount", Long.class, "bitCount", true, args, BitCountNode.class);
     }
 
     public static long longReverseBytes(long value) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SubstitutionNodeSourcePositionTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SubstitutionNodeSourcePositionTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -84,7 +84,7 @@
     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
         new PluginFactory_SubstitutionNodeSourcePositionTest().registerPlugins(invocationPlugins, null);
         ClassfileBytecodeProvider bytecodeProvider = getSystemClassLoaderBytecodeProvider();
-        InvocationPlugins.Registration r = new InvocationPlugins.Registration(invocationPlugins, TestMethod.class, bytecodeProvider);
+        InvocationPlugins.Registration r = new InvocationPlugins.Registration(invocationPlugins, TestMethod.class, getReplacements(), bytecodeProvider);
         r.registerMethodSubstitution(TestMethodSubstitution.class, "test", int.class);
         super.registerInvocationPlugins(invocationPlugins);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SubstitutionsTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SubstitutionsTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -120,7 +120,7 @@
     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
         new PluginFactory_SubstitutionsTest().registerPlugins(invocationPlugins, null);
         ClassfileBytecodeProvider bytecodeProvider = getSystemClassLoaderBytecodeProvider();
-        Registration r = new Registration(invocationPlugins, TestMethod.class, bytecodeProvider);
+        Registration r = new Registration(invocationPlugins, TestMethod.class, getReplacements(), bytecodeProvider);
         r.registerMethodSubstitution(TestMethodSubstitution.class, "test");
         super.registerInvocationPlugins(invocationPlugins);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/classfile/ClassfileBytecodeProviderTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/classfile/ClassfileBytecodeProviderTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -104,7 +104,7 @@
 import org.graalvm.compiler.replacements.classfile.ClassfileBytecodeProvider;
 import org.graalvm.compiler.runtime.RuntimeProvider;
 import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
-import org.graalvm.compiler.test.ModuleSupport;
+import org.graalvm.compiler.api.test.ModuleSupport;
 import org.graalvm.compiler.test.SubprocessUtil;
 import org.junit.Assert;
 import org.junit.Assume;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/classfile/RedefineIntrinsicTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/classfile/RedefineIntrinsicTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -87,7 +87,7 @@
     @Override
     protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
         BytecodeProvider replacementBytecodeProvider = getSystemClassLoaderBytecodeProvider();
-        Registration r = new Registration(invocationPlugins, Original.class, replacementBytecodeProvider);
+        Registration r = new Registration(invocationPlugins, Original.class, getReplacements(), replacementBytecodeProvider);
         r.registerMethodSubstitution(Intrinsic.class, "getValue");
         super.registerInvocationPlugins(invocationPlugins);
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/CachingPEGraphDecoder.java	Mon Nov 04 11:25:55 2019 +0000
@@ -132,7 +132,7 @@
                             : null;
             GraphBuilderPhase.Instance graphBuilderPhaseInstance = createGraphBuilderPhaseInstance(initialIntrinsicContext);
             graphBuilderPhaseInstance.apply(graphToEncode);
-            new CanonicalizerPhase().apply(graphToEncode, providers);
+            CanonicalizerPhase.create().apply(graphToEncode, providers);
             if (postParsingPhase != null) {
                 postParsingPhase.apply(graphToEncode, providers);
             }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java	Mon Nov 04 11:25:55 2019 +0000
@@ -47,6 +47,7 @@
 import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
+import org.graalvm.compiler.core.common.GraalOptions;
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
 import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
@@ -129,6 +130,7 @@
 import org.graalvm.compiler.nodes.java.ValueCompareAndSwapNode;
 import org.graalvm.compiler.nodes.memory.HeapAccess.BarrierType;
 import org.graalvm.compiler.nodes.memory.ReadNode;
+import org.graalvm.compiler.nodes.memory.VolatileReadNode;
 import org.graalvm.compiler.nodes.memory.WriteNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.memory.address.IndexAddressNode;
@@ -397,12 +399,17 @@
         AddressNode address = createFieldAddress(graph, object, field);
         assert address != null : "Field that is loaded must not be eliminated: " + field.getDeclaringClass().toJavaName(true) + "." + field.getName();
 
-        ReadNode memoryRead = graph.add(new ReadNode(address, fieldLocationIdentity(field), loadStamp, fieldLoadBarrierType(field)));
+        ReadNode memoryRead = null;
+        if (loadField.isVolatile() && GraalOptions.LateMembars.getValue(graph.getOptions())) {
+            memoryRead = graph.add(new VolatileReadNode(address, fieldLocationIdentity(field), loadStamp, fieldLoadBarrierType(field)));
+        } else {
+            memoryRead = graph.add(new ReadNode(address, fieldLocationIdentity(field), loadStamp, fieldLoadBarrierType(field)));
+        }
         ValueNode readValue = implicitLoadConvert(graph, getStorageKind(field), memoryRead);
         loadField.replaceAtUsages(readValue);
         graph.replaceFixed(loadField, memoryRead);
 
-        if (loadField.isVolatile()) {
+        if (loadField.isVolatile() && !GraalOptions.LateMembars.getValue(graph.getOptions())) {
             MembarNode preMembar = graph.add(new MembarNode(JMM_PRE_VOLATILE_READ));
             graph.addBeforeFixed(memoryRead, preMembar);
             MembarNode postMembar = graph.add(new MembarNode(JMM_POST_VOLATILE_READ));
@@ -419,11 +426,11 @@
         AddressNode address = createFieldAddress(graph, object, field);
         assert address != null;
 
-        WriteNode memoryWrite = graph.add(new WriteNode(address, fieldLocationIdentity(field), value, fieldStoreBarrierType(storeField.field())));
+        WriteNode memoryWrite = graph.add(new WriteNode(address, fieldLocationIdentity(field), value, fieldStoreBarrierType(storeField.field()), storeField.isVolatile()));
         memoryWrite.setStateAfter(storeField.stateAfter());
         graph.replaceFixedWithFixed(storeField, memoryWrite);
 
-        if (storeField.isVolatile()) {
+        if (storeField.isVolatile() && !GraalOptions.LateMembars.getValue(graph.getOptions())) {
             MembarNode preMembar = graph.add(new MembarNode(JMM_PRE_VOLATILE_WRITE));
             graph.addBeforeFixed(memoryWrite, preMembar);
             MembarNode postMembar = graph.add(new MembarNode(JMM_POST_VOLATILE_WRITE));
@@ -528,7 +535,7 @@
 
         AddressNode address = createArrayIndexAddress(graph, array, elementKind, storeIndexed.index(), boundsCheck);
         WriteNode memoryWrite = graph.add(new WriteNode(address, NamedLocationIdentity.getArrayLocation(elementKind), implicitStoreConvert(graph, elementKind, value),
-                        arrayStoreBarrierType(storeIndexed.elementKind())));
+                        arrayStoreBarrierType(storeIndexed.elementKind()), false));
         memoryWrite.setGuard(boundsCheck);
         if (condition != null) {
             tool.createGuard(storeIndexed, condition, DeoptimizationReason.ArrayStoreException, DeoptimizationAction.InvalidateReprofile);
@@ -623,7 +630,7 @@
 
         AddressNode address = graph.unique(new OffsetAddressNode(cas.object(), cas.offset()));
         BarrierType barrierType = guessStoreBarrierType(cas.object(), expectedValue);
-        LogicCompareAndSwapNode atomicNode = graph.add(new LogicCompareAndSwapNode(address, cas.getLocationIdentity(), expectedValue, newValue, barrierType));
+        LogicCompareAndSwapNode atomicNode = graph.add(new LogicCompareAndSwapNode(address, cas.getKilledLocationIdentity(), expectedValue, newValue, barrierType));
         atomicNode.setStateAfter(cas.stateAfter());
         graph.replaceFixedWithFixed(cas, atomicNode);
     }
@@ -637,7 +644,7 @@
 
         AddressNode address = graph.unique(new OffsetAddressNode(cas.object(), cas.offset()));
         BarrierType barrierType = guessStoreBarrierType(cas.object(), expectedValue);
-        ValueCompareAndSwapNode atomicNode = graph.add(new ValueCompareAndSwapNode(address, expectedValue, newValue, cas.getLocationIdentity(), barrierType));
+        ValueCompareAndSwapNode atomicNode = graph.add(new ValueCompareAndSwapNode(address, expectedValue, newValue, cas.getKilledLocationIdentity(), barrierType));
         ValueNode coercedNode = implicitLoadConvert(graph, valueKind, atomicNode, true);
         atomicNode.setStateAfter(cas.stateAfter());
         cas.replaceAtUsages(coercedNode);
@@ -653,7 +660,7 @@
         AddressNode address = graph.unique(new OffsetAddressNode(n.object(), n.offset()));
         BarrierType barrierType = guessStoreBarrierType(n.object(), n.newValue());
         LIRKind lirAccessKind = LIRKind.fromJavaKind(target.arch, valueKind);
-        LoweredAtomicReadAndWriteNode memoryRead = graph.add(new LoweredAtomicReadAndWriteNode(address, n.getLocationIdentity(), newValue, lirAccessKind, barrierType));
+        LoweredAtomicReadAndWriteNode memoryRead = graph.add(new LoweredAtomicReadAndWriteNode(address, n.getKilledLocationIdentity(), newValue, lirAccessKind, barrierType));
         memoryRead.setStateAfter(n.stateAfter());
 
         ValueNode readValue = implicitLoadConvert(graph, valueKind, memoryRead);
@@ -744,7 +751,7 @@
         JavaKind valueKind = store.accessKind();
         ValueNode value = implicitStoreConvert(graph, valueKind, store.value(), compressible);
         AddressNode address = createUnsafeAddress(graph, store.object(), store.offset());
-        WriteNode write = graph.add(new WriteNode(address, store.getLocationIdentity(), value, unsafeStoreBarrierType(store)));
+        WriteNode write = graph.add(new WriteNode(address, store.getKilledLocationIdentity(), value, unsafeStoreBarrierType(store), false));
         write.setStateAfter(store.stateAfter());
         graph.replaceFixedWithFixed(store, write);
     }
@@ -755,7 +762,7 @@
         JavaKind valueKind = store.getKind();
         ValueNode value = implicitStoreConvert(graph, valueKind, store.getValue(), false);
         AddressNode address = graph.addOrUniqueWithInputs(OffsetAddressNode.create(store.getAddress()));
-        WriteNode write = graph.add(new WriteNode(address, store.getLocationIdentity(), value, BarrierType.NONE));
+        WriteNode write = graph.add(new WriteNode(address, store.getKilledLocationIdentity(), value, BarrierType.NONE, false));
         write.setStateAfter(store.stateAfter());
         graph.replaceFixedWithFixed(store, write);
     }
@@ -782,7 +789,7 @@
     protected void lowerJavaWriteNode(JavaWriteNode write) {
         StructuredGraph graph = write.graph();
         ValueNode value = implicitStoreConvert(graph, write.getWriteKind(), write.value(), write.isCompressible());
-        WriteNode memoryWrite = graph.add(new WriteNode(write.getAddress(), write.getLocationIdentity(), value, write.getBarrierType()));
+        WriteNode memoryWrite = graph.add(new WriteNode(write.getAddress(), write.getKilledLocationIdentity(), value, write.getBarrierType(), false));
         memoryWrite.setStateAfter(write.stateAfter());
         graph.replaceFixedWithFixed(write, memoryWrite);
         memoryWrite.setGuard(write.getGuard());
@@ -842,7 +849,7 @@
                                 barrierType = arrayInitializationBarrier(entryKind);
                             }
                             if (address != null) {
-                                WriteNode write = new WriteNode(address, LocationIdentity.init(), implicitStoreConvert(graph, entryKind, value), barrierType);
+                                WriteNode write = new WriteNode(address, LocationIdentity.init(), implicitStoreConvert(graph, entryKind, value), barrierType, false);
                                 graph.addAfterFixed(newObject, graph.add(write));
                             }
                         }
@@ -875,7 +882,7 @@
                                     barrierType = arrayStoreBarrierType(virtual.entryKind(i));
                                 }
                                 if (address != null) {
-                                    WriteNode write = new WriteNode(address, LocationIdentity.init(), implicitStoreConvert(graph, JavaKind.Object, allocValue), barrierType);
+                                    WriteNode write = new WriteNode(address, LocationIdentity.init(), implicitStoreConvert(graph, JavaKind.Object, allocValue), barrierType, false);
                                     graph.addBeforeFixed(commit, graph.add(write));
                                 }
                             }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/NodeIntrinsificationProvider.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/NodeIntrinsificationProvider.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -34,22 +34,27 @@
 import org.graalvm.compiler.replacements.arraycopy.ArrayCopyForeignCalls;
 import org.graalvm.compiler.word.WordTypes;
 
+import jdk.vm.ci.code.TargetDescription;
 import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.MetaAccessProvider;
 import jdk.vm.ci.meta.ResolvedJavaType;
 
 public class NodeIntrinsificationProvider implements InjectionProvider {
 
+    public static final TargetDescription INJECTED_TARGET = null;
+
     private final MetaAccessProvider metaAccess;
     private final SnippetReflectionProvider snippetReflection;
     private final ForeignCallsProvider foreignCalls;
     private final WordTypes wordTypes;
+    private final TargetDescription target;
 
-    public NodeIntrinsificationProvider(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, ForeignCallsProvider foreignCalls, WordTypes wordTypes) {
+    public NodeIntrinsificationProvider(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, ForeignCallsProvider foreignCalls, WordTypes wordTypes, TargetDescription target) {
         this.metaAccess = metaAccess;
         this.snippetReflection = snippetReflection;
         this.foreignCalls = foreignCalls;
         this.wordTypes = wordTypes;
+        this.target = target;
     }
 
     @Override
@@ -78,6 +83,8 @@
             return type.cast(snippetReflection);
         } else if (type.equals(WordTypes.class)) {
             return type.cast(wordTypes);
+        } else if (type.equals(TargetDescription.class)) {
+            return type.cast(target);
         } else {
             throw new GraalError("Cannot handle injected argument of type %s.", type.getName());
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/ReplacementsImpl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -269,11 +269,15 @@
     }
 
     @Override
-    public void registerMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context, OptionValues options) {
+    public void registerMethodSubstitution(MethodSubstitutionPlugin plugin) {
         // No initialization needed as method substitutions are parsed by the BytecodeParser.
     }
 
     @Override
+    public void registerConditionalPlugin(InvocationPlugin plugin) {
+    }
+
+    @Override
     public boolean hasSubstitution(ResolvedJavaMethod method, int invokeBci) {
         InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(method);
         return plugin != null && (!plugin.inlineOnly() || invokeBci >= 0);
@@ -560,7 +564,7 @@
 
                 createGraphBuilder(replacements.providers, config, OptimisticOptimizations.NONE, initialIntrinsicContext).apply(graph);
 
-                new CanonicalizerPhase().apply(graph, replacements.providers);
+                CanonicalizerPhase.create().apply(graph, replacements.providers);
             } catch (Throwable e) {
                 throw debug.handle(e);
             }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java	Mon Nov 04 11:25:55 2019 +0000
@@ -891,7 +891,7 @@
             }
             snippetCopy.setGuardsStage(guardsStage);
             try (DebugContext.Scope s = debug.scope("LoweringSnippetTemplate", snippetCopy)) {
-                new LoweringPhase(new CanonicalizerPhase(), args.cacheKey.loweringStage).apply(snippetCopy, providers);
+                new LoweringPhase(CanonicalizerPhase.create(), args.cacheKey.loweringStage).apply(snippetCopy, providers);
             } catch (Throwable e) {
                 throw debug.handle(e);
             }
@@ -930,7 +930,10 @@
             assert checkAllVarargPlaceholdersAreDeleted(parameterCount, placeholders);
 
             new FloatingReadPhase(true, true).apply(snippetCopy);
-            new RemoveValueProxyPhase().apply(snippetCopy);
+
+            if (!guardsStage.requiresValueProxies()) {
+                new RemoveValueProxyPhase().apply(snippetCopy);
+            }
 
             MemoryAnchorNode anchor = snippetCopy.add(new MemoryAnchorNode());
             snippetCopy.start().replaceAtUsages(InputType.Memory, anchor);
@@ -1059,8 +1062,14 @@
                 if (loopBegin != null) {
                     LoopEx loop = new LoopsData(snippetCopy).loop(loopBegin);
                     Mark mark = snippetCopy.getMark();
-                    LoopTransformations.fullUnroll(loop, providers, new CanonicalizerPhase());
-                    new CanonicalizerPhase().applyIncremental(snippetCopy, providers, mark, false);
+                    CanonicalizerPhase canonicalizer = null;
+                    if (GraalOptions.ImmutableCode.getValue(snippetCopy.getOptions())) {
+                        canonicalizer = CanonicalizerPhase.createWithoutReadCanonicalization();
+                    } else {
+                        canonicalizer = CanonicalizerPhase.create();
+                    }
+                    LoopTransformations.fullUnroll(loop, providers, canonicalizer);
+                    CanonicalizerPhase.create().applyIncremental(snippetCopy, providers, mark, false);
                     loop.deleteUnusedNodes();
                 }
                 GraphUtil.removeFixedWithUnusedInputs(explodeLoop);
@@ -1278,7 +1287,7 @@
 
         if (replacee instanceof MemoryCheckpoint.Single) {
             // check if some node in snippet graph also kills the same location
-            LocationIdentity locationIdentity = ((MemoryCheckpoint.Single) replacee).getLocationIdentity();
+            LocationIdentity locationIdentity = ((MemoryCheckpoint.Single) replacee).getKilledLocationIdentity();
             if (locationIdentity.isAny()) {
                 assert !(memoryMap.getLastLocationAccess(any()) instanceof MemoryAnchorNode) : replacee + " kills ANY_LOCATION, but snippet does not";
                 // if the replacee kills ANY_LOCATION, the snippet can kill arbitrary locations
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java	Mon Nov 04 11:25:55 2019 +0000
@@ -40,7 +40,6 @@
 
 import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
-import org.graalvm.compiler.bytecode.BytecodeProvider;
 import org.graalvm.compiler.core.common.calc.Condition;
 import org.graalvm.compiler.core.common.calc.Condition.CanonicalizedCondition;
 import org.graalvm.compiler.core.common.calc.UnsignedMath;
@@ -115,6 +114,7 @@
 import org.graalvm.compiler.nodes.java.UnsafeCompareAndSwapNode;
 import org.graalvm.compiler.nodes.memory.HeapAccess;
 import org.graalvm.compiler.nodes.memory.address.IndexAddressNode;
+import org.graalvm.compiler.nodes.spi.Replacements;
 import org.graalvm.compiler.nodes.type.StampTool;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.nodes.virtual.EnsureVirtualizedNode;
@@ -154,30 +154,30 @@
  */
 public class StandardGraphBuilderPlugins {
 
-    public static void registerInvocationPlugins(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, InvocationPlugins plugins, BytecodeProvider bytecodeProvider,
+    public static void registerInvocationPlugins(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, InvocationPlugins plugins, Replacements replacements,
                     boolean allowDeoptimization, boolean explicitUnsafeNullChecks) {
         registerObjectPlugins(plugins);
         registerClassPlugins(plugins);
         registerMathPlugins(plugins, allowDeoptimization);
         registerStrictMathPlugins(plugins);
         registerUnsignedMathPlugins(plugins);
-        registerStringPlugins(plugins, bytecodeProvider, snippetReflection);
+        registerStringPlugins(plugins, replacements, snippetReflection);
         registerCharacterPlugins(plugins);
         registerShortPlugins(plugins);
         registerIntegerLongPlugins(plugins, JavaKind.Int);
         registerIntegerLongPlugins(plugins, JavaKind.Long);
         registerFloatPlugins(plugins);
         registerDoublePlugins(plugins);
-        registerArraysPlugins(plugins, bytecodeProvider);
-        registerArrayPlugins(plugins, bytecodeProvider);
-        registerUnsafePlugins(plugins, bytecodeProvider, explicitUnsafeNullChecks);
+        registerArraysPlugins(plugins, replacements);
+        registerArrayPlugins(plugins, replacements);
+        registerUnsafePlugins(plugins, replacements, explicitUnsafeNullChecks);
         registerEdgesPlugins(metaAccess, plugins);
         registerGraalDirectivesPlugins(plugins);
         registerBoxingPlugins(plugins);
-        registerJMHBlackholePlugins(plugins, bytecodeProvider);
-        registerJFRThrowablePlugins(plugins, bytecodeProvider);
-        registerMethodHandleImplPlugins(plugins, snippetReflection, bytecodeProvider);
-        registerJcovCollectPlugins(plugins, bytecodeProvider);
+        registerJMHBlackholePlugins(plugins, replacements);
+        registerJFRThrowablePlugins(plugins, replacements);
+        registerMethodHandleImplPlugins(plugins, snippetReflection, replacements);
+        registerJcovCollectPlugins(plugins, replacements);
     }
 
     private static final Field STRING_VALUE_FIELD;
@@ -196,8 +196,8 @@
         STRING_CODER_FIELD = coder;
     }
 
-    private static void registerStringPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider, SnippetReflectionProvider snippetReflection) {
-        final Registration r = new Registration(plugins, String.class, bytecodeProvider);
+    private static void registerStringPlugins(InvocationPlugins plugins, Replacements replacements, SnippetReflectionProvider snippetReflection) {
+        final Registration r = new Registration(plugins, String.class, replacements);
         r.register1("hashCode", Receiver.class, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
@@ -243,7 +243,7 @@
             });
         } else {
             r.registerMethodSubstitution(JDK9StringSubstitutions.class, "equals", Receiver.class, Object.class);
-            Registration utf16sub = new Registration(plugins, StringUTF16Substitutions.class, bytecodeProvider);
+            Registration utf16sub = new Registration(plugins, StringUTF16Substitutions.class, replacements);
             utf16sub.register2("getCharDirect", byte[].class, int.class, new InvocationPlugin() {
                 @Override
                 public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2) {
@@ -261,10 +261,10 @@
                 }
             });
 
-            final Registration latin1r = new Registration(plugins, "java.lang.StringLatin1", bytecodeProvider);
+            final Registration latin1r = new Registration(plugins, "java.lang.StringLatin1", replacements);
             latin1r.register5("indexOf", byte[].class, int.class, byte[].class, int.class, int.class, new StringLatin1IndexOfConstantPlugin());
 
-            final Registration utf16r = new Registration(plugins, "java.lang.StringUTF16", bytecodeProvider);
+            final Registration utf16r = new Registration(plugins, "java.lang.StringUTF16", replacements);
             utf16r.register5("indexOfUnsafe", byte[].class, int.class, byte[].class, int.class, int.class, new StringUTF16IndexOfConstantPlugin());
             utf16r.setAllowOverwrite(true);
             utf16r.registerMethodSubstitution(StringUTF16Substitutions.class, "getChar", byte[].class, int.class);
@@ -292,8 +292,8 @@
         }
     }
 
-    private static void registerArraysPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, Arrays.class, bytecodeProvider);
+    private static void registerArraysPlugins(InvocationPlugins plugins, Replacements replacements) {
+        Registration r = new Registration(plugins, Arrays.class, replacements);
         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", boolean[].class, boolean[].class);
         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", byte[].class, byte[].class);
         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", short[].class, short[].class);
@@ -302,8 +302,8 @@
         r.registerMethodSubstitution(ArraysSubstitutions.class, "equals", long[].class, long[].class);
     }
 
-    private static void registerArrayPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, Array.class, bytecodeProvider);
+    private static void registerArrayPlugins(InvocationPlugins plugins, Replacements replacements) {
+        Registration r = new Registration(plugins, Array.class, replacements);
         r.register2("newInstance", Class.class, int.class, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode componentType, ValueNode length) {
@@ -381,10 +381,10 @@
 
     private static UnsafeCompareAndExchangePluginsRegistrar unsafeCompareAndExchangePluginsRegistrar = new UnsafeCompareAndExchangePluginsRegistrar();
 
-    public static void registerPlatformSpecificUnsafePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider, boolean explicitUnsafeNullChecks, JavaKind[] supportedCasKinds) {
+    public static void registerPlatformSpecificUnsafePlugins(InvocationPlugins plugins, Replacements replacements, boolean explicitUnsafeNullChecks, JavaKind[] supportedCasKinds) {
         registerPlatformSpecificUnsafePlugins(supportedCasKinds, new Registration(plugins, Unsafe.class), true, explicitUnsafeNullChecks);
         if (JavaVersionUtil.JAVA_SPEC > 8) {
-            registerPlatformSpecificUnsafePlugins(supportedCasKinds, new Registration(plugins, "jdk.internal.misc.Unsafe", bytecodeProvider), false, explicitUnsafeNullChecks);
+            registerPlatformSpecificUnsafePlugins(supportedCasKinds, new Registration(plugins, "jdk.internal.misc.Unsafe", replacements), false, explicitUnsafeNullChecks);
         }
 
     }
@@ -398,10 +398,10 @@
         }
     }
 
-    private static void registerUnsafePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider, boolean explicitUnsafeNullChecks) {
+    private static void registerUnsafePlugins(InvocationPlugins plugins, Replacements replacements, boolean explicitUnsafeNullChecks) {
         registerUnsafePlugins(new Registration(plugins, Unsafe.class), true, explicitUnsafeNullChecks);
         if (JavaVersionUtil.JAVA_SPEC > 8) {
-            registerUnsafePlugins(new Registration(plugins, "jdk.internal.misc.Unsafe", bytecodeProvider), false, explicitUnsafeNullChecks);
+            registerUnsafePlugins(new Registration(plugins, "jdk.internal.misc.Unsafe", replacements), false, explicitUnsafeNullChecks);
         }
     }
 
@@ -1300,7 +1300,7 @@
         });
     }
 
-    private static void registerJMHBlackholePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
+    private static void registerJMHBlackholePlugins(InvocationPlugins plugins, Replacements replacements) {
         InvocationPlugin blackholePlugin = new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver blackhole, ValueNode value) {
@@ -1316,7 +1316,7 @@
         };
         String[] names = {"org.openjdk.jmh.infra.Blackhole", "org.openjdk.jmh.logic.BlackHole"};
         for (String name : names) {
-            Registration r = new Registration(plugins, name, bytecodeProvider);
+            Registration r = new Registration(plugins, name, replacements);
             for (JavaKind kind : JavaKind.values()) {
                 if ((kind.isPrimitive() && kind != JavaKind.Void) || kind == JavaKind.Object) {
                     Class<?> javaClass = kind == JavaKind.Object ? Object.class : kind.toJavaClass();
@@ -1327,8 +1327,8 @@
         }
     }
 
-    private static void registerJFRThrowablePlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, "oracle.jrockit.jfr.jdkevents.ThrowableTracer", bytecodeProvider);
+    private static void registerJFRThrowablePlugins(InvocationPlugins plugins, Replacements replacements) {
+        Registration r = new Registration(plugins, "oracle.jrockit.jfr.jdkevents.ThrowableTracer", replacements);
         r.register2("traceThrowable", Throwable.class, String.class, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) {
@@ -1343,8 +1343,8 @@
         });
     }
 
-    private static void registerMethodHandleImplPlugins(InvocationPlugins plugins, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, "java.lang.invoke.MethodHandleImpl", bytecodeProvider);
+    private static void registerMethodHandleImplPlugins(InvocationPlugins plugins, SnippetReflectionProvider snippetReflection, Replacements replacements) {
+        Registration r = new Registration(plugins, "java.lang.invoke.MethodHandleImpl", replacements);
         // In later JDKs this no longer exists and the usage is replace by Class.cast which is
         // already an intrinsic
         r.registerOptional2("castReference", Class.class, Object.class, new InvocationPlugin() {
@@ -1408,8 +1408,8 @@
      * Registers a plugin to ignore {@code com.sun.tdk.jcov.runtime.Collect.hit} within an
      * intrinsic.
      */
-    private static void registerJcovCollectPlugins(InvocationPlugins plugins, BytecodeProvider bytecodeProvider) {
-        Registration r = new Registration(plugins, "com.sun.tdk.jcov.runtime.Collect", bytecodeProvider);
+    private static void registerJcovCollectPlugins(InvocationPlugins plugins, Replacements replacements) {
+        Registration r = new Registration(plugins, "com.sun.tdk.jcov.runtime.Collect", replacements);
         r.register1("hit", int.class, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode object) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/TargetGraphBuilderPlugins.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/TargetGraphBuilderPlugins.java	Mon Nov 04 11:25:55 2019 +0000
@@ -24,12 +24,12 @@
 
 package org.graalvm.compiler.replacements;
 
-import org.graalvm.compiler.bytecode.BytecodeProvider;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
+import org.graalvm.compiler.nodes.spi.Replacements;
 
 import jdk.vm.ci.code.Architecture;
 
 public interface TargetGraphBuilderPlugins {
-    void register(Plugins plugins, BytecodeProvider replacementsBytecodeProvider, Architecture arch, boolean explicitUnsafeNullChecks, boolean registerMathPlugins, boolean emitJDK9StringSubstitutions,
+    void register(Plugins plugins, Replacements replacements, Architecture arch, boolean explicitUnsafeNullChecks, boolean registerMathPlugins, boolean emitJDK9StringSubstitutions,
                     boolean useFMAIntrinsics);
 }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/arraycopy/ArrayCopyCallNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/arraycopy/ArrayCopyCallNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -188,6 +188,11 @@
         return locationIdentity;
     }
 
+    @Override
+    public LocationIdentity getKilledLocationIdentity() {
+        return getLocationIdentity();
+    }
+
     @NodeIntrinsic(hasSideEffect = true)
     private static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length, @ConstantNodeParameter JavaKind elementKind, @ConstantNodeParameter boolean aligned,
                     @ConstantNodeParameter boolean disjoint, @ConstantNodeParameter boolean uninitialized, @ConstantNodeParameter int heapWordSize);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/arraycopy/ArrayCopyNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/arraycopy/ArrayCopyNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -57,7 +57,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         if (!forceAnyLocation && elementKind == null) {
             elementKind = ArrayCopySnippets.Templates.selectComponentKind(this);
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/arraycopy/CheckcastArrayCopyCallNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/arraycopy/CheckcastArrayCopyCallNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -146,7 +146,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         /*
          * Because of restrictions that the memory graph of snippets matches the original node,
          * pretend that we kill any.
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/arraycopy/GenericArrayCopyCallNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/arraycopy/GenericArrayCopyCallNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -102,7 +102,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -128,6 +128,11 @@
     }
 
     @Override
+    public LocationIdentity getKilledLocationIdentity() {
+        return getLocationIdentity();
+    }
+
+    @Override
     public MemoryNode getLastLocationAccess() {
         return lastLocationAccess;
     }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MacroNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MacroNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -173,7 +173,7 @@
         }
         DebugContext debug = replacementGraph.getDebug();
         try (DebugContext.Scope s = debug.scope("LoweringSnippetTemplate", replacementGraph)) {
-            new LoweringPhase(new CanonicalizerPhase(), tool.getLoweringStage()).apply(replacementGraph, c);
+            new LoweringPhase(CanonicalizerPhase.create(), tool.getLoweringStage()).apply(replacementGraph, c);
         } catch (Throwable e) {
             throw debug.handle(e);
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MacroStateSplitNode.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MacroStateSplitNode.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -75,7 +75,7 @@
     }
 
     @Override
-    public LocationIdentity getLocationIdentity() {
+    public LocationIdentity getKilledLocationIdentity() {
         return LocationIdentity.any();
     }
 
@@ -87,7 +87,7 @@
             }
             assert invoke.stateAfter().bci == BytecodeFrame.AFTER_BCI;
             // Here we need to fix the bci of the invoke
-            InvokeNode newInvoke = snippetGraph.add(new InvokeNode(invoke.callTarget(), bci(), invoke.getLocationIdentity()));
+            InvokeNode newInvoke = snippetGraph.add(new InvokeNode(invoke.callTarget(), bci(), invoke.getKilledLocationIdentity()));
             newInvoke.setStateAfter(invoke.stateAfter());
             snippetGraph.replaceFixedWithFixed((InvokeNode) invoke.asNode(), newInvoke);
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/ExportingClassLoader.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2016, 2019, 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.test;
-
-/**
- * A class loader that exports all packages in the module defining the class loader to all classes
- * in the unnamed module associated with the loader.
- */
-public class ExportingClassLoader extends ClassLoader {
-    public ExportingClassLoader() {
-        ModuleSupport.exportAllPackagesTo(getClass(), this);
-    }
-
-    public ExportingClassLoader(ClassLoader parent) {
-        super(parent);
-        ModuleSupport.exportAllPackagesTo(getClass(), this);
-    }
-}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.test/src/org/graalvm/compiler/test/ModuleSupport.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/*
- * Copyright (c) 2019, 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.test;
-
-import java.io.IOException;
-import java.lang.module.ModuleDescriptor.Requires;
-import java.net.URI;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.graalvm.compiler.debug.DebugOptions;
-
-import jdk.internal.module.Modules;
-
-public class ModuleSupport {
-
-    public static void exportPackageTo(Class<?> moduleMember, String packageName, Class<?> requestor) {
-        Module moduleToExport = moduleMember.getModule();
-        Module requestorModule = requestor.getModule();
-        if (moduleToExport != requestorModule) {
-            Modules.addExports(moduleToExport, packageName, requestorModule);
-        }
-    }
-
-    public static void exportAllPackagesTo(Class<?> moduleMember, Class<?> requestor) {
-        Module moduleToExport = moduleMember.getModule();
-        Module requestorModule = requestor.getModule();
-        if (moduleToExport != requestorModule) {
-            for (String packageName : moduleToExport.getPackages()) {
-                Modules.addExports(moduleToExport, packageName, requestorModule);
-            }
-        }
-    }
-
-    public static void exportAllPackagesTo(Class<?> moduleMember, ClassLoader cl) {
-        Module moduleToExport = moduleMember.getModule();
-        Module unnamedModule = cl.getUnnamedModule();
-        for (String packageName : moduleToExport.getPackages()) {
-            Modules.addExports(moduleToExport, packageName, unnamedModule);
-        }
-    }
-
-    @SuppressWarnings("unused")
-    public static void exportAndOpenAllPackagesToUnnamed(String name) {
-        Module module = ModuleLayer.boot().findModule(name).orElseThrow();
-        Set<String> packages = module.getPackages();
-        for (String pkg : packages) {
-            Modules.addExportsToAllUnnamed(module, pkg);
-            Modules.addOpensToAllUnnamed(module, pkg);
-        }
-    }
-
-    public static List<String> getJRTGraalClassNames() throws IOException {
-        List<String> classNames = new ArrayList<>();
-        FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap());
-        Module graalModule = DebugOptions.class.getModule();
-        Set<String> graalModuleSet = new HashSet<>();
-        graalModuleSet.add(graalModule.getName());
-        for (Module module : graalModule.getLayer().modules()) {
-            if (requires(module, graalModule)) {
-                graalModuleSet.add(module.getName());
-            }
-        }
-
-        Path top = fs.getPath("/modules/");
-        Files.find(top, Integer.MAX_VALUE,
-                        (path, attrs) -> attrs.isRegularFile()).forEach(p -> {
-                            int nameCount = p.getNameCount();
-                            if (nameCount > 2) {
-                                String base = p.getName(nameCount - 1).toString();
-                                if (base.endsWith(".class") && !base.equals("module-info.class")) {
-                                    String module = p.getName(1).toString();
-                                    if (graalModuleSet.contains(module)) {
-                                        // Strip module prefix and convert to dotted
-                                        // form
-                                        String className = p.subpath(2, nameCount).toString().replace('/', '.');
-                                        // Strip ".class" suffix
-                                        className = className.replace('/', '.').substring(0, className.length() - ".class".length());
-                                        classNames.add(className);
-                                    }
-                                }
-                            }
-                        });
-        return classNames;
-    }
-
-    private static boolean requires(Module module, Module graalModule) {
-        ModuleLayer graalLayer = graalModule.getLayer();
-        for (Requires r : module.getDescriptor().requires()) {
-            if (r.name().equals(graalModule.getName())) {
-                return true;
-            }
-            Module dep = graalLayer.findModule(r.name()).get();
-            if (requires(dep, graalModule)) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java	Mon Nov 04 11:25:55 2019 +0000
@@ -121,11 +121,11 @@
             return processUnsafeStore((RawStoreNode) node, state, effects);
         } else if (node instanceof MemoryCheckpoint.Single) {
             COUNTER_MEMORYCHECKPOINT.increment(node.getDebug());
-            LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity();
+            LocationIdentity identity = ((MemoryCheckpoint.Single) node).getKilledLocationIdentity();
             processIdentity(state, identity);
         } else if (node instanceof MemoryCheckpoint.Multi) {
             COUNTER_MEMORYCHECKPOINT.increment(node.getDebug());
-            for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) {
+            for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getKilledLocationIdentities()) {
                 processIdentity(state, identity);
             }
         }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationBlockState.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationBlockState.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -152,6 +152,7 @@
     }
 
     public ReadEliminationBlockState(ReadEliminationBlockState other) {
+        super(other);
         readCache = EconomicMap.create(Equivalence.DEFAULT, other.readCache);
     }
 
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, 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
@@ -135,9 +135,9 @@
             }
         } else if (node instanceof WriteNode) {
             WriteNode write = (WriteNode) node;
-            if (write.getLocationIdentity().isSingle()) {
+            if (write.getKilledLocationIdentity().isSingle()) {
                 ValueNode object = GraphUtil.unproxify(write.getAddress());
-                LoadCacheEntry identifier = new LoadCacheEntry(object, write.getLocationIdentity());
+                LoadCacheEntry identifier = new LoadCacheEntry(object, write.getKilledLocationIdentity());
                 ValueNode cachedValue = state.getCacheEntry(identifier);
 
                 ValueNode value = getScalarAlias(write.value());
@@ -145,10 +145,10 @@
                     effects.deleteNode(write);
                     deleted = true;
                 }
-                processIdentity(state, write.getLocationIdentity());
+                processIdentity(state, write.getKilledLocationIdentity());
                 state.addCacheEntry(identifier, value);
             } else {
-                processIdentity(state, write.getLocationIdentity());
+                processIdentity(state, write.getKilledLocationIdentity());
             }
         } else if (node instanceof UnsafeAccessNode) {
             ResolvedJavaType type = StampTool.typeOrNull(((UnsafeAccessNode) node).object());
@@ -170,9 +170,9 @@
                 } else {
                     assert node instanceof RawStoreNode;
                     RawStoreNode write = (RawStoreNode) node;
-                    if (write.getLocationIdentity().isSingle()) {
+                    if (write.getKilledLocationIdentity().isSingle()) {
                         ValueNode object = GraphUtil.unproxify(write.object());
-                        UnsafeLoadCacheEntry identifier = new UnsafeLoadCacheEntry(object, write.offset(), write.getLocationIdentity());
+                        UnsafeLoadCacheEntry identifier = new UnsafeLoadCacheEntry(object, write.offset(), write.getKilledLocationIdentity());
                         ValueNode cachedValue = state.getCacheEntry(identifier);
 
                         ValueNode value = getScalarAlias(write.value());
@@ -180,18 +180,18 @@
                             effects.deleteNode(write);
                             deleted = true;
                         }
-                        processIdentity(state, write.getLocationIdentity());
+                        processIdentity(state, write.getKilledLocationIdentity());
                         state.addCacheEntry(identifier, value);
                     } else {
-                        processIdentity(state, write.getLocationIdentity());
+                        processIdentity(state, write.getKilledLocationIdentity());
                     }
                 }
             }
         } else if (node instanceof MemoryCheckpoint.Single) {
-            LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity();
+            LocationIdentity identity = ((MemoryCheckpoint.Single) node).getKilledLocationIdentity();
             processIdentity(state, identity);
         } else if (node instanceof MemoryCheckpoint.Multi) {
-            for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) {
+            for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getKilledLocationIdentities()) {
                 processIdentity(state, identity);
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/OptionsEncoder.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018, 2019, 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.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Facilities for encoding/decoding a set of options to/from a byte array.
+ */
+public final class OptionsEncoder {
+
+    private OptionsEncoder() {
+    }
+
+    /**
+     * Determines if {@code value} is supported by {@link #encode(Map)}.
+     */
+    public static boolean isValueSupported(Object value) {
+        return TypedDataOutputStream.isValueSupported(value);
+    }
+
+    /**
+     * Encodes {@code options} into a byte array.
+     *
+     * @throws IllegalArgumentException if any value in {@code options} is not
+     *             {@linkplain #isValueSupported(Object) supported}
+     */
+    public static byte[] encode(final Map<String, Object> options) {
+        try (ByteArrayOutputStream baout = new ByteArrayOutputStream()) {
+            try (TypedDataOutputStream out = new TypedDataOutputStream(baout)) {
+                out.writeInt(options.size());
+                for (Map.Entry<String, Object> e : options.entrySet()) {
+                    out.writeUTF(e.getKey());
+                    try {
+                        out.writeTypedValue(e.getValue());
+                    } catch (IllegalArgumentException iae) {
+                        throw new IllegalArgumentException(String.format("Key: %s, Value: %s, Value type: %s",
+                                        e.getKey(), e.getValue(), e.getValue().getClass()), iae);
+                    }
+                }
+            }
+            return baout.toByteArray();
+        } catch (IOException ioe) {
+            throw new IllegalArgumentException(ioe);
+        }
+    }
+
+    /**
+     * Decodes {@code input} into a name/value map.
+     *
+     * @throws IllegalArgumentException if {@code input} cannot be decoded
+     */
+    public static Map<String, Object> decode(byte[] input) {
+        Map<String, Object> res = new LinkedHashMap<>();
+        try (TypedDataInputStream in = new TypedDataInputStream(new ByteArrayInputStream(input))) {
+            final int size = in.readInt();
+            for (int i = 0; i < size; i++) {
+                final String key = in.readUTF();
+                final Object value = in.readTypedValue();
+                res.put(key, value);
+            }
+            if (in.available() != 0) {
+                throw new IllegalArgumentException(in.available() + " undecoded bytes");
+            }
+        } catch (IOException ioe) {
+            throw new IllegalArgumentException(ioe);
+        }
+        return res;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/TypedDataInputStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019, 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.util;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A stream that can read (trivial) values using their in-band data type information, intended for
+ * use with {@link TypedDataOutputStream}.
+ */
+public class TypedDataInputStream extends DataInputStream {
+    public TypedDataInputStream(InputStream in) {
+        super(in);
+    }
+
+    /**
+     * Reads a single value, using the data type encoded in the stream.
+     *
+     * @return The read value, such as a boxed primitive or a {@link String}.
+     * @exception IOException in case of an I/O error.
+     */
+    public Object readTypedValue() throws IOException {
+        Object value;
+        final byte type = readByte();
+        switch (type) {
+            case 'Z':
+                value = readBoolean();
+                break;
+            case 'B':
+                value = readByte();
+                break;
+            case 'S':
+                value = readShort();
+                break;
+            case 'C':
+                value = readChar();
+                break;
+            case 'I':
+                value = readInt();
+                break;
+            case 'J':
+                value = readLong();
+                break;
+            case 'F':
+                value = readFloat();
+                break;
+            case 'D':
+                value = readDouble();
+                break;
+            case 'U':
+                value = readUTF();
+                break;
+            default:
+                throw new IOException("Unsupported type: " + Integer.toHexString(type));
+        }
+        return value;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/TypedDataOutputStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019, 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.util;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A stream that can write (trivial) values together with their data type, for use with
+ * {@link TypedDataInputStream}.
+ */
+public class TypedDataOutputStream extends DataOutputStream {
+    /** Determines if {@code value} is supported by {@link #writeTypedValue(Object)}. */
+    public static boolean isValueSupported(Object value) {
+        if (value == null) {
+            return false;
+        }
+        Class<?> valueClass = value.getClass();
+        return valueClass == Boolean.class ||
+                        valueClass == Byte.class ||
+                        valueClass == Short.class ||
+                        valueClass == Character.class ||
+                        valueClass == Integer.class ||
+                        valueClass == Long.class ||
+                        valueClass == Float.class ||
+                        valueClass == Double.class ||
+                        valueClass == String.class ||
+                        value.getClass().isEnum();
+    }
+
+    public TypedDataOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    /**
+     * Writes the value that is represented by the given non-null object, together with information
+     * on the value's data type.
+     *
+     * @param value A value of a {@linkplain #isValueSupported supported type}.
+     * @exception IllegalArgumentException when the provided type is not supported.
+     * @exception IOException in case of an I/O error.
+     */
+    public void writeTypedValue(Object value) throws IOException {
+        Class<?> valueClz = value.getClass();
+        if (valueClz == Boolean.class) {
+            this.writeByte('Z');
+            this.writeBoolean((Boolean) value);
+        } else if (valueClz == Byte.class) {
+            this.writeByte('B');
+            this.writeByte((Byte) value);
+        } else if (valueClz == Short.class) {
+            this.writeByte('S');
+            this.writeShort((Short) value);
+        } else if (valueClz == Character.class) {
+            this.writeByte('C');
+            this.writeChar((Character) value);
+        } else if (valueClz == Integer.class) {
+            this.writeByte('I');
+            this.writeInt((Integer) value);
+        } else if (valueClz == Long.class) {
+            this.writeByte('J');
+            this.writeLong((Long) value);
+        } else if (valueClz == Float.class) {
+            this.writeByte('F');
+            this.writeFloat((Float) value);
+        } else if (valueClz == Double.class) {
+            this.writeByte('D');
+            this.writeDouble((Double) value);
+        } else if (valueClz == String.class) {
+            this.writeByte('U');
+            this.writeUTF((String) value);
+        } else if (valueClz.isEnum()) {
+            this.writeByte('U');
+            this.writeUTF(((Enum<?>) value).name());
+        } else {
+            throw new IllegalArgumentException(String.format("Unsupported type: Value: %s, Value type: %s", value, valueClz));
+        }
+    }
+}
--- a/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c	Mon Nov 04 11:25:55 2019 +0000
@@ -35,9 +35,11 @@
 #ifdef _WIN32
  #include <winsock2.h>
  #include <ws2tcpip.h>
+ #include <iphlpapi.h>
 #else
  #include <arpa/inet.h>
  #include <sys/socket.h>
+ #include <net/if.h>
 #endif
 
 /*
@@ -267,16 +269,101 @@
 }
 
 /*
+ * Parses scope id.
+ * Scope id is ulong on Windows, uint32 on unix, so returns long which can be cast to uint32.
+ * On error sets last error and returns -1.
+ */
+static long parseScopeId(const char *str) {
+    // try to handle scope as interface name
+    unsigned long scopeId = if_nametoindex(str);
+    if (scopeId == 0) {
+        // try to parse integer value
+        char *end;
+        scopeId = strtoul(str, &end, 10);
+        if (*end != '\0') {
+            setLastError(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "failed to parse scope");
+            return -1;
+        }
+    }
+    // ensure parsed value is in uint32 range
+    if (scopeId > 0xFFFFFFFF) {
+        setLastError(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "scope is out of range");
+        return -1;
+    }
+    return (long)scopeId;
+}
+
+/*
+ * Wrapper for dbgsysGetAddrInfo (getaddrinfo).
+ * Handles enclosing square brackets and scopes.
+ */
+static jdwpTransportError
+getAddrInfo(const char *hostname, size_t hostnameLen,
+            const char *service,
+            const struct addrinfo *hints,
+            struct addrinfo **result)
+{
+    int err = 0;
+    char *buffer = NULL;
+    long scopeId = 0;
+
+    if (hostname != NULL) {
+        char *scope = NULL;
+        // skip surrounding
+        if (hostnameLen > 2 && hostname[0] == '[' && hostname[hostnameLen - 1] == ']') {
+            hostname++;
+            hostnameLen -= 2;
+        }
+        buffer = (*callback->alloc)((int)hostnameLen + 1);
+        if (buffer == NULL) {
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
+        }
+        memcpy(buffer, hostname, hostnameLen);
+        buffer[hostnameLen] = '\0';
+
+        scope = strchr(buffer, '%');
+        if (scope != NULL) {
+            // drop scope from the address
+            *scope = '\0';
+            // and parse the value
+            scopeId = parseScopeId(scope + 1);
+            if (scopeId < 0) {
+                (*callback->free)(buffer);
+                return JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT;
+            }
+        }
+    }
+
+    err = dbgsysGetAddrInfo(buffer, service, hints, result);
+
+    if (buffer != NULL) {
+        (*callback->free)(buffer);
+    }
+    if (err != 0) {
+        setLastError(err, "getaddrinfo: failed to parse address");
+        return JDWPTRANSPORT_ERROR_IO_ERROR;
+    }
+
+    if (scopeId > 0) {
+        if ((*result)->ai_family != AF_INET6) {
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "IPv4 address cannot contain scope");
+        }
+
+        ((struct sockaddr_in6 *)((*result)->ai_addr))->sin6_scope_id = (uint32_t)scopeId;
+    }
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+/*
  * Result must be released with dbgsysFreeAddrInfo.
  */
 static jdwpTransportError
 parseAddress(const char *address, struct addrinfo **result) {
     const char *colon;
-    size_t hostLen;
-    char *host = NULL;
+    size_t hostnameLen;
     const char *port;
     struct addrinfo hints;
-    int res;
 
     *result = NULL;
 
@@ -295,39 +382,21 @@
     hints.ai_protocol = IPPROTO_TCP;
     hints.ai_flags = AI_NUMERICSERV;    // port must be a number
 
-    hostLen = (colon == NULL ? 0 : colon - address);
-    if (hostLen == 0) {
+    hostnameLen = (colon == NULL ? 0 : colon - address);
+    if (hostnameLen == 0) {
         /* no hostname - use localhost address (pass NULL to getaddrinfo) */
-    } else  if (*address == '*' && hostLen == 1) {
+        address = NULL;
+    } else  if (*address == '*' && hostnameLen == 1) {
         /* *:port - listen on all interfaces
          * use IPv6 socket (to accept IPv6 and mapped IPv4),
          * pass hostname == NULL to getaddrinfo.
          */
         hints.ai_family = allowOnlyIPv4 ? AF_INET : AF_INET6;
         hints.ai_flags |= AI_PASSIVE | (allowOnlyIPv4 ? 0 : AI_V4MAPPED | AI_ALL);
-    } else {
-        if (address[0] == '[' && colon[-1] == ']') {
-            address++;
-            hostLen -= 2;
-        }
-        host = (*callback->alloc)((int)hostLen + 1);
-        if (host == NULL) {
-            RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
-        }
-        strncpy(host, address, hostLen);
-        host[hostLen] = '\0';
+        address = NULL;
     }
 
-    res = dbgsysGetAddrInfo(host, port, &hints, result);
-    if (host != NULL) {
-        (*callback->free)(host);
-    }
-    if (res != 0) {
-        setLastError(res, "getaddrinfo: unknown host");
-        return JDWPTRANSPORT_ERROR_IO_ERROR;
-    }
-
-    return JDWPTRANSPORT_ERROR_NONE;
+    return getAddrInfo(address, hostnameLen, port, &hints, result);
 }
 
 /*
@@ -352,7 +421,7 @@
 parseAllowedAddr(const char *buffer, struct in6_addr *result, int *isIPv4) {
     struct addrinfo hints;
     struct addrinfo *addrInfo = NULL;
-    int err;
+    jdwpTransportError err;
 
     /*
      * To parse both IPv4 and IPv6 need to specify AF_UNSPEC family
@@ -364,11 +433,10 @@
     hints.ai_protocol = IPPROTO_TCP;
     hints.ai_flags = AI_NUMERICHOST;        // only numeric addresses, no resolution
 
-    err = dbgsysGetAddrInfo(buffer, NULL, &hints, &addrInfo);
+    err = getAddrInfo(buffer, strlen(buffer), NULL, &hints, &addrInfo);
 
-    if (err != 0) {
-        setLastError(err, "getaddrinfo: failed to parse address");
-        return JDWPTRANSPORT_ERROR_IO_ERROR;
+    if (err != JDWPTRANSPORT_ERROR_NONE) {
+        return err;
     }
 
     if (addrInfo->ai_family == AF_INET6) {
@@ -844,6 +912,7 @@
     }
 
     err = dbgsysConnect(socketFD, ai->ai_addr, (socklen_t)ai->ai_addrlen);
+
     if (err == DBG_EINPROGRESS && timeout > 0) {
         err = dbgsysFinishConnect(socketFD, (long)timeout);
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/Recording.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/Recording.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -413,6 +413,36 @@
         internal.setMaxSize(maxSize);
     }
 
+        /**
+         * Determines how often events are made available for streaming.
+         *
+         * @param interval the interval at which events are made available for streaming.
+         *
+         * @throws IllegalArgumentException if {@code interval} is negative
+         *
+         * @throws IllegalStateException if the recording is in the {@code CLOSED} state
+         *
+         * @since 14
+         */
+        public void setFlushInterval(Duration interval) {
+            Objects.nonNull(interval);
+            if (interval.isNegative()) {
+                throw new IllegalArgumentException("Stream interval can't be negative");
+            }
+            internal.setFlushInterval(interval);
+        }
+
+    /**
+     * Returns how often events are made available for streaming purposes.
+     *
+     * @return the flush interval, or {@code null} if no interval has been set
+     *
+     * @since 14
+     */
+    public Duration getFlushInterval() {
+        return internal.getFlushInterval();
+    }
+
     /**
      * Determines how far back data is kept in the disk repository.
      * <p>
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ChunkParser.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.consumer;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-
-import jdk.jfr.EventType;
-import jdk.jfr.internal.LogLevel;
-import jdk.jfr.internal.LogTag;
-import jdk.jfr.internal.Logger;
-import jdk.jfr.internal.MetadataDescriptor;
-import jdk.jfr.internal.Type;
-import jdk.jfr.internal.consumer.ChunkHeader;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Parses a chunk.
- *
- */
-final class ChunkParser {
-    private static final long CONSTANT_POOL_TYPE_ID = 1;
-    private final RecordingInput input;
-    private final LongMap<Parser> parsers;
-    private final ChunkHeader chunkHeader;
-    private final long absoluteChunkEnd;
-    private final MetadataDescriptor metadata;
-    private final LongMap<Type> typeMap;
-    private final TimeConverter timeConverter;
-
-    public ChunkParser(RecordingInput input) throws IOException {
-        this(new ChunkHeader(input));
-    }
-
-    private ChunkParser(ChunkHeader header) throws IOException {
-        this.input = header.getInput();
-        this.chunkHeader = header;
-        this.metadata = header.readMetadata();
-        this.absoluteChunkEnd = header.getEnd();
-        this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
-
-        ParserFactory factory = new ParserFactory(metadata, timeConverter);
-        LongMap<ConstantMap> constantPools = factory.getConstantPools();
-        parsers = factory.getParsers();
-        typeMap = factory.getTypeMap();
-
-        fillConstantPools(parsers, constantPools);
-        constantPools.forEach(ConstantMap::setIsResolving);
-        constantPools.forEach(ConstantMap::resolve);
-        constantPools.forEach(ConstantMap::setResolved);
-
-        input.position(chunkHeader.getEventStart());
-    }
-
-    public RecordedEvent readEvent() throws IOException {
-        while (input.position() < absoluteChunkEnd) {
-            long pos = input.position();
-            int size = input.readInt();
-            if (size == 0) {
-                throw new IOException("Event can't have zero size");
-            }
-            long typeId = input.readLong();
-            if (typeId > CONSTANT_POOL_TYPE_ID) { // also skips metadata (id=0)
-                Parser ep = parsers.get(typeId);
-                if (ep instanceof EventParser) {
-                    return (RecordedEvent) ep.parse(input);
-                }
-            }
-            input.position(pos + size);
-        }
-        return null;
-    }
-
-    private void fillConstantPools(LongMap<Parser> typeParser, LongMap<ConstantMap> constantPools) throws IOException {
-        long nextCP = chunkHeader.getAbsoluteChunkStart();
-        long deltaToNext = chunkHeader.getConstantPoolPosition();
-        while (deltaToNext != 0) {
-            nextCP += deltaToNext;
-            input.position(nextCP);
-            final long position = nextCP;
-            int size = input.readInt(); // size
-            long typeId = input.readLong();
-            if (typeId != CONSTANT_POOL_TYPE_ID) {
-                throw new IOException("Expected check point event (id = 1) at position " + nextCP + ", but found type id = " + typeId);
-            }
-            input.readLong(); // timestamp
-            input.readLong(); // duration
-            deltaToNext = input.readLong();
-            final long delta = deltaToNext;
-            boolean flush = input.readBoolean();
-            int poolCount = input.readInt();
-            Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
-                return "New constant pool: startPosition=" + position + ", size=" + size + ", deltaToNext=" + delta + ", flush=" + flush + ", poolCount=" + poolCount;
-            });
-
-            for (int i = 0; i < poolCount; i++) {
-                long id = input.readLong(); // type id
-                ConstantMap pool = constantPools.get(id);
-                Type type = typeMap.get(id);
-                if (pool == null) {
-                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used");
-                    if (type == null) {
-                        throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]");
-                    }
-                    pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
-                    constantPools.put(type.getId(), pool);
-                }
-                Parser parser = typeParser.get(id);
-                if (parser == null) {
-                    throw new IOException("Could not find constant pool type with id = " + id);
-                }
-                try {
-                    int count = input.readInt();
-                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> "Constant: " + getName(id) + "[" + count + "]");
-                    for (int j = 0; j < count; j++) {
-                        long key = input.readLong();
-                        Object value = parser.parse(input);
-                        pool.put(key, value);
-                    }
-                } catch (Exception e) {
-                    throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]", e);
-                }
-            }
-            if (input.position() != nextCP + size) {
-                throw new IOException("Size of check point event doesn't match content");
-            }
-        }
-    }
-
-    private String getName(long id) {
-        Type type = typeMap.get(id);
-        return type == null ? ("unknown(" + id + ")") : type.getName();
-    }
-
-    public Collection<Type> getTypes() {
-        return metadata.getTypes();
-    }
-
-    public List<EventType> getEventTypes() {
-        return metadata.getEventTypes();
-    }
-
-    public boolean isLastChunk() {
-        return chunkHeader.isLastChunk();
-    }
-
-    public ChunkParser nextChunkParser() throws IOException {
-        return new ChunkParser(chunkHeader.nextHeader());
-    }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ConstantMap.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.consumer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Holds mapping between a set of keys and their corresponding object.
- *
- * If the type is a known type, i.e. {@link RecordedThread}, an
- * {@link ObjectFactory} can be supplied which will instantiate a typed object.
- */
-final class ConstantMap {
-    private final static class Reference {
-        private final long key;
-        private final ConstantMap pool;
-
-        Reference(ConstantMap pool, long key) {
-            this.pool = pool;
-            this.key = key;
-        }
-
-        Object resolve() {
-            return pool.get(key);
-        }
-    }
-
-    private final ObjectFactory<?> factory;
-    private final LongMap<Object> objects;
-
-    private LongMap<Boolean> isResolving;
-    private boolean allResolved;
-    private String name;
-
-    ConstantMap(ObjectFactory<?> factory, String name) {
-        this.name = name;
-        this.objects = new LongMap<>();
-        this.factory = factory;
-    }
-
-    Object get(long id) {
-        // fast path, all objects in pool resolved
-        if (allResolved) {
-            return objects.get(id);
-        }
-        // referenced from a pool, deal with this later
-        if (isResolving == null) {
-            return new Reference(this, id);
-        }
-
-        Boolean beingResolved = isResolving.get(id);
-
-        // we are resolved (but not the whole pool)
-        if (Boolean.FALSE.equals(beingResolved)) {
-            return objects.get(id);
-        }
-
-        // resolving ourself, abort to avoid infinite recursion
-        if (Boolean.TRUE.equals(beingResolved)) {
-            return null;
-        }
-
-        // resolve me!
-        isResolving.put(id, Boolean.TRUE);
-        Object resolved = resolve(objects.get(id));
-        isResolving.put(id, Boolean.FALSE);
-        if (factory != null) {
-            Object factorized = factory.createObject(id, resolved);
-            objects.put(id, factorized);
-            return factorized;
-        } else {
-            objects.put(id, resolved);
-            return resolved;
-        }
-    }
-
-    private static Object resolve(Object o) {
-        if (o instanceof Reference) {
-            return resolve(((Reference) o).resolve());
-        }
-        if (o != null && o.getClass().isArray()) {
-            final Object[] array = (Object[]) o;
-            for (int i = 0; i < array.length; i++) {
-                array[i] = resolve(array[i]);
-            }
-            return array;
-        }
-        return o;
-    }
-
-    public void resolve() {
-        List<Long> keyList = new ArrayList<>();
-        objects.keys().forEachRemaining(keyList::add);
-        for (Long l : keyList) {
-            get(l);
-        }
-    }
-
-    public void put(long key, Object value) {
-        objects.put(key, value);
-    }
-
-    public void setIsResolving() {
-        isResolving = new LongMap<>();
-    }
-
-    public void setResolved() {
-        allResolved = true;
-        isResolving = null; // pool finished, release memory
-    }
-
-    public String getName() {
-        return name;
-    }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventParser.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.consumer;
-
-import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION;
-
-import java.io.IOException;
-import java.util.List;
-
-import jdk.jfr.EventType;
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Parses an event and returns a {@link RecordedEvent}.
- *
- */
-final class EventParser extends Parser {
-    private final Parser[] parsers;
-    private final EventType eventType;
-    private final TimeConverter timeConverter;
-    private final boolean hasDuration;
-    private final List<ValueDescriptor> valueDescriptors;
-
-    EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
-        this.timeConverter = timeConverter;
-        this.parsers = parsers;
-        this.eventType = type;
-        this.hasDuration = type.getField(FIELD_DURATION) != null;
-        this.valueDescriptors = type.getFields();
-    }
-
-    @Override
-    public Object parse(RecordingInput input) throws IOException {
-        Object[] values = new Object[parsers.length];
-        for (int i = 0; i < parsers.length; i++) {
-            values[i] = parsers[i].parse(input);
-        }
-        Long startTicks = (Long) values[0];
-        long startTime = timeConverter.convertTimestamp(startTicks);
-        if (hasDuration) {
-            long durationTicks = (Long) values[1];
-            long endTime = timeConverter.convertTimestamp(startTicks + durationTicks);
-            return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter);
-        } else {
-            return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter);
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+import jdk.jfr.internal.SecuritySupport;
+import jdk.jfr.internal.Utils;
+import jdk.jfr.internal.consumer.EventDirectoryStream;
+import jdk.jfr.internal.consumer.EventFileStream;
+import jdk.jfr.internal.consumer.FileAccess;
+
+/**
+ * Represents a stream of events.
+ * <p>
+ * A stream is a sequence of events and the way to interact with a stream is to
+ * register actions. The {@code EventStream} interface is not to be implemented
+ * and future versions of the JDK may prevent this completely.
+ * <p>
+ * To receive a notification when an event arrives, register an action using the
+ * {@link #onEvent(Consumer)} method. To filter the stream for an event with a
+ * specific name, use {@link #onEvent(String, Consumer)} method.
+ * <p>
+ * By default, the same {@code RecordedEvent} object can be used to
+ * represent two or more distinct events. That object can be delivered
+ * multiple times to the same action as well as to other actions. To use an
+ * event object after the action is completed, the
+ * {@link #setReuse(boolean)} method should be set to {@code false} so a
+ * new object is allocated for each event.
+ * <p>
+ * Events are delivered in batches. To receive a notification when a batch is
+ * complete, register an action using the {@link #onFlush(Runnable)} method.
+ * This is an opportunity to aggregate or push data to external systems while
+ * the Java Virtual Machine (JVM) is preparing the next batch.
+ * <p>
+ * Events within a batch are sorted chronologically by their end time.
+ * Well-ordering of events is only maintained for events available to the JVM at
+ * the point of flush, i.e. for the set of events delivered as a unit in a
+ * single batch. Events delivered in a batch could therefore be out-of-order
+ * compared to events delivered in a previous batch, but never out-of-order with
+ * events within the same batch. If ordering is not a concern, sorting can be
+ * disabled using the {@link #setOrdered(boolean)} method.
+ * <p>
+ * To dispatch events to registered actions, the stream must be started. To
+ * start processing in the current thread, invoke the {@link #start()} method.
+ * To process actions asynchronously in a separate thread, invoke the
+ * {@link #startAsync()} method. To await completion of the stream, use the
+ * awaitTermination {@link #awaitTermination()} or the
+ * {@link #awaitTermination(Duration)} method.
+ * <p>
+ * When a stream ends it is automatically closed. To manually stop processing of
+ * events, close the stream by invoking the {@link #close()} method. A stream
+ * can also be automatically closed in exceptional circumstances, for example if
+ * the JVM that is being monitored exits. To receive a notification in any of
+ * these occasions, use the {@link #onClose(Runnable)} method to register an
+ * action.
+ * <p>
+ * If an unexpected exception occurs in an action, it is possible to catch the
+ * exception in an error handler. An error handler can be registered using the
+ * {@link #onError(Runnable)} method. If no error handler is registered, the
+ * default behavior is to print the exception and its backtrace to the standard
+ * error stream.
+ * <p>
+ * The following example shows how an {@code EventStream} can be used to listen
+ * to events on a JVM running Flight Recorder
+ *
+ * <pre>
+ * <code>
+ * try (var es = EventStream.openRepository()) {
+ *   es.onEvent("jdk.CPULoad", event -> {
+ *     System.out.println("CPU Load " + event.getEndTime());
+ *     System.out.println(" Machine total: " + 100 * event.getFloat("machineTotal") + "%");
+ *     System.out.println(" JVM User: " + 100 * event.getFloat("jvmUser") + "%");
+ *     System.out.println(" JVM System: " + 100 * event.getFloat("jvmSystem") + "%");
+ *     System.out.println();
+ *   });
+ *   es.onEvent("jdk.GarbageCollection", event -> {
+ *     System.out.println("Garbage collection: " + event.getLong("gcId"));
+ *     System.out.println(" Cause: " + event.getString("cause"));
+ *     System.out.println(" Total pause: " + event.getDuration("sumOfPauses"));
+ *     System.out.println(" Longest pause: " + event.getDuration("longestPause"));
+ *     System.out.println();
+ *   });
+ *   es.start();
+ * }
+ * </code>
+ * </pre>
+ * <p>
+ * To start recording together with the stream, see {@link RecordingStream}.
+ *
+ * @since 14
+ */
+public interface EventStream extends AutoCloseable {
+    /**
+     * Creates a stream from the repository of the current Java Virtual Machine
+     * (JVM).
+     * <p>
+     * By default, the stream starts with the next event flushed by Flight
+     * Recorder.
+     *
+     * @return an event stream, not {@code null}
+     *
+     * @throws IOException if a stream can't be opened, or an I/O error occurs
+     *         when trying to access the repository
+     *
+     * @throws SecurityException if a security manager exists and the caller
+     *         does not have
+     *         {@code FlightRecorderPermission("accessFlightRecorder")}
+     */
+    public static EventStream openRepository() throws IOException {
+        Utils.checkAccessFlightRecorder();
+        return new EventDirectoryStream(AccessController.getContext(), null, SecuritySupport.PRIVILIGED, false);
+    }
+
+    /**
+     * Creates an event stream from a disk repository.
+     * <p>
+     * By default, the stream starts with the next event flushed by Flight
+     * Recorder.
+     *
+     * @param directory location of the disk repository, not {@code null}
+     *
+     * @return an event stream, not {@code null}
+     *
+     * @throws IOException if a stream can't be opened, or an I/O error occurs
+     *         when trying to access the repository
+     *
+     * @throws SecurityException if a security manager exists and its
+     *         {@code checkRead} method denies read access to the directory, or
+     *         files in the directory.
+     */
+    public static EventStream openRepository(Path directory) throws IOException {
+        Objects.nonNull(directory);
+        AccessControlContext acc = AccessController.getContext();
+        return new EventDirectoryStream(acc, directory, FileAccess.UNPRIVILIGED, false);
+    }
+
+    /**
+     * Creates an event stream from a file.
+     * <p>
+     * By default, the stream starts with the first event in the file.
+     *
+     * @param file location of the file, not {@code null}
+     *
+     * @return an event stream, not {@code null}
+     *
+     * @throws IOException if the file can't be opened, or an I/O error occurs
+     *         during reading
+     *
+     * @throws SecurityException if a security manager exists and its
+     *         {@code checkRead} method denies read access to the file
+     */
+    static EventStream openFile(Path file) throws IOException {
+        return new EventFileStream(AccessController.getContext(), file);
+    }
+
+    /**
+     * Registers an action to perform on all events in the stream.
+     *
+     * @param action an action to perform on each {@code RecordedEvent}, not
+     *        {@code null}
+     */
+    void onEvent(Consumer<RecordedEvent> action);
+
+    /**
+     * Registers an action to perform on all events matching a name.
+     *
+     * @param eventName the name of the event, not {@code null}
+     *
+     * @param action an action to perform on each {@code RecordedEvent} matching
+     *        the event name, not {@code null}
+     */
+    void onEvent(String eventName, Consumer<RecordedEvent> action);
+
+    /**
+     * Registers an action to perform after the stream has been flushed.
+     *
+     * @param action an action to perform after the stream has been
+     *        flushed, not {@code null}
+     */
+    void onFlush(Runnable action);
+
+    /**
+     * Registers an action to perform if an exception occurs.
+     * <p>
+     * if an action is not registered, an exception stack trace is printed to
+     * standard error.
+     * <p>
+     * Registering an action overrides the default behavior. If multiple actions
+     * have been registered, they are performed in the order of registration.
+     * <p>
+     * If this method itself throws an exception, resulting behavior is
+     * undefined.
+     *
+     * @param action an action to perform if an exception occurs, not
+     *        {@code null}
+     */
+    void onError(Consumer<Throwable> action);
+
+    /**
+     * Registers an action to perform when the stream is closed.
+     * <p>
+     * If the stream is already closed, the action will be performed immediately
+     * in the current thread.
+     *
+     * @param action an action to perform after the stream is closed, not
+     *        {@code null}
+     * @see #close()
+     */
+    void onClose(Runnable action);
+
+    /**
+     * Releases all resources associated with this stream.
+     * <p>
+     * Closing a previously closed stream has no effect.
+     */
+    void close();
+
+    /**
+     * Unregisters an action.
+     * <p>
+     * If the action has been registered multiple times, all instances are
+     * unregistered.
+     *
+     * @param action the action to unregister, not {@code null}
+     *
+     * @return {@code true} if the action was unregistered, {@code false}
+     *         otherwise
+     *
+     * @see #onEvent(Consumer)
+     * @see #onEvent(String, Consumer)
+     * @see #onFlush(Runnable)
+     * @see #onClose(Runnable)
+     * @see #onError(Consumer)
+     */
+    boolean remove(Object action);
+
+    /**
+     * Specifies that the event object in an {@link #onEvent(Consumer)} action
+     * can be reused.
+     * <p>
+     * If reuse is set to {@code true), an action should not keep a reference
+     * to the event object after the action has completed.
+     *
+     * @param reuse {@code true} if an event object can be reused, {@code false}
+     * otherwise
+     */
+    void setReuse(boolean reuse);
+
+    /**
+     * Specifies that events arrives in chronological order, sorted by the time
+     * they were committed to the stream.
+     *
+     * @param ordered if event objects arrive in chronological order to
+     *        {@code #onEvent(Consumer)}
+     */
+    void setOrdered(boolean ordered);
+
+    /**
+     * Specifies the start time of the stream.
+     * <p>
+     * The start time must be set before starting the stream
+     *
+     * @param startTime the start time, not {@code null}
+     *
+     * @throws IllegalStateException if the stream is already started
+     *
+     * @see #start()
+     * @see #startAsync()
+     */
+    void setStartTime(Instant startTime);
+
+    /**
+     * Specifies the end time of the stream.
+     * <p>
+     * The end time must be set before starting the stream.
+     * <p>
+     * At end time, the stream is closed.
+     *
+     * @param endTime the end time, not {@code null}
+     *
+     * @throws IllegalStateException if the stream is already started
+     *
+     * @see #start()
+     * @see #startAsync()
+     */
+    void setEndTime(Instant endTime);
+
+    /**
+     * Start processing of actions.
+     * <p>
+     * Actions are performed in the current thread.
+     *
+     * @throws IllegalStateException if the stream is already started or closed
+     */
+    void start();
+
+    /**
+     * Start asynchronous processing of actions.
+     * <p>
+     * Actions are performed in a single separate thread.
+     *
+     * @throws IllegalStateException if the stream is already started or closed
+     */
+    void startAsync();
+
+    /**
+     * Blocks until all actions are completed, or the stream is closed, or the
+     * timeout occurs, or the current thread is interrupted, whichever happens
+     * first.
+     *
+     * @param timeout the maximum time to wait, not {@code null}
+     *
+     * @throws IllegalArgumentException if timeout is negative
+     * @throws InterruptedException if interrupted while waiting
+     *
+     * @see #start()
+     * @see #startAsync()
+     * @see Thread#interrupt()
+     */
+    void awaitTermination(Duration timeout) throws InterruptedException;
+
+    /**
+     * Blocks until all actions are completed, or the stream is closed, or the
+     * current thread is interrupted, whichever happens first.
+     *
+     * @throws InterruptedException if interrupted while waiting
+     *
+     * @see #start()
+     * @see #startAsync()
+     * @see Thread#interrupt()
+     */
+    void awaitTermination() throws InterruptedException;
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/LongMap.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.consumer;
-
-import java.util.HashMap;
-import java.util.Iterator;
-
-/**
- * Commonly used data structure for looking up objects given an id (long value)
- *
- * TODO: Implement without using Map and Long objects, to minimize allocation
- *
- * @param <T>
- */
-final class LongMap<T> implements Iterable<T> {
-    private final HashMap<Long, T> map;
-
-    LongMap() {
-        map = new HashMap<>(101);
-    }
-
-    void put(long id, T object) {
-        map.put(id, object);
-    }
-
-    T get(long id) {
-        return map.get(id);
-    }
-
-    @Override
-    public Iterator<T> iterator() {
-        return map.values().iterator();
-    }
-
-    Iterator<Long> keys() {
-        return map.keySet().iterator();
-    }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ObjectFactory.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.consumer;
-
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
-
-/**
- * Abstract factory for creating specialized types
- */
-abstract class ObjectFactory<T> {
-
-    final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types.";
-    final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX;
-    final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame";
-    final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame";
-
-    public static ObjectFactory<?> create(Type type, TimeConverter timeConverter) {
-        switch (type.getName()) {
-        case "java.lang.Thread":
-            return RecordedThread.createFactory(type, timeConverter);
-        case TYPE_PREFIX_VERSION_1 + "StackFrame":
-        case TYPE_PREFIX_VERSION_2 + "StackFrame":
-            return RecordedFrame.createFactory(type, timeConverter);
-        case TYPE_PREFIX_VERSION_1 + "Method":
-        case TYPE_PREFIX_VERSION_2 + "Method":
-            return RecordedMethod.createFactory(type, timeConverter);
-        case TYPE_PREFIX_VERSION_1 + "ThreadGroup":
-        case TYPE_PREFIX_VERSION_2 + "ThreadGroup":
-            return RecordedThreadGroup.createFactory(type, timeConverter);
-        case TYPE_PREFIX_VERSION_1 + "StackTrace":
-        case TYPE_PREFIX_VERSION_2 + "StackTrace":
-            return RecordedStackTrace.createFactory(type, timeConverter);
-        case TYPE_PREFIX_VERSION_1 + "ClassLoader":
-        case TYPE_PREFIX_VERSION_2 + "ClassLoader":
-            return RecordedClassLoader.createFactory(type, timeConverter);
-        case "java.lang.Class":
-            return RecordedClass.createFactory(type, timeConverter);
-        }
-        return null;
-    }
-
-    private final List<ValueDescriptor> valueDescriptors;
-
-    ObjectFactory(Type type) {
-        this.valueDescriptors = type.getFields();
-    }
-
-    T createObject(long id, Object value) {
-        if (value == null) {
-            return null;
-        }
-        if (value instanceof Object[]) {
-            return createTyped(valueDescriptors, id, (Object[]) value);
-        }
-        throw new InternalError("Object factory must have struct type");
-    }
-
-    abstract T createTyped(List<ValueDescriptor> valueDescriptors, long id, Object[] values);
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/Parser.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.consumer;
-
-import java.io.IOException;
-
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Base class for parsing data from a {@link RecordingInput}.
- */
-abstract class Parser {
-    /**
-     * Parses data from a {@link RecordingInput} and return an object.
-     *
-     * @param input input to read from
-     * @return an object
-     * @throws IOException if operation couldn't be completed due to I/O
-     *         problems
-     */
-    abstract Object parse(RecordingInput input) throws IOException;
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/ParserFactory.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,303 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.consumer;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import jdk.jfr.EventType;
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.MetadataDescriptor;
-import jdk.jfr.internal.PrivateAccess;
-import jdk.jfr.internal.Type;
-import jdk.jfr.internal.consumer.RecordingInput;
-
-/**
- * Class that create parsers suitable for reading events and constant pools
- */
-final class ParserFactory {
-    private final LongMap<Parser> parsers = new LongMap<>();
-    private final TimeConverter timeConverter;
-    private final LongMap<Type> types = new LongMap<>();
-    private final LongMap<ConstantMap> constantPools;
-
-    public ParserFactory(MetadataDescriptor metadata, TimeConverter timeConverter) throws IOException {
-        this.constantPools = new LongMap<>();
-        this.timeConverter = timeConverter;
-        for (Type t : metadata.getTypes()) {
-            types.put(t.getId(), t);
-        }
-        for (Type t : types) {
-            if (!t.getFields().isEmpty()) { // Avoid primitives
-                CompositeParser cp = createCompositeParser(t);
-                if (t.isSimpleType()) { // Reduce to nested parser
-                   parsers.put(t.getId(), cp.parsers[0]);
-                }
-
-            }
-        }
-        // Override event types with event parsers
-        for (EventType t : metadata.getEventTypes()) {
-            parsers.put(t.getId(), createEventParser(t));
-        }
-    }
-
-    public LongMap<Parser> getParsers() {
-        return parsers;
-    }
-
-    public LongMap<ConstantMap> getConstantPools() {
-        return constantPools;
-    }
-
-    public LongMap<Type> getTypeMap() {
-        return types;
-    }
-
-    private EventParser createEventParser(EventType eventType) throws IOException {
-        List<Parser> parsers = new ArrayList<Parser>();
-        for (ValueDescriptor f : eventType.getFields()) {
-            parsers.add(createParser(f));
-        }
-        return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0]));
-    }
-
-    private Parser createParser(ValueDescriptor v) throws IOException {
-        boolean constantPool = PrivateAccess.getInstance().isConstantPool(v);
-        if (v.isArray()) {
-            Type valueType = PrivateAccess.getInstance().getType(v);
-            ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null);
-            return new ArrayParser(createParser(element));
-        }
-        long id = v.getTypeId();
-        Type type = types.get(id);
-        if (type == null) {
-            throw new IOException("Type '" + v.getTypeName() + "' is not defined");
-        }
-        if (constantPool) {
-            ConstantMap pool = constantPools.get(id);
-            if (pool == null) {
-                pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
-                constantPools.put(id, pool);
-            }
-            return new ConstantMapValueParser(pool);
-        }
-        Parser parser = parsers.get(id);
-        if (parser == null) {
-            if (!v.getFields().isEmpty()) {
-                return createCompositeParser(type);
-            } else {
-                return registerParserType(type, createPrimitiveParser(type));
-            }
-        }
-        return parser;
-    }
-
-    private Parser createPrimitiveParser(Type type) throws IOException {
-        switch (type.getName()) {
-        case "int":
-            return new IntegerParser();
-        case "long":
-            return new LongParser();
-        case "float":
-            return new FloatParser();
-        case "double":
-            return new DoubleParser();
-        case "char":
-            return new CharacterParser();
-        case "boolean":
-            return new BooleanParser();
-        case "short":
-            return new ShortParser();
-        case "byte":
-            return new ByteParser();
-        case "java.lang.String":
-            ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
-            constantPools.put(type.getId(), pool);
-            return new StringParser(pool);
-        default:
-            throw new IOException("Unknown primitive type " + type.getName());
-        }
-    }
-
-    private Parser registerParserType(Type t, Parser parser) {
-        Parser p = parsers.get(t.getId());
-        // check if parser exists (known type)
-        if (p != null) {
-            return p;
-        }
-        parsers.put(t.getId(), parser);
-        return parser;
-    }
-
-    private CompositeParser createCompositeParser(Type type) throws IOException {
-        List<ValueDescriptor> vds = type.getFields();
-        Parser[] parsers = new Parser[vds.size()];
-        CompositeParser composite = new CompositeParser(parsers);
-        // need to pre-register so recursive types can be handled
-        registerParserType(type, composite);
-
-        int index = 0;
-        for (ValueDescriptor vd : vds) {
-            parsers[index++] = createParser(vd);
-        }
-        return composite;
-    }
-
-    private static final class BooleanParser extends Parser {
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
-        }
-    }
-
-    private static final class ByteParser extends Parser {
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            return Byte.valueOf(input.readByte());
-        }
-    }
-
-    private static final class LongParser extends Parser {
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            return Long.valueOf(input.readLong());
-        }
-    }
-
-    private static final class IntegerParser extends Parser {
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            return Integer.valueOf(input.readInt());
-        }
-    }
-
-    private static final class ShortParser extends Parser {
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            return Short.valueOf(input.readShort());
-        }
-    }
-
-    private static final class CharacterParser extends Parser {
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            return Character.valueOf(input.readChar());
-        }
-    }
-
-    private static final class FloatParser extends Parser {
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            return Float.valueOf(input.readFloat());
-        }
-    }
-
-    private static final class DoubleParser extends Parser {
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            return Double.valueOf(input.readDouble());
-        }
-    }
-
-    private static final class StringParser extends Parser {
-        private final ConstantMap stringConstantMap;
-        private String last;
-
-        StringParser(ConstantMap stringConstantMap) {
-            this.stringConstantMap = stringConstantMap;
-        }
-
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            String s = parseEncodedString(input);
-            if (!Objects.equals(s, last)) {
-                last = s;
-            }
-            return last;
-        }
-
-        private String parseEncodedString(RecordingInput input) throws IOException {
-            byte encoding = input.readByte();
-            if (encoding == RecordingInput.STRING_ENCODING_CONSTANT_POOL) {
-                long id = input.readLong();
-                return (String) stringConstantMap.get(id);
-            } else {
-                return input.readEncodedString(encoding);
-            }
-        }
-    }
-
-    private final static class ArrayParser extends Parser {
-        private final Parser elementParser;
-
-        public ArrayParser(Parser elementParser) {
-            this.elementParser = elementParser;
-        }
-
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            final int size = input.readInt();
-            final Object[] array = new Object[size];
-            for (int i = 0; i < size; i++) {
-                array[i] = elementParser.parse(input);
-            }
-            return array;
-        }
-    }
-
-    private final static class CompositeParser extends Parser {
-        private final Parser[] parsers;
-
-        public CompositeParser(Parser[] valueParsers) {
-            this.parsers = valueParsers;
-        }
-
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            final Object[] values = new Object[parsers.length];
-            for (int i = 0; i < values.length; i++) {
-                values[i] = parsers[i].parse(input);
-            }
-            return values;
-        }
-    }
-
-    private static final class ConstantMapValueParser extends Parser {
-        private final ConstantMap pool;
-
-        ConstantMapValueParser(ConstantMap pool) {
-            this.pool = pool;
-        }
-
-        @Override
-        public Object parse(RecordingInput input) throws IOException {
-            return pool.get(input.readLong());
-        }
-    }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClass.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClass.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,10 +26,8 @@
 package jdk.jfr.consumer;
 
 import java.lang.reflect.Modifier;
-import java.util.List;
 
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
 
 /**
  * A recorded Java type, such as a class or an interface.
@@ -37,21 +35,11 @@
  * @since 9
  */
 public final class RecordedClass extends RecordedObject {
-
-    static ObjectFactory<RecordedClass> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedClass>(type) {
-            @Override
-            RecordedClass createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedClass(desc, id, object, timeConverter);
-            }
-        };
-    }
-
     private final long uniqueId;
 
     // package private
-    private RecordedClass(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
-        super(descriptors, values, timeConverter);
+    RecordedClass(ObjectContext objectContext, long id, Object[] values) {
+        super(objectContext, values);
         this.uniqueId = id;
     }
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClassLoader.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedClassLoader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -25,10 +25,7 @@
 
 package jdk.jfr.consumer;
 
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
 
 /**
  * A recorded Java class loader.
@@ -36,21 +33,11 @@
  * @since 9
  */
 public final class RecordedClassLoader extends RecordedObject {
-
-    static ObjectFactory<RecordedClassLoader> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedClassLoader>(type) {
-            @Override
-            RecordedClassLoader createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedClassLoader(desc, id, object, timeConverter);
-            }
-        };
-    }
-
     private final long uniqueId;
 
     // package private
-    private RecordedClassLoader(List<ValueDescriptor> descriptors, long id, Object[] values, TimeConverter timeConverter) {
-        super(descriptors, values, timeConverter);
+    RecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
+        super(objectContext, values);
         this.uniqueId = id;
     }
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedEvent.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -32,6 +32,7 @@
 import jdk.jfr.EventType;
 import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.EventInstrumentation;
+import jdk.jfr.internal.consumer.ObjectContext;
 
 /**
  * A recorded event.
@@ -39,17 +40,14 @@
  * @since 9
  */
 public final class RecordedEvent extends RecordedObject {
-    private final EventType eventType;
-    private final long startTime;
-    // package private needed for efficient sorting
-    final long endTime;
+    long startTimeTicks;
+    long endTimeTicks;
 
     // package private
-    RecordedEvent(EventType type, List<ValueDescriptor> vds, Object[] values, long startTime, long endTime, TimeConverter timeConverter) {
-        super(vds, values, timeConverter);
-        this.eventType = type;
-        this.startTime = startTime;
-        this.endTime = endTime;
+    RecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) {
+        super(objectContext, values);
+        this.startTimeTicks = startTimeTicks;
+        this.endTimeTicks = endTimeTicks;
     }
 
     /**
@@ -78,7 +76,7 @@
      * @return the event type, not {@code null}
      */
     public EventType getEventType() {
-        return eventType;
+        return objectContext.eventType;
     }
 
     /**
@@ -89,7 +87,7 @@
      * @return the start time, not {@code null}
      */
     public Instant getStartTime() {
-        return Instant.ofEpochSecond(0, startTime);
+        return Instant.ofEpochSecond(0, getStartTimeNanos());
     }
 
     /**
@@ -100,7 +98,7 @@
      * @return the end time, not {@code null}
      */
     public Instant getEndTime() {
-        return Instant.ofEpochSecond(0, endTime);
+        return Instant.ofEpochSecond(0, getEndTimeNanos());
     }
 
     /**
@@ -109,7 +107,7 @@
      * @return the duration in nanoseconds, not {@code null}
      */
     public Duration getDuration() {
-        return Duration.ofNanos(endTime - startTime);
+        return Duration.ofNanos(getEndTimeNanos() - getStartTimeNanos());
     }
 
     /**
@@ -119,6 +117,31 @@
      */
     @Override
     public List<ValueDescriptor> getFields() {
-        return getEventType().getFields();
+        return objectContext.fields;
+    }
+
+    protected final Object objectAt(int index) {
+        if (index == 0) {
+            return startTimeTicks;
+        }
+        if (hasDuration()) {
+            if (index == 1) {
+                return endTimeTicks - startTimeTicks;
+            }
+            return objects[index - 2];
+        }
+        return objects[index - 1];
+    }
+
+    private boolean hasDuration() {
+        return objects.length + 2 == objectContext.fields.size();
+    }
+
+    private long getStartTimeNanos() {
+        return objectContext.convertTimestamp(startTimeTicks);
+    }
+
+    private long getEndTimeNanos() {
+        return objectContext.convertTimestamp(endTimeTicks);
     }
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedFrame.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedFrame.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,10 +26,8 @@
 package jdk.jfr.consumer;
 
 import java.lang.reflect.Modifier;
-import java.util.List;
 
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
 
 /**
  * A recorded frame in a stack trace.
@@ -37,19 +35,9 @@
  * @since 9
  */
 public final class RecordedFrame extends RecordedObject {
-
-    static ObjectFactory<RecordedFrame> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedFrame>(type) {
-            @Override
-            RecordedFrame createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedFrame(desc, object, timeConverter);
-            }
-        };
-    }
-
     // package private
-    RecordedFrame(List<ValueDescriptor> desc, Object[] objects, TimeConverter timeConverter) {
-        super(desc, objects, timeConverter);
+    RecordedFrame(ObjectContext objectContext, Object[] values) {
+        super(objectContext, values);
     }
 
     /**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,10 +26,8 @@
 package jdk.jfr.consumer;
 
 import java.lang.reflect.Modifier;
-import java.util.List;
 
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
 
 /**
  * A recorded method.
@@ -38,17 +36,9 @@
  */
 public final class RecordedMethod extends RecordedObject {
 
-    static ObjectFactory<RecordedMethod> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedMethod>(type) {
-            @Override
-            RecordedMethod createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedMethod(desc, object, timeConverter);
-            }
-        };
-    }
-
-    private RecordedMethod(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
-        super(descriptors, objects, timeConverter);
+    // package private
+    RecordedMethod(ObjectContext objectContext, Object[] values) {
+        super(objectContext, values);
     }
 
     /**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -25,18 +25,24 @@
 
 package jdk.jfr.consumer;
 
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.OffsetDateTime;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 
 import jdk.jfr.Timespan;
 import jdk.jfr.Timestamp;
 import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.consumer.JdkJfrConsumer;
+import jdk.jfr.internal.consumer.ObjectFactory;
 import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
 import jdk.jfr.internal.tool.PrettyWriter;
 
 /**
@@ -51,6 +57,89 @@
  */
 public class RecordedObject {
 
+    static{
+        JdkJfrConsumer access = new JdkJfrConsumer() {
+            public List<Type> readTypes(RecordingFile file) throws IOException {
+                return file.readTypes();
+            }
+
+            public boolean isLastEventInChunk(RecordingFile file) {
+                return file.isLastEventInChunk();
+            }
+
+            @Override
+            public Object getOffsetDataTime(RecordedObject event, String name) {
+                return event.getOffsetDateTime(name);
+            }
+
+            @Override
+            public RecordedClass newRecordedClass(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedClass(objectContext, id, values);
+            }
+
+            @Override
+            public RecordedClassLoader newRecordedClassLoader(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedClassLoader(objectContext, id, values);
+            }
+
+            @Override
+            public Comparator<? super RecordedEvent> eventComparator() {
+                return new Comparator<RecordedEvent>()  {
+                    @Override
+                    public int compare(RecordedEvent e1, RecordedEvent e2) {
+                        return Long.compare(e1.endTimeTicks, e2.endTimeTicks);
+                    }
+                };
+            }
+
+            @Override
+            public RecordedStackTrace newRecordedStackTrace(ObjectContext objectContext, Object[] values) {
+                return new RecordedStackTrace(objectContext, values);
+            }
+
+            @Override
+            public RecordedThreadGroup newRecordedThreadGroup(ObjectContext objectContext, Object[] values) {
+                return new RecordedThreadGroup(objectContext, values);
+            }
+
+            @Override
+            public RecordedFrame newRecordedFrame(ObjectContext objectContext, Object[] values) {
+                return new RecordedFrame(objectContext, values);
+            }
+
+            @Override
+            public RecordedThread newRecordedThread(ObjectContext objectContext, long id, Object[] values) {
+                return new RecordedThread(objectContext, id, values);
+            }
+
+            @Override
+            public RecordedMethod newRecordedMethod(ObjectContext objectContext, Object[] values) {
+                return new RecordedMethod(objectContext, values);
+            }
+
+            @Override
+            public RecordedEvent newRecordedEvent(ObjectContext objectContext, Object[] values, long startTimeTicks, long endTimeTicks) {
+                return new RecordedEvent(objectContext, values, startTimeTicks, endTimeTicks);
+            }
+
+            @Override
+            public void setStartTicks(RecordedEvent event, long startTicks) {
+               event.startTimeTicks = startTicks;
+            }
+
+            @Override
+            public void setEndTicks(RecordedEvent event, long endTicks) {
+               event.endTimeTicks = endTicks;
+            }
+
+            @Override
+            public Object[] eventValues(RecordedEvent event) {
+                return event.objects;
+            }
+        };
+        JdkJfrConsumer.setAccess(access);
+    }
+
     private final static class UnsignedValue {
         private final Object o;
 
@@ -63,15 +152,13 @@
         }
     }
 
-    private final Object[] objects;
-    private final List<ValueDescriptor> descriptors;
-    private final TimeConverter timeConverter;
+    final Object[] objects;
+    final ObjectContext objectContext;
 
     // package private, not to be subclassed outside this package
-    RecordedObject(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
-        this.descriptors = descriptors;
+    RecordedObject(ObjectContext objectContext, Object[] objects) {
+        this.objectContext = objectContext;
         this.objects = objects;
-        this.timeConverter = timeConverter;
     }
 
     // package private
@@ -101,7 +188,7 @@
      */
     public boolean hasField(String name) {
         Objects.requireNonNull(name);
-        for (ValueDescriptor v : descriptors) {
+        for (ValueDescriptor v : objectContext.fields) {
             if (v.getName().equals(name)) {
                 return true;
             }
@@ -109,7 +196,7 @@
         int dotIndex = name.indexOf(".");
         if (dotIndex > 0) {
             String structName = name.substring(0, dotIndex);
-            for (ValueDescriptor v : descriptors) {
+            for (ValueDescriptor v : objectContext.fields) {
                 if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
                     RecordedObject child = getValue(structName);
                     if (child != null) {
@@ -169,12 +256,16 @@
         return t;
     }
 
+    protected Object objectAt(int index) {
+        return objects[index];
+    }
+
     private Object getValue(String name, boolean allowUnsigned) {
         Objects.requireNonNull(name);
         int index = 0;
-        for (ValueDescriptor v : descriptors) {
+        for (ValueDescriptor v : objectContext.fields) {
             if (name.equals(v.getName())) {
-                Object object = objects[index];
+                Object object = objectAt(index);
                 if (object == null) {
                     // error or missing
                     return null;
@@ -200,7 +291,7 @@
                         return structifyArray(v, array, 0);
                     }
                     // struct
-                    return new RecordedObject(v.getFields(), (Object[]) object, timeConverter);
+                    return new RecordedObject(objectContext.getInstance(v), (Object[]) object);
                 }
             }
             index++;
@@ -209,7 +300,7 @@
         int dotIndex = name.indexOf(".");
         if (dotIndex > 0) {
             String structName = name.substring(0, dotIndex);
-            for (ValueDescriptor v : descriptors) {
+            for (ValueDescriptor v : objectContext.fields) {
                 if (!v.getFields().isEmpty() && v.getName().equals(structName)) {
                     RecordedObject child = getValue(structName);
                     String subName = name.substring(dotIndex + 1);
@@ -261,7 +352,7 @@
     private <T> T getTypedValue(String name, String typeName) {
         Objects.requireNonNull(name);
         // Validate name and type first
-        getValueDescriptor(descriptors, name, typeName);
+        getValueDescriptor(objectContext.fields, name, typeName);
         return getValue(name);
     }
 
@@ -270,15 +361,16 @@
             return null;
         }
         Object[] structArray = new Object[array.length];
+        ObjectContext objContext = objectContext.getInstance(v);
         for (int i = 0; i < structArray.length; i++) {
             Object arrayElement = array[i];
             if (dimension == 0) {
                 // No general way to handle structarrays
                 // without invoking ObjectFactory for every instance (which may require id)
                 if (isStackFrameType(v.getTypeName())) {
-                    structArray[i] = new RecordedFrame(v.getFields(), (Object[]) arrayElement, timeConverter);
+                    structArray[i] = new RecordedFrame(objContext, (Object[]) arrayElement);
                 } else {
-                    structArray[i] = new RecordedObject(v.getFields(), (Object[]) arrayElement, timeConverter);
+                    structArray[i] = new RecordedObject(objContext, (Object[]) arrayElement);
                 }
             } else {
                 structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1);
@@ -303,7 +395,7 @@
      * @return the fields, not {@code null}
      */
     public List<ValueDescriptor> getFields() {
-        return descriptors;
+        return objectContext.fields;
     }
 
     /**
@@ -725,7 +817,7 @@
     }
 
     private Duration getDuration(long timespan, String name) throws InternalError {
-        ValueDescriptor v = getValueDescriptor(descriptors, name, null);
+        ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
         if (timespan == Long.MIN_VALUE) {
             return Duration.ofSeconds(Long.MIN_VALUE, 0);
         }
@@ -741,7 +833,7 @@
             case Timespan.NANOSECONDS:
                 return Duration.ofNanos(timespan);
             case Timespan.TICKS:
-                return Duration.ofNanos(timeConverter.convertTimespan(timespan));
+                return Duration.ofNanos(objectContext.convertTimespan(timespan));
             }
             throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value());
         }
@@ -804,7 +896,7 @@
     }
 
     private Instant getInstant(long timestamp, String name) {
-        ValueDescriptor v = getValueDescriptor(descriptors, name, null);
+        ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null);
         Timestamp ts = v.getAnnotation(Timestamp.class);
         if (ts != null) {
             if (timestamp == Long.MIN_VALUE) {
@@ -814,7 +906,7 @@
             case Timestamp.MILLISECONDS_SINCE_EPOCH:
                 return Instant.ofEpochMilli(timestamp);
             case Timestamp.TICKS:
-                return Instant.ofEpochSecond(0, timeConverter.convertTimestamp(timestamp));
+                return Instant.ofEpochSecond(0, objectContext.convertTimestamp(timestamp));
             }
             throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value());
         }
@@ -889,12 +981,12 @@
     }
 
     // package private for now. Used by EventWriter
-    OffsetDateTime getOffsetDateTime(String name) {
+    private OffsetDateTime getOffsetDateTime(String name) {
         Instant instant = getInstant(name);
         if (instant.equals(Instant.MIN)) {
             return OffsetDateTime.MIN;
         }
-        return OffsetDateTime.ofInstant(getInstant(name), timeConverter.getZoneOffset());
+        return OffsetDateTime.ofInstant(getInstant(name), objectContext.getZoneOffset());
     }
 
     private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedStackTrace.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedStackTrace.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,8 +29,7 @@
 import java.util.Collections;
 import java.util.List;
 
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
 
 /**
  * A recorded stack trace.
@@ -38,18 +37,9 @@
  * @since 9
  */
 public final class RecordedStackTrace extends RecordedObject {
-
-    static ObjectFactory<RecordedStackTrace> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedStackTrace>(type) {
-            @Override
-            RecordedStackTrace createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedStackTrace(desc, object, timeConverter);
-            }
-        };
-    }
-
-    private RecordedStackTrace(List<ValueDescriptor> desc, Object[] values, TimeConverter timeConverter) {
-        super(desc, values, timeConverter);
+    // package private
+    RecordedStackTrace(ObjectContext objectContext, Object[] values) {
+        super(objectContext, values);
     }
 
     /**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThread.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -25,10 +25,7 @@
 
 package jdk.jfr.consumer;
 
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
 
 /**
  * A recorded thread.
@@ -36,20 +33,11 @@
  * @since 9
  */
 public final class RecordedThread extends RecordedObject {
-
-    static ObjectFactory<RecordedThread> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedThread>(type) {
-            @Override
-            RecordedThread createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedThread(desc, id, object, timeConverter);
-            }
-        };
-    }
-
     private final long uniqueId;
 
-    private RecordedThread(List<ValueDescriptor> descriptors, long id, Object[] values,  TimeConverter timeConverter) {
-        super(descriptors, values, timeConverter);
+    // package private
+    RecordedThread(ObjectContext objectContext, long id, Object[] values) {
+        super(objectContext, values);
         this.uniqueId = id;
     }
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -25,10 +25,7 @@
 
 package jdk.jfr.consumer;
 
-import java.util.List;
-
-import jdk.jfr.ValueDescriptor;
-import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.ObjectContext;
 
 /**
  * A recorded Java thread group.
@@ -36,18 +33,9 @@
  * @since 9
  */
 public final class RecordedThreadGroup extends RecordedObject {
-
-    static ObjectFactory<RecordedThreadGroup> createFactory(Type type, TimeConverter timeConverter) {
-        return new ObjectFactory<RecordedThreadGroup>(type) {
-            @Override
-            RecordedThreadGroup createTyped(List<ValueDescriptor> desc, long id, Object[] object) {
-                return new RecordedThreadGroup(desc, object, timeConverter);
-            }
-        };
-    }
-
-    private RecordedThreadGroup(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) {
-        super(descriptors, objects, timeConverter);
+    // package private
+    RecordedThreadGroup(ObjectContext objectContext, Object[] values) {
+        super(objectContext, values);
     }
 
     /**
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -32,7 +32,6 @@
 import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 
@@ -40,8 +39,9 @@
 import jdk.jfr.internal.MetadataDescriptor;
 import jdk.jfr.internal.Type;
 import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.ChunkParser;
+import jdk.jfr.internal.consumer.FileAccess;
 import jdk.jfr.internal.consumer.RecordingInput;
-import jdk.jfr.internal.consumer.RecordingInternals;
 
 /**
  * A recording file.
@@ -62,27 +62,6 @@
  * @since 9
  */
 public final class RecordingFile implements Closeable {
-    static{
-        RecordingInternals.INSTANCE = new RecordingInternals() {
-            public List<Type> readTypes(RecordingFile file) throws IOException {
-                return file.readTypes();
-            }
-
-            public boolean isLastEventInChunk(RecordingFile file) {
-                return file.isLastEventInChunk;
-            }
-
-            @Override
-            public Object getOffsetDataTime(RecordedObject event, String name) {
-                return event.getOffsetDateTime(name);
-            }
-
-            @Override
-            public void sort(List<RecordedEvent> events) {
-               Collections.sort(events, (e1, e2) -> Long.compare(e1.endTime, e2.endTime));
-            }
-        };
-    }
 
     private boolean isLastEventInChunk;
     private final File file;
@@ -104,7 +83,7 @@
      */
     public RecordingFile(Path file) throws IOException {
         this.file = file.toFile();
-        this.input = new RecordingInput(this.file);
+        this.input = new RecordingInput(this.file, FileAccess.UNPRIVILIGED);
         findNext();
     }
 
@@ -154,14 +133,15 @@
      */
     public List<EventType> readEventTypes() throws IOException {
         ensureOpen();
+        MetadataDescriptor previous = null;
         List<EventType> types = new ArrayList<>();
         HashSet<Long> foundIds = new HashSet<>();
-        try (RecordingInput ri = new RecordingInput(file)) {
+        try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILIGED)) {
             ChunkHeader ch = new ChunkHeader(ri);
-            aggregateEventTypeForChunk(ch, types, foundIds);
+            aggregateEventTypeForChunk(ch, null, types, foundIds);
             while (!ch.isLastChunk()) {
                 ch = ch.nextHeader();
-                aggregateEventTypeForChunk(ch, types, foundIds);
+                previous = aggregateEventTypeForChunk(ch, previous, types, foundIds);
             }
         }
         return types;
@@ -169,37 +149,41 @@
 
     List<Type> readTypes() throws IOException  {
         ensureOpen();
+        MetadataDescriptor previous = null;
         List<Type> types = new ArrayList<>();
         HashSet<Long> foundIds = new HashSet<>();
-        try (RecordingInput ri = new RecordingInput(file)) {
+        try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILIGED)) {
             ChunkHeader ch = new ChunkHeader(ri);
-            aggregateTypeForChunk(ch, types, foundIds);
+            ch.awaitFinished();
+            aggregateTypeForChunk(ch, null, types, foundIds);
             while (!ch.isLastChunk()) {
                 ch = ch.nextHeader();
-                aggregateTypeForChunk(ch, types, foundIds);
+                previous = aggregateTypeForChunk(ch, previous, types, foundIds);
             }
         }
         return types;
     }
 
-    private void aggregateTypeForChunk(ChunkHeader ch, List<Type> types, HashSet<Long> foundIds) throws IOException {
-        MetadataDescriptor m = ch.readMetadata();
+    private MetadataDescriptor aggregateTypeForChunk(ChunkHeader ch, MetadataDescriptor previous, List<Type> types, HashSet<Long> foundIds) throws IOException {
+        MetadataDescriptor m = ch.readMetadata(previous);
         for (Type t : m.getTypes()) {
             if (!foundIds.contains(t.getId())) {
                 types.add(t);
                 foundIds.add(t.getId());
             }
         }
+        return m;
     }
 
-    private static void aggregateEventTypeForChunk(ChunkHeader ch, List<EventType> types, HashSet<Long> foundIds) throws IOException {
-        MetadataDescriptor m = ch.readMetadata();
+    private static MetadataDescriptor aggregateEventTypeForChunk(ChunkHeader ch,  MetadataDescriptor previous, List<EventType> types, HashSet<Long> foundIds) throws IOException {
+        MetadataDescriptor m = ch.readMetadata(previous);
         for (EventType t : m.getEventTypes()) {
             if (!foundIds.contains(t.getId())) {
                 types.add(t);
                 foundIds.add(t.getId());
             }
         }
+        return m;
     }
 
     /**
@@ -246,6 +230,17 @@
         }
     }
 
+    // package protected
+    File getFile() {
+        return file;
+    }
+
+    // package protected
+    boolean isLastEventInChunk() {
+        return isLastEventInChunk;
+    }
+
+
     // either sets next to an event or sets eof to true
     private void findNext() throws IOException {
         while (nextEvent == null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.consumer;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import jdk.jfr.Configuration;
+import jdk.jfr.Event;
+import jdk.jfr.EventSettings;
+import jdk.jfr.EventType;
+import jdk.jfr.Recording;
+import jdk.jfr.internal.PlatformRecording;
+import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.SecuritySupport;
+import jdk.jfr.internal.Utils;
+import jdk.jfr.internal.consumer.EventDirectoryStream;
+
+/**
+ * A recording stream produces events from the current JVM (Java Virtual
+ * Machine).
+ * <p>
+ * The following example shows how to record events using the default
+ * configuration and print the Garbage Collection, CPU Load and JVM Information
+ * event to standard out.
+ * <pre>
+ * <code>
+ * Configuration c = Configuration.getConfiguration("default");
+ * try (var rs = new RecordingStream(c)) {
+ *     rs.onEvent("jdk.GarbageCollection", System.out::println);
+ *     rs.onEvent("jdk.CPULoad", System.out::println);
+ *     rs.onEvent("jdk.JVMInformation", System.out::println);
+ *     rs.start();
+ *   }
+ * }
+ * </code>
+ * </pre>
+ *
+ * @since 14
+ */
+public final class RecordingStream implements AutoCloseable, EventStream {
+
+    private final Recording recording;
+    private final EventDirectoryStream directoryStream;
+
+    /**
+     * Creates an event stream for the current JVM (Java Virtual Machine).
+     *
+     * @throws IllegalStateException if Flight Recorder can't be created (for
+     *         example, if the Java Virtual Machine (JVM) lacks Flight Recorder
+     *         support, or if the file repository can't be created or accessed)
+     *
+     * @throws SecurityException if a security manager exists and the caller
+     *         does not have
+     *         {@code FlightRecorderPermission("accessFlightRecorder")}
+     */
+    public RecordingStream() {
+        Utils.checkAccessFlightRecorder();
+        AccessControlContext acc = AccessController.getContext();
+        this.recording = new Recording();
+        this.recording.setFlushInterval(Duration.ofMillis(1000));
+        try {
+            this.directoryStream = new EventDirectoryStream(acc, null, SecuritySupport.PRIVILIGED, true);
+        } catch (IOException ioe) {
+            this.recording.close();
+            throw new IllegalStateException(ioe.getMessage());
+        }
+    }
+
+    /**
+     * Creates a recording stream using settings from a configuration.
+     * <p>
+     * The following example shows how to create a recording stream that uses a
+     * predefined configuration.
+     *
+     * <pre>
+     * <code>
+     * var c = Configuration.getConfiguration("default");
+     * try (var rs = new RecordingStream(c)) {
+     *   rs.onEvent(System.out::println);
+     *   rs.start();
+     * }
+     * </code>
+     * </pre>
+     *
+     * @param configuration configuration that contains the settings to use,
+     *        not {@code null}
+     *
+     * @throws IllegalStateException if Flight Recorder can't be created (for
+     *         example, if the Java Virtual Machine (JVM) lacks Flight Recorder
+     *         support, or if the file repository can't be created or accessed)
+     *
+     * @throws SecurityException if a security manager is used and
+     *         FlightRecorderPermission "accessFlightRecorder" is not set.
+     *
+     * @see Configuration
+     */
+    public RecordingStream(Configuration configuration) {
+        this();
+        recording.setSettings(configuration.getSettings());
+    }
+
+    /**
+     * Enables the event with the specified name.
+     * <p>
+     * If multiple events have the same name (for example, the same class is
+     * loaded in different class loaders), then all events that match the name
+     * are enabled. To enable a specific class, use the {@link #enable(Class)}
+     * method or a {@code String} representation of the event type ID.
+     *
+     * @param name the settings for the event, not {@code null}
+     *
+     * @return an event setting for further configuration, not {@code null}
+     *
+     * @see EventType
+     */
+    public EventSettings enable(String name) {
+        return recording.enable(name);
+    }
+
+    /**
+     * Replaces all settings for this recording stream.
+     * <p>
+     * The following example records 20 seconds using the "default" configuration
+     * and then changes settings to the "profile" configuration.
+     *
+     * <pre>
+     * <code>
+     *     Configuration defaultConfiguration = Configuration.getConfiguration("default");
+     *     Configuration profileConfiguration = Configuration.getConfiguration("profile");
+     *     try (var rs = new RecordingStream(defaultConfiguration) {
+     *        rs.onEvent(System.out::println);
+     *        rs.startAsync();
+     *        Thread.sleep(20_000);
+     *        rs.setSettings(profileConfiguration.getSettings());
+     *        Thread.sleep(20_000);
+     *     }
+     * </code>
+     * </pre>
+     *
+     * @param settings the settings to set, not {@code null}
+     *
+     * @see Recording#setSettings(Map)
+     */
+    public void setSettings(Map<String, String> settings) {
+        recording.setSettings(settings);
+    };
+
+    /**
+     * Enables event.
+     *
+     * @param eventClass the event to enable, not {@code null}
+     *
+     * @throws IllegalArgumentException if {@code eventClass} is an abstract
+     *         class or not a subclass of {@link Event}
+     *
+     * @return an event setting for further configuration, not {@code null}
+     */
+    public EventSettings enable(Class<? extends Event> eventClass) {
+        return recording.enable(eventClass);
+    }
+
+    /**
+     * Disables event with the specified name.
+     * <p>
+     * If multiple events with same name (for example, the same class is loaded
+     * in different class loaders), then all events that match the name are
+     * disabled. To disable a specific class, use the {@link #disable(Class)}
+     * method or a {@code String} representation of the event type ID.
+     *
+     * @param name the settings for the event, not {@code null}
+     *
+     * @return an event setting for further configuration, not {@code null}
+     *
+     */
+    public EventSettings disable(String name) {
+        return recording.disable(name);
+    }
+
+    /**
+     * Disables event.
+     *
+     * @param eventClass the event to enable, not {@code null}
+     *
+     * @throws IllegalArgumentException if {@code eventClass} is an abstract
+     *         class or not a subclass of {@link Event}
+     *
+     * @return an event setting for further configuration, not {@code null}
+     *
+     */
+    public EventSettings disable(Class<? extends Event> eventClass) {
+        return recording.disable(eventClass);
+    }
+
+    /**
+     * Determines how far back data is kept for the stream.
+     * <p>
+     * To control the amount of recording data stored on disk, the maximum
+     * length of time to retain the data can be specified. Data stored on disk
+     * that is older than the specified length of time is removed by the Java
+     * Virtual Machine (JVM).
+     * <p>
+     * If neither maximum limit or the maximum age is set, the size of the
+     * recording may grow indefinitely if events are on
+     *
+     * @param maxAge the length of time that data is kept, or {@code null} if
+     *        infinite
+     *
+     * @throws IllegalArgumentException if {@code maxAge} is negative
+     *
+     * @throws IllegalStateException if the recording is in the {@code CLOSED}
+     *         state
+     */
+    public void setMaxAge(Duration maxAge) {
+        recording.setMaxAge(maxAge);
+    }
+
+    /**
+     * Determines how much data is kept for the stream.
+     * <p>
+     * To control the amount of recording data that is stored on disk, the
+     * maximum amount of data to retain can be specified. When the maximum limit
+     * is exceeded, the Java Virtual Machine (JVM) removes the oldest chunk to
+     * make room for a more recent chunk.
+     * <p>
+     * If neither maximum limit or the maximum age is set, the size of the
+     * recording may grow indefinitely.
+     * <p>
+     * The size is measured in bytes.
+     *
+     * @param maxSize the amount of data to retain, {@code 0} if infinite
+     *
+     * @throws IllegalArgumentException if {@code maxSize} is negative
+     *
+     * @throws IllegalStateException if the recording is in {@code CLOSED} state
+     */
+    public void setMaxSize(long maxSize) {
+        recording.setMaxSize(maxSize);
+    }
+
+    /**
+     * Determines how often events are made available for streaming.
+     *
+     * @param interval the interval at which events are made available to the
+     *        stream, no {@code null}
+     *
+     * @throws IllegalArgumentException if {@code interval} is negative
+     *
+     * @throws IllegalStateException if the stream is closed
+     */
+    public void setFlushInterval(Duration interval) {
+        recording.setFlushInterval(interval);
+    }
+
+    @Override
+    public void setReuse(boolean reuse) {
+        directoryStream.setReuse(reuse);
+    }
+
+    @Override
+    public void setOrdered(boolean ordered) {
+        directoryStream.setOrdered(ordered);
+    }
+
+    @Override
+    public void setStartTime(Instant startTime) {
+        directoryStream.setStartTime(startTime);
+    }
+
+    @Override
+    public void setEndTime(Instant endTime) {
+        directoryStream.setEndTime(endTime);
+    }
+
+    @Override
+    public void onEvent(String eventName, Consumer<RecordedEvent> action) {
+        directoryStream.onEvent(eventName, action);
+    }
+
+    @Override
+    public void onEvent(Consumer<RecordedEvent> action) {
+        directoryStream.onEvent(action);
+    }
+
+    @Override
+    public void onFlush(Runnable action) {
+        directoryStream.onFlush(action);
+    }
+
+    @Override
+    public void onClose(Runnable action) {
+        directoryStream.onClose(action);
+    }
+
+    @Override
+    public void onError(Consumer<Throwable> action) {
+        directoryStream.onError(action);
+    }
+
+    @Override
+    public void close() {
+        recording.close();
+        directoryStream.close();
+    }
+
+    @Override
+    public boolean remove(Object action) {
+        return directoryStream.remove(action);
+    }
+
+    @Override
+    public void start() {
+        PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
+        long startNanos = pr.start();
+        directoryStream.start(startNanos);
+    }
+
+    @Override
+    public void startAsync() {
+        PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
+        long startNanos = pr.start();
+        directoryStream.startAsync(startNanos);
+    }
+
+    @Override
+    public void awaitTermination(Duration timeout) throws InterruptedException {
+        directoryStream.awaitTermination(timeout);
+    }
+
+    @Override
+    public void awaitTermination() throws InterruptedException {
+        directoryStream.awaitTermination();
+    }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/TimeConverter.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2016, 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.consumer;
-
-import java.time.DateTimeException;
-import java.time.ZoneOffset;
-
-import jdk.jfr.internal.LogLevel;
-import jdk.jfr.internal.LogTag;
-import jdk.jfr.internal.Logger;
-import jdk.jfr.internal.consumer.ChunkHeader;
-
-/**
- * Converts ticks to nanoseconds
- */
-final class TimeConverter {
-    private final long startTicks;
-    private final long startNanos;
-    private final double divisor;
-    private final ZoneOffset zoneOffet;
-
-    TimeConverter(ChunkHeader chunkHeader, int rawOffset) {
-        this.startTicks = chunkHeader.getStartTicks();
-        this.startNanos = chunkHeader.getStartNanos();
-        this.divisor = chunkHeader.getTicksPerSecond() / 1000_000_000L;
-        this.zoneOffet = zoneOfSet(rawOffset);
-    }
-
-    private ZoneOffset zoneOfSet(int rawOffset) {
-        try {
-            return ZoneOffset.ofTotalSeconds(rawOffset / 1000);
-        } catch (DateTimeException dte) {
-            Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Could not create ZoneOffset from raw offset " + rawOffset);
-        }
-        return ZoneOffset.UTC;
-    }
-
-    public long convertTimestamp(long ticks) {
-        return startNanos + (long) ((ticks - startTicks) / divisor);
-    }
-
-    public long convertTimespan(long ticks) {
-        return (long) (ticks / divisor);
-    }
-
-    public ZoneOffset getZoneOffset() {
-        return zoneOffet;
-    }
-}
--- a/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveRecordingEvent.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveRecordingEvent.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -40,6 +40,13 @@
 @StackTrace(false)
 public final class ActiveRecordingEvent extends AbstractJDKEvent {
 
+    public static final ThreadLocal<ActiveRecordingEvent> EVENT = new ThreadLocal<ActiveRecordingEvent>() {
+        @Override
+        protected ActiveRecordingEvent initialValue() {
+            return new ActiveRecordingEvent();
+        }
+    };
+
     @Label("Id")
     public long id;
 
@@ -53,6 +60,10 @@
     @Timespan(Timespan.MILLISECONDS)
     public long maxAge;
 
+    @Label("Flush Interval")
+    @Timespan(Timespan.MILLISECONDS)
+    public long flushInterval;
+
     @Label("Max Size")
     @DataAmount
     public long maxSize;
--- a/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveSettingEvent.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/events/ActiveSettingEvent.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -37,6 +37,13 @@
 @StackTrace(false)
 public final class ActiveSettingEvent extends AbstractJDKEvent {
 
+    public static final ThreadLocal<ActiveSettingEvent> EVENT = new ThreadLocal<ActiveSettingEvent>() {
+        @Override
+        protected ActiveSettingEvent initialValue() {
+            return new ActiveSettingEvent();
+        }
+    };
+
     @Label("Event Id")
     public long id;
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -32,11 +32,7 @@
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
 
 import jdk.internal.module.Modules;
 import jdk.jfr.AnnotationElement;
@@ -59,7 +55,14 @@
 // holds SettingControl instances that need to be released
 // when a class is unloaded (to avoid memory leaks).
 public final class EventControl {
-
+    final static class NamedControl {
+        public final String name;
+        public final Control control;
+        NamedControl(String name, Control control) {
+            this.name = name;
+            this.control = control;
+        }
+    }
     static final String FIELD_SETTING_PREFIX = "setting";
     private static final Type TYPE_ENABLED = TypeLibrary.createType(EnabledSetting.class);
     private static final Type TYPE_THRESHOLD = TypeLibrary.createType(ThresholdSetting.class);
@@ -67,24 +70,24 @@
     private static final Type TYPE_PERIOD = TypeLibrary.createType(PeriodSetting.class);
     private static final Type TYPE_CUTOFF = TypeLibrary.createType(CutoffSetting.class);
 
-    private final List<SettingInfo> settingInfos = new ArrayList<>();
-    private final Map<String, Control> eventControls = new HashMap<>(5);
+    private final ArrayList<SettingInfo> settingInfos = new ArrayList<>();
+    private final ArrayList<NamedControl> namedControls = new ArrayList<>(5);
     private final PlatformEventType type;
     private final String idName;
 
     EventControl(PlatformEventType eventType) {
-        eventControls.put(Enabled.NAME, defineEnabled(eventType));
+        addControl(Enabled.NAME, defineEnabled(eventType));
         if (eventType.hasDuration()) {
-            eventControls.put(Threshold.NAME, defineThreshold(eventType));
+            addControl(Threshold.NAME, defineThreshold(eventType));
         }
         if (eventType.hasStackTrace()) {
-            eventControls.put(StackTrace.NAME, defineStackTrace(eventType));
+            addControl(StackTrace.NAME, defineStackTrace(eventType));
         }
         if (eventType.hasPeriod()) {
-            eventControls.put(Period.NAME, definePeriod(eventType));
+            addControl(Period.NAME, definePeriod(eventType));
         }
         if (eventType.hasCutoff()) {
-            eventControls.put(Cutoff.NAME, defineCutoff(eventType));
+            addControl(Cutoff.NAME, defineCutoff(eventType));
         }
 
         ArrayList<AnnotationElement> aes = new ArrayList<>(eventType.getAnnotationElements());
@@ -99,6 +102,19 @@
         this.idName = String.valueOf(eventType.getId());
     }
 
+    private boolean hasControl(String name) {
+        for (NamedControl nc : namedControls) {
+            if (name.equals(nc.name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void addControl(String name, Control control) {
+        namedControls.add(new NamedControl(name, control));
+    }
+
     static void remove(PlatformEventType type, List<AnnotationElement> aes, Class<? extends java.lang.annotation.Annotation> clazz) {
         long id = Type.getTypeId(clazz);
         for (AnnotationElement a : type.getAnnotationElements()) {
@@ -131,7 +147,8 @@
                             if (n != null) {
                                 name = n.value();
                             }
-                            if (!eventControls.containsKey(name)) {
+
+                            if (!hasControl(name)) {
                                 defineSetting((Class<? extends SettingControl>) settingClass, m, type, name);
                             }
                         }
@@ -163,7 +180,7 @@
                     }
                 }
                 aes.trimToSize();
-                eventControls.put(settingName, si.settingControl);
+                addControl(settingName, si.settingControl);
                 eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, settingName, defaultValue, aes));
                 settingInfos.add(si);
             }
@@ -247,9 +264,9 @@
     }
 
     void disable() {
-        for (Control c : eventControls.values()) {
-            if (c instanceof EnabledSetting) {
-                c.setValueSafe("false");
+        for (NamedControl nc : namedControls) {
+            if (nc.control instanceof EnabledSetting) {
+                nc.control.setValueSafe("false");
                 return;
             }
         }
@@ -259,24 +276,23 @@
         if (!type.isRegistered()) {
             return;
         }
-        for (Map.Entry<String, Control> entry : eventControls.entrySet()) {
-            Control c = entry.getValue();
-            if (Utils.isSettingVisible(c, type.hasEventHook())) {
-                String value = c.getLastValue();
+        ActiveSettingEvent event = ActiveSettingEvent.EVENT.get();
+        for (NamedControl nc : namedControls) {
+            if (Utils.isSettingVisible(nc.control, type.hasEventHook())) {
+                String value = nc.control.getLastValue();
                 if (value == null) {
-                    value = c.getDefaultValue();
+                    value = nc.control.getDefaultValue();
                 }
-                ActiveSettingEvent ase = new ActiveSettingEvent();
-                ase.id = type.getId();
-                ase.name = entry.getKey();
-                ase.value = value;
-                ase.commit();
+                event.id = type.getId();
+                event.name = nc.name;
+                event.value = value;
+                event.commit();
             }
         }
     }
 
-    public Set<Entry<String, Control>> getEntries() {
-        return eventControls.entrySet();
+    public ArrayList<NamedControl> getNamedControls() {
+        return namedControls;
     }
 
     public PlatformEventType getEventType() {
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -363,74 +363,72 @@
             methodVisitor.visitMaxs(0, 0);
         });
 
-        // MyEvent#commit() - Java event writer
         updateMethod(METHOD_COMMIT, methodVisitor -> {
-                // if (!isEnable()) {
-                // return;
-                // }
-                methodVisitor.visitCode();
-                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false);
-                Label l0 = new Label();
-                methodVisitor.visitJumpInsn(Opcodes.IFNE, l0);
-                methodVisitor.visitInsn(Opcodes.RETURN);
-                methodVisitor.visitLabel(l0);
-                methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
-                // if (startTime == 0) {
-                // startTime = EventWriter.timestamp();
-                // } else {
-                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
-                methodVisitor.visitInsn(Opcodes.LCONST_0);
-                methodVisitor.visitInsn(Opcodes.LCMP);
-                Label durationalEvent = new Label();
-                methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent);
+            // if (!isEnable()) {
+            // return;
+            // }
+            methodVisitor.visitCode();
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false);
+            Label l0 = new Label();
+            methodVisitor.visitJumpInsn(Opcodes.IFNE, l0);
+            methodVisitor.visitInsn(Opcodes.RETURN);
+            methodVisitor.visitLabel(l0);
+            methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+            // if (startTime == 0) {
+            // startTime = EventWriter.timestamp();
+            // } else {
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+            methodVisitor.visitInsn(Opcodes.LCONST_0);
+            methodVisitor.visitInsn(Opcodes.LCMP);
+            Label durationalEvent = new Label();
+            methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent);
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
+            methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+            Label commit = new Label();
+            methodVisitor.visitJumpInsn(Opcodes.GOTO, commit);
+            // if (duration == 0) {
+            // duration = EventWriter.timestamp() - startTime;
+            // }
+            // }
+            methodVisitor.visitLabel(durationalEvent);
+            methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
+            methodVisitor.visitInsn(Opcodes.LCONST_0);
+            methodVisitor.visitInsn(Opcodes.LCMP);
+            methodVisitor.visitJumpInsn(Opcodes.IFNE, commit);
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
+            methodVisitor.visitInsn(Opcodes.LSUB);
+            methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
+            methodVisitor.visitLabel(commit);
+            // if (shouldCommit()) {
+            methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false);
+            Label end = new Label();
+            // eventHandler.write(...);
+            // }
+            methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
+            getEventHandler(methodVisitor);
+
+            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
+            for (FieldInfo fi : fieldInfos) {
                 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(),
-                        METHOD_TIME_STAMP.getDescriptor(), false);
-                methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
-                Label commit = new Label();
-                methodVisitor.visitJumpInsn(Opcodes.GOTO, commit);
-                // if (duration == 0) {
-                // duration = EventWriter.timestamp() - startTime;
-                // }
-                // }
-                methodVisitor.visitLabel(durationalEvent);
-                methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
-                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
-                methodVisitor.visitInsn(Opcodes.LCONST_0);
-                methodVisitor.visitInsn(Opcodes.LCMP);
-                methodVisitor.visitJumpInsn(Opcodes.IFNE, commit);
-                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
-                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
-                methodVisitor.visitInsn(Opcodes.LSUB);
-                methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
-                methodVisitor.visitLabel(commit);
-                // if (shouldCommit()) {
-                methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
-                methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false);
-                Label end = new Label();
-                // eventHandler.write(...);
-                // }
-                methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
-                getEventHandler(methodVisitor);
+                methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor);
+            }
 
-                methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
-                for (FieldInfo fi : fieldInfos) {
-                    methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
-                    methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor);
-                }
-
-                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false);
-                methodVisitor.visitLabel(end);
-                methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
-                methodVisitor.visitInsn(Opcodes.RETURN);
-                methodVisitor.visitEnd();
-            });
+            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false);
+            methodVisitor.visitLabel(end);
+            methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+            methodVisitor.visitInsn(Opcodes.RETURN);
+            methodVisitor.visitEnd();
+        });
 
         // MyEvent#shouldCommit()
         updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> {
@@ -469,6 +467,7 @@
         });
     }
 
+
     private void getEventHandler(MethodVisitor methodVisitor) {
         if (untypedEventHandler) {
             methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_OBJECT.getDescriptor());
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventWriter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
 package jdk.jfr.internal;
 
 import jdk.internal.misc.Unsafe;
-import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringParser;
 
 /**
  * Class must reside in a package with package restriction.
@@ -115,18 +115,18 @@
 
     public void putString(String s, StringPool pool) {
         if (s == null) {
-            putByte(RecordingInput.STRING_ENCODING_NULL);
+            putByte(StringParser.Encoding.NULL.byteValue());
             return;
         }
         int length = s.length();
         if (length == 0) {
-            putByte(RecordingInput.STRING_ENCODING_EMPTY_STRING);
+            putByte(StringParser.Encoding.EMPTY_STRING.byteValue());
             return;
         }
         if (length > StringPool.MIN_LIMIT && length < StringPool.MAX_LIMIT) {
             long l = StringPool.addString(s);
             if (l > 0) {
-                putByte(RecordingInput.STRING_ENCODING_CONSTANT_POOL);
+                putByte(StringParser.Encoding.CONSTANT_POOL.byteValue());
                 putLong(l);
                 return;
             }
@@ -138,7 +138,7 @@
     private void putStringValue(String s) {
         int length = s.length();
         if (isValidForSize(1 + 5 + 3 * length)) {
-            putUncheckedByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // 1 byte
+            putUncheckedByte(StringParser.Encoding.CHAR_ARRAY.byteValue()); // 1 byte
             putUncheckedInt(length); // max 5 bytes
             for (int i = 0; i < length; i++) {
                 putUncheckedChar(s.charAt(i)); // max 3 bytes
@@ -197,11 +197,7 @@
         if (currentPosition + requestedSize > maxPosition) {
             flushOnEnd = flush(usedSize(), requestedSize);
             // retry
-            if (currentPosition + requestedSize > maxPosition) {
-                Logger.log(LogTag.JFR_SYSTEM,
-                           LogLevel.WARN, () ->
-                               "Unable to commit. Requested size " + requestedSize + " too large");
-                valid = false;
+            if (!valid) {
                 return false;
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/FilePurger.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import jdk.jfr.internal.SecuritySupport.SafePath;
+
+// This class keeps track of files that can't be deleted
+// so they can a later staged be removed.
+final class FilePurger {
+
+    private final static Set<SafePath> paths = new LinkedHashSet<>();
+
+    public synchronized static void add(SafePath p) {
+        paths.add(p);
+        if (paths.size() > 1000) {
+            removeOldest();
+        }
+    }
+
+    public synchronized static void purge() {
+        if (paths.isEmpty()) {
+            return;
+        }
+
+        for (SafePath p : new ArrayList<>(paths)) {
+            if (delete(p)) {
+                paths.remove(p);
+            }
+        }
+    }
+
+    private static void removeOldest() {
+        SafePath oldest = paths.iterator().next();
+        paths.remove(oldest);
+    }
+
+    private static boolean delete(SafePath p) {
+        try {
+            SecuritySupport.delete(p);
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -270,7 +270,6 @@
      *
      * @param file the file where data should be written, or null if it should
      *        not be copied out (in memory).
-     *
      * @throws IOException
      */
     public native void setOutput(String file);
@@ -460,6 +459,16 @@
     public static native boolean flush(EventWriter writer, int uncommittedSize, int requestedSize);
 
     /**
+     * Flushes all thread buffers to disk and the constant pool data needed to read
+     * them.
+     * <p>
+     * When the method returns, the chunk header should be updated with valid
+     * pointers to the metadata event, last check point event, correct file size and
+     * the generation id.
+     *
+     */
+    public native void flush();
+    /**
      * Sets the location of the disk repository, to be used at an emergency
      * dump.
      *
@@ -523,4 +532,30 @@
      * @return if it is time to perform a chunk rotation
      */
     public native boolean shouldRotateDisk();
+
+    /**
+     * Exclude a thread from the jfr system
+     *
+     */
+    public native void exclude(Thread thread);
+
+    /**
+     * Include a thread back into the jfr system
+     *
+     */
+    public native void include(Thread thread);
+
+    /**
+     * Test if a thread ius currently excluded from the jfr system.
+     *
+     * @return is thread currently excluded
+     */
+    public native boolean isExcluded(Thread thread);
+
+    /**
+     * Get the start time in nanos from the header of the current chunk
+     *
+     *@return start time of the recording in nanos, -1 in case of in-memory
+     */
+    public native long getChunkStartNanos();
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/LogTag.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/LogTag.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -63,21 +63,25 @@
      */
     JFR_SYSTEM_METADATA(6),
     /**
+     *  Covers streaming (for Hotspot developers)
+     */
+    JFR_SYSTEM_STREAMING(7),
+    /**
      *  Covers metadata for Java user (for Hotspot developers)
      */
-    JFR_METADATA(7),
+    JFR_METADATA(8),
     /**
      * Covers events (for users of the JDK)
      */
-    JFR_EVENT(8),
+    JFR_EVENT(9),
     /**
      * Covers setting (for users of the JDK)
      */
-    JFR_SETTING(9),
+    JFR_SETTING(10),
     /**
      * Covers usage of jcmd with JFR
      */
-    JFR_DCMD(10);
+    JFR_DCMD(11);
 
     /* set from native side */
     volatile int tagSetLevel = 100; // prevent logging if JVM log system has not been initialized
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal;
+
+import java.util.BitSet;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+@SuppressWarnings("unchecked")
+public final class LongMap<T> {
+    private static final int MAXIMUM_CAPACITY = 1 << 30;
+    private static final long[] EMPTY_KEYS = new long[0];
+    private static final Object[] EMPTY_OBJECTS = new Object[0];
+    private static final int DEFAULT_SIZE = 32;
+    private static final Object NULL_OBJECT = new Object();
+
+    private final int bitCount;
+    private BitSet bitSet;
+    private long[] keys = EMPTY_KEYS;
+    private T[] objects = (T[]) EMPTY_OBJECTS;
+    private int count;
+    private int shift;
+
+    public LongMap() {
+        this.bitCount = 0;
+    }
+
+    public LongMap(int markBits) {
+        this.bitCount = markBits;
+        this.bitSet = new BitSet();
+    }
+
+    // Should be 2^n
+    private void initialize(int capacity) {
+        keys = new long[capacity];
+        objects = (T[]) new Object[capacity];
+        shift = 64 - (31 - Integer.numberOfLeadingZeros(capacity));
+    }
+
+    public void claimBits() {
+        // flip last bit back and forth to make bitset expand to max size
+        int lastBit = bitSetIndex(objects.length - 1, bitCount -1);
+        bitSet.flip(lastBit);
+        bitSet.flip(lastBit);
+    }
+
+    public void setId(long id, int bitIndex) {
+        int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
+        bitSet.set(bitSetIndex, true);
+    }
+
+    public void clearId(long id, int bitIndex) {
+        int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
+        bitSet.set(bitSetIndex, false);
+    }
+
+    public boolean isSetId(long id, int bitIndex) {
+        int bitSetIndex = bitSetIndex(tableIndexOf(id), bitIndex);
+        return bitSet.get(bitSetIndex);
+    }
+
+    private int bitSetIndex(int tableIndex, int bitIndex) {
+        return bitCount * tableIndex + bitIndex;
+    }
+
+    private int tableIndexOf(long id) {
+        int index = index(id);
+        while (true) {
+            if (objects[index] == null) {
+                throw new InternalError("Unknown id");
+            }
+            if (keys[index] == id) {
+                return index;
+            }
+            index++;
+            if (index == keys.length) {
+                index = 0;
+            }
+        }
+    }
+
+    public boolean hasKey(long id) {
+        int index = index(id);
+        while (true) {
+            if (objects[index] == null) {
+               return false;
+            }
+            if (keys[index] == id) {
+                return true;
+            }
+            index++;
+            if (index == keys.length) {
+                index = 0;
+            }
+        }
+    }
+
+    public void expand(int size) {
+        int l = 4 * size / 3;
+        if (l <= keys.length) {
+            return;
+        }
+        int n = tableSizeFor(l);
+        LongMap<T> temp = new LongMap<>(bitCount);
+        temp.initialize(n);
+        // Optimization, avoid growing while copying bits
+        if (bitCount > 0 && !bitSet.isEmpty()) {
+           temp.claimBits();
+           claimBits();
+        }
+        for (int tIndex = 0; tIndex < keys.length; tIndex++) {
+            T o = objects[tIndex];
+            if (o != null) {
+                long key = keys[tIndex];
+                temp.put(key, o);
+                if (bitCount != 0) {
+                    for (int bIndex = 0; bIndex < bitCount; bIndex++) {
+                        boolean bitValue = isSetId(key, bIndex);
+                        if (bitValue) {
+                            temp.setId(key, bIndex);
+                        }
+                    }
+                }
+            }
+        }
+        keys = temp.keys;
+        objects = temp.objects;
+        shift = temp.shift;
+        bitSet = temp.bitSet;
+    }
+
+    public void put(long id, T object) {
+        if (keys == EMPTY_KEYS) {
+            // Lazy initialization
+            initialize(DEFAULT_SIZE);
+        }
+        if (object == null) {
+            object = (T) NULL_OBJECT;
+        }
+
+        int index = index(id);
+        // probe for empty slot
+        while (true) {
+            if (objects[index] == null) {
+                keys[index] = id;
+                objects[index] = object;
+                count++;
+                // Don't expand lazy since it
+                // can cause resize when replacing
+                // an object.
+                if (count > 3 * keys.length / 4) {
+                    expand(2 * keys.length);
+                }
+                return;
+            }
+            // if it already exists, replace
+            if (keys[index] == id) {
+                objects[index] = object;
+                return;
+            }
+            index++;
+            if (index == keys.length) {
+                index = 0;
+            }
+        }
+    }
+    public T getAt(int tableIndex) {
+        T o =  objects[tableIndex];
+        return o == NULL_OBJECT ? null : o;
+    }
+
+    public T get(long id) {
+        if (keys == EMPTY_KEYS) {
+            return null;
+        }
+        int index = index(id);
+        while (true) {
+            if (objects[index] == null) {
+                return null;
+            }
+            if (keys[index] == id) {
+                return getAt(index);
+            }
+            index++;
+            if (index == keys.length) {
+                index = 0;
+            }
+        }
+    }
+
+    private int index(long id) {
+        return (int) ((id * -7046029254386353131L) >>> shift);
+    }
+
+    // Copied from HashMap::tableSizeFor
+    private static final int tableSizeFor(int cap) {
+        int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
+        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+    }
+
+    public void forEachKey(LongConsumer keyTraverser) {
+        for (int i = 0; i < keys.length; i++) {
+            if (objects[i] != null) {
+                keyTraverser.accept(keys[i]);
+            }
+        }
+    }
+
+    public void forEach(Consumer<T> consumer) {
+        for (int i = 0; i < keys.length; i++) {
+            T o = objects[i];
+            if (o != null) {
+                consumer.accept(o);
+            }
+        }
+    }
+
+    public int size() {
+        return count;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < objects.length; i++) {
+            sb.append(i);
+            sb.append(": id=");
+            sb.append(keys[i]);
+            sb.append(" ");
+            sb.append(objects[i]);
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataDescriptor.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataDescriptor.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -25,7 +25,6 @@
 
 package jdk.jfr.internal;
 
-import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -35,6 +34,7 @@
 import java.util.TimeZone;
 
 import jdk.jfr.EventType;
+import jdk.jfr.internal.consumer.RecordingInput;
 
 /**
  * Metadata about a chunk
@@ -214,6 +214,7 @@
     long gmtOffset;
     String locale;
     Element root;
+    public long metadataId;
 
     // package private
     MetadataDescriptor() {
@@ -252,7 +253,7 @@
         return locale;
     }
 
-    public static MetadataDescriptor read(DataInput input) throws IOException {
+    public static MetadataDescriptor read(RecordingInput input) throws IOException {
         MetadataReader r = new MetadataReader(input);
         return r.getDescriptor();
     }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -49,6 +49,8 @@
 import jdk.jfr.SettingDescriptor;
 import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.MetadataDescriptor.Element;
+import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringParser;
 
 /**
  * Parses metadata.
@@ -61,12 +63,13 @@
     private final MetadataDescriptor descriptor;
     private final Map<Long, Type> types = new HashMap<>();
 
-    public MetadataReader(DataInput input) throws IOException {
+    public MetadataReader(RecordingInput input) throws IOException {
         this.input = input;
         int size = input.readInt();
         this.pool = new ArrayList<>(size);
+        StringParser p = new StringParser(null, false);
         for (int i = 0; i < size; i++) {
-            this.pool.add(input.readUTF());
+            this.pool.add((String) p.parse(input));
         }
         descriptor = new MetadataDescriptor();
         Element root = createElement();
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -46,6 +46,7 @@
 import jdk.jfr.Threshold;
 import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.RequestEngine.RequestHook;
+import jdk.jfr.internal.consumer.RepositoryFiles;
 import jdk.jfr.internal.handlers.EventHandler;
 
 public final class MetadataRepository {
@@ -207,10 +208,14 @@
     }
 
     public synchronized List<EventControl> getEventControls() {
-        List<EventControl> controls = new ArrayList<>();
+        List<Class<? extends jdk.internal.event.Event>> eventClasses = jvm.getAllEventClasses();
+        ArrayList<EventControl> controls = new ArrayList<>(eventClasses.size() + nativeControls.size());
         controls.addAll(nativeControls);
-        for (EventHandler eh : getEventHandlers()) {
-            controls.add(eh.getEventControl());
+        for (Class<? extends jdk.internal.event.Event> clazz : eventClasses) {
+            EventHandler eh = Utils.getHandler(clazz);
+            if (eh != null) {
+                controls.add(eh.getEventControl());
+            }
         }
         return controls;
     }
@@ -255,20 +260,24 @@
         staleMetadata = true;
     }
 
-    // Lock around setOutput ensures that other threads dosn't
-    // emit event after setOutput and unregister the event class, before a call
+    // Lock around setOutput ensures that other threads don't
+    // emit events after setOutput and unregister the event class, before a call
     // to storeDescriptorInJVM
     synchronized void setOutput(String filename) {
+        if (staleMetadata) {
+            storeDescriptorInJVM();
+        }
         jvm.setOutput(filename);
-
+        if (filename != null) {
+            RepositoryFiles.notifyNewFile();
+        }
         unregisterUnloaded();
         if (unregistered) {
-            staleMetadata = typeLibrary.clearUnregistered();
+            if (typeLibrary.clearUnregistered()) {
+                storeDescriptorInJVM();
+            }
             unregistered = false;
         }
-        if (staleMetadata) {
-            storeDescriptorInJVM();
-        }
     }
 
     private void unregisterUnloaded() {
@@ -307,4 +316,11 @@
         throw new InternalError("Mirror class must have annotation " + MirrorEvent.class.getName());
     }
 
+    public synchronized void flush() {
+        if (staleMetadata) {
+            storeDescriptorInJVM();
+        }
+        jvm.flush();
+    }
+
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataWriter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataWriter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -53,7 +53,7 @@
 import jdk.jfr.ValueDescriptor;
 import jdk.jfr.internal.MetadataDescriptor.Attribute;
 import jdk.jfr.internal.MetadataDescriptor.Element;
-import jdk.jfr.internal.consumer.RecordingInput;
+import jdk.jfr.internal.consumer.StringParser;
 
 /**
  * Class responsible for converting a list of types into a format that can be
@@ -94,10 +94,10 @@
 
     private void writeString(DataOutput out, String s) throws IOException {
         if (s == null ) {
-            out.writeByte(RecordingInput.STRING_ENCODING_NULL);
+            out.writeByte(StringParser.Encoding.NULL.byteValue());
             return;
         }
-        out.writeByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // encoding UTF-16
+        out.writeByte(StringParser.Encoding.CHAR_ARRAY.byteValue()); // encoding UTF-16
         int length = s.length();
         writeInt(out, length);
             for (int i = 0; i < length; i++) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -69,7 +69,7 @@
         super(name, Type.SUPER_TYPE_EVENT, id);
         this.dynamicSettings = dynamicSettings;
         this.isJVM = Type.isDefinedByJVM(id);
-        this.isMethodSampling = name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample");
+        this.isMethodSampling = isJVM && (name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample"));
         this.isJDK = isJDK;
         this.stackTraceOffset = stackTraceOffset(name, isJDK);
     }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecorder.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -31,7 +31,6 @@
 import static jdk.jfr.internal.LogTag.JFR;
 import static jdk.jfr.internal.LogTag.JFR_SYSTEM;
 
-import java.io.IOException;
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.time.Duration;
@@ -59,6 +58,7 @@
 
 public final class PlatformRecorder {
 
+
     private final List<PlatformRecording> recordings = new ArrayList<>();
     private final static List<SecureRecorderListener> changeListeners = new ArrayList<>();
     private final Repository repository;
@@ -96,6 +96,7 @@
             Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> {
                 result.add(new Timer("JFR Recording Scheduler", true));
             });
+            jvm.exclude(t);
             t.start();
             t.join();
             return result.get(0);
@@ -204,7 +205,7 @@
         repository.clear();
     }
 
-    synchronized void start(PlatformRecording recording) {
+    synchronized long start(PlatformRecording recording) {
         // State can only be NEW or DELAYED because of previous checks
         Instant now = Instant.now();
         recording.setStartTime(now);
@@ -215,14 +216,17 @@
         }
         boolean toDisk = recording.isToDisk();
         boolean beginPhysical = true;
+        long streamInterval = recording.getStreamIntervalMillis();
         for (PlatformRecording s : getRecordings()) {
             if (s.getState() == RecordingState.RUNNING) {
                 beginPhysical = false;
                 if (s.isToDisk()) {
                     toDisk = true;
                 }
+                streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis());
             }
         }
+        long startNanos = -1;
         if (beginPhysical) {
             RepositoryChunk newChunk = null;
             if (toDisk) {
@@ -233,6 +237,7 @@
             }
             currentChunk = newChunk;
             jvm.beginRecording_();
+            startNanos = jvm.getChunkStartNanos();
             recording.setState(RecordingState.RUNNING);
             updateSettings();
             writeMetaEvents();
@@ -242,6 +247,7 @@
                 newChunk = repository.newChunk(now);
                 RequestEngine.doChunkEnd();
                 MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString());
+                startNanos = jvm.getChunkStartNanos();
             }
             recording.setState(RecordingState.RUNNING);
             updateSettings();
@@ -251,8 +257,12 @@
             }
             currentChunk = newChunk;
         }
+        if (toDisk) {
+            RequestEngine.setFlushInterval(streamInterval);
+        }
+        RequestEngine.doChunkBegin();
 
-        RequestEngine.doChunkBegin();
+        return startNanos;
     }
 
     synchronized void stop(PlatformRecording recording) {
@@ -267,6 +277,7 @@
         Instant now = Instant.now();
         boolean toDisk = false;
         boolean endPhysical = true;
+        long streamInterval = Long.MAX_VALUE;
         for (PlatformRecording s : getRecordings()) {
             RecordingState rs = s.getState();
             if (s != recording && RecordingState.RUNNING == rs) {
@@ -274,6 +285,7 @@
                 if (s.isToDisk()) {
                     toDisk = true;
                 }
+                streamInterval = Math.min(streamInterval, s.getStreamIntervalMillis());
             }
         }
         OldObjectSample.emit(recording);
@@ -309,6 +321,13 @@
             currentChunk = newChunk;
             RequestEngine.doChunkBegin();
         }
+
+        if (toDisk) {
+            RequestEngine.setFlushInterval(streamInterval);
+        } else {
+            RequestEngine.setFlushInterval(Long.MAX_VALUE);
+        }
+
         recording.setState(RecordingState.STOPPED);
     }
 
@@ -338,6 +357,18 @@
         MetadataRepository.getInstance().setSettings(list);
     }
 
+    public synchronized void rotateIfRecordingToDisk() {
+        boolean disk = false;
+        for (PlatformRecording s : getRecordings()) {
+            if (RecordingState.RUNNING == s.getState() && s.isToDisk()) {
+                disk = true;
+            }
+        }
+        if (disk) {
+            rotateDisk();
+        }
+    }
+
     synchronized void rotateDisk() {
         Instant now = Instant.now();
         RepositoryChunk newChunk = repository.newChunk(now);
@@ -395,14 +426,14 @@
                 r.appendChunk(chunk);
             }
         }
+        FilePurger.purge();
     }
 
     private void writeMetaEvents() {
-
         if (activeRecordingEvent.isEnabled()) {
+            ActiveRecordingEvent event = ActiveRecordingEvent.EVENT.get();
             for (PlatformRecording r : getRecordings()) {
                 if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) {
-                    ActiveRecordingEvent event = new ActiveRecordingEvent();
                     event.id = r.getId();
                     event.name = r.getName();
                     WriteableUserPath p = r.getDestination();
@@ -415,6 +446,8 @@
                     event.maxSize = size == null ? Long.MAX_VALUE : size;
                     Instant start = r.getStartTime();
                     event.recordingStart = start == null ? Long.MAX_VALUE : start.toEpochMilli();
+                    Duration fi = r.getFlushInterval();
+                    event.flushInterval = fi == null ? Long.MAX_VALUE : fi.toMillis();
                     event.commit();
                 }
             }
@@ -448,7 +481,7 @@
                 JVM.FILE_DELTA_CHANGE.wait(duration < 10 ? 10 : duration);
             }
         } catch (InterruptedException e) {
-            e.printStackTrace();
+            // Ignore
         }
     }
 
@@ -550,4 +583,7 @@
         target.setStopTime(endTime);
         target.setInternalDuration(Duration.between(startTime, endTime));
     }
+
+
+
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformRecording.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -84,6 +84,7 @@
     private TimerTask startTask;
     private AccessControlContext noDestinationDumpOnExitAccessControlContext;
     private boolean shuoldWriteActiveRecordingEvent = true;
+    private Duration flushInterval = Duration.ofSeconds(1);
 
     PlatformRecording(PlatformRecorder recorder, long id) {
         // Typically the access control context is taken
@@ -98,9 +99,10 @@
         this.name = String.valueOf(id);
     }
 
-    public void start() {
+    public long start() {
         RecordingState oldState;
         RecordingState newState;
+        long startNanos = -1;
         synchronized (recorder) {
             oldState = getState();
             if (!Utils.isBefore(state, RecordingState.RUNNING)) {
@@ -111,7 +113,7 @@
                 startTask = null;
                 startTime = null;
             }
-            recorder.start(this);
+            startNanos = recorder.start(this);
             Logger.log(LogTag.JFR, LogLevel.INFO, () -> {
                 // Only print non-default values so it easy to see
                 // which options were added
@@ -143,6 +145,8 @@
             newState = getState();
         }
         notifyIfStateChanged(oldState, newState);
+
+        return startNanos;
     }
 
     public boolean stop(String reason) {
@@ -780,4 +784,28 @@
     public SafePath getDumpOnExitDirectory()  {
         return this.dumpOnExitDirectory;
     }
+
+    public void setFlushInterval(Duration interval) {
+        synchronized (recorder) {
+            if (getState() == RecordingState.CLOSED) {
+                throw new IllegalStateException("Can't set stream interval when recording is closed");
+            }
+            this.flushInterval = interval;
+        }
+    }
+
+    public Duration getFlushInterval() {
+        synchronized (recorder) {
+            return flushInterval;
+        }
+    }
+
+    public long getStreamIntervalMillis() {
+        synchronized (recorder) {
+            if (flushInterval != null) {
+                return flushInterval.toMillis();
+            }
+            return Long.MAX_VALUE;
+        }
+    }
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Repository.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Repository.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -43,6 +43,7 @@
 
     public final static DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter
             .ofPattern("yyyy_MM_dd_HH_mm_ss");
+    private static final String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
 
     private final Set<SafePath> cleanupDirectories = new HashSet<>();
     private SafePath baseLocation;
@@ -55,8 +56,7 @@
         return instance;
     }
 
-    public synchronized void setBasePath(SafePath baseLocation) throws Exception {
-
+    public synchronized void setBasePath(SafePath baseLocation) throws IOException {
         if(baseLocation.equals(this.baseLocation)) {
             Logger.log(LogTag.JFR, LogLevel.INFO, "Same base repository path " + baseLocation.toString() + " is set");
             return;
@@ -74,7 +74,7 @@
         this.baseLocation = baseLocation;
     }
 
-    synchronized void ensureRepository() throws Exception {
+    public synchronized void ensureRepository() throws IOException {
         if (baseLocation == null) {
             setBasePath(SecuritySupport.JAVA_IO_TMPDIR);
         }
@@ -96,7 +96,7 @@
         }
     }
 
-    private static SafePath createRepository(SafePath basePath) throws Exception {
+    private static SafePath createRepository(SafePath basePath) throws IOException {
         SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath);
         SafePath f = null;
 
@@ -113,13 +113,14 @@
         }
 
         if (i == MAX_REPO_CREATION_RETRIES) {
-            throw new Exception("Unable to create JFR repository directory using base location (" + basePath + ")");
+            throw new IOException("Unable to create JFR repository directory using base location (" + basePath + ")");
         }
         SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f);
+        SecuritySupport.setProperty(JFR_REPOSITORY_LOCATION_PROPERTY, canonicalRepositoryPath.toString());
         return canonicalRepositoryPath;
     }
 
-    private static SafePath createRealBasePath(SafePath safePath) throws Exception {
+    private static SafePath createRealBasePath(SafePath safePath) throws IOException {
         if (SecuritySupport.exists(safePath)) {
             if (!SecuritySupport.isWritable(safePath)) {
                 throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable");
@@ -159,7 +160,7 @@
                 SecuritySupport.clearDirectory(p);
                 Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p);
             } catch (IOException e) {
-                Logger.log(LogTag.JFR, LogLevel.ERROR, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
+                Logger.log(LogTag.JFR, LogLevel.INFO, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
             }
         }
     }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -63,10 +63,10 @@
                 LocalDateTime.ofInstant(startTime, z.getZone()));
         this.startTime = startTime;
         this.repositoryPath = path;
-        this.unFinishedFile = findFileName(repositoryPath, fileName, ".part");
+        this.unFinishedFile = findFileName(repositoryPath, fileName, ".jfr");
         this.file = findFileName(repositoryPath, fileName, ".jfr");
         this.unFinishedRAF = SecuritySupport.createRandomAccessFile(unFinishedFile);
-        SecuritySupport.touch(file);
+ //       SecuritySupport.touch(file);
     }
 
     private static SafePath findFileName(SafePath directory, String name, String extension) throws Exception {
@@ -105,8 +105,6 @@
     private static long finish(SafePath unFinishedFile, SafePath file) throws IOException {
         Objects.requireNonNull(unFinishedFile);
         Objects.requireNonNull(file);
-        SecuritySupport.delete(file);
-        SecuritySupport.moveReplace(unFinishedFile, file);
         return SecuritySupport.getFileSize(file);
     }
 
@@ -123,9 +121,11 @@
             SecuritySupport.delete(f);
             Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " deleted");
         } catch (IOException e) {
-            Logger.log(LogTag.JFR, LogLevel.ERROR, ()  -> "Repository chunk " + f + " could not be deleted: " + e.getMessage());
+            // Probably happens because file is being streamed
+            // on Windows where files in use can't be removed.
+            Logger.log(LogTag.JFR, LogLevel.DEBUG, ()  -> "Repository chunk " + f + " could not be deleted: " + e.getMessage());
             if (f != null) {
-                SecuritySupport.deleteOnExit(f);
+                FilePurger.add(f);
             }
         }
     }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/RequestEngine.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -30,6 +30,7 @@
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -96,6 +97,8 @@
 
     private final static List<RequestHook> entries = new CopyOnWriteArrayList<>();
     private static long lastTimeMillis;
+    private static long flushInterval = Long.MAX_VALUE;
+    private static long streamDelta;
 
     public static void addHook(AccessControlContext acc, PlatformEventType type, Runnable hook) {
         Objects.requireNonNull(acc);
@@ -209,7 +212,9 @@
             lastTimeMillis = now;
             return 0;
         }
-        for (RequestHook he : entries) {
+        Iterator<RequestHook> hookIterator = entries.iterator();
+        while(hookIterator.hasNext()) {
+            RequestHook he = hookIterator.next();
             long left = 0;
             PlatformEventType es = he.type;
             // Not enabled, skip.
@@ -228,7 +233,6 @@
                 // for wait > period
                 r_delta = 0;
                 he.execute();
-                ;
             }
 
             // calculate time left
@@ -250,7 +254,39 @@
                 min = left;
             }
         }
+        // Flush should happen after all periodic events has been emitted
+        // Repeat of the above algorithm, but using the stream interval.
+        if (flushInterval != Long.MAX_VALUE) {
+            long r_period = flushInterval;
+            long r_delta = streamDelta;
+            r_delta += delta;
+            if (r_delta >= r_period) {
+                r_delta = 0;
+                MetadataRepository.getInstance().flush();
+                Utils.notifyFlush();
+            }
+            long left = (r_period - r_delta);
+            if (left < 0) {
+                left = 0;
+            }
+            streamDelta = r_delta;
+            if (min == 0 || left < min) {
+                min = left;
+            }
+        }
+
         lastTimeMillis = now;
         return min;
     }
+
+    static void setFlushInterval(long millis) {
+        // Don't accept shorter interval than 1 s.
+        long interval = millis < 1000 ? 1000  : millis;
+        flushInterval = interval;
+        if (interval < flushInterval) {
+            synchronized (JVM.FILE_DELTA_CHANGE) {
+                JVM.FILE_DELTA_CHANGE.notifyAll();
+            }
+        }
+    }
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -38,6 +38,7 @@
 import java.lang.reflect.ReflectPermission;
 import java.nio.channels.FileChannel;
 import java.nio.channels.ReadableByteChannel;
+import java.nio.file.DirectoryStream;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -65,6 +66,7 @@
 import jdk.jfr.FlightRecorderListener;
 import jdk.jfr.FlightRecorderPermission;
 import jdk.jfr.Recording;
+import jdk.jfr.internal.consumer.FileAccess;
 
 /**
  * Contains JFR code that does
@@ -75,7 +77,7 @@
     private final static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
     private final static Module JFR_MODULE = Event.class.getModule();
     public  final static SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr");
-
+    public final static FileAccess PRIVILIGED = new Privileged();
     static final SafePath USER_HOME = getPathInProperty("user.home", null);
     static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null);
 
@@ -150,7 +152,7 @@
      * a malicious provider.
      *
      */
-    public static final class SafePath {
+    public static final class SafePath implements Comparable<SafePath> {
         private final Path path;
         private final String text;
 
@@ -168,11 +170,20 @@
             return path;
         }
 
+        public File toFile() {
+            return path.toFile();
+        }
+
         public String toString() {
             return text;
         }
 
         @Override
+        public int compareTo(SafePath that) {
+            return that.text.compareTo(this.text);
+        }
+
+        @Override
         public boolean equals(Object other) {
             if(other != null && other instanceof SafePath){
                 return this.toPath().equals(((SafePath) other).toPath());
@@ -303,6 +314,10 @@
         doPrivileged(() ->  MetadataRepository.getInstance().registerMirror(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT));
     }
 
+    public static void setProperty(String propertyName, String value) {
+        doPrivileged(() -> System.setProperty(propertyName, value), new PropertyPermission(propertyName, "write"));
+    }
+
     static boolean getBooleanProperty(String propertyName) {
         return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read"));
     }
@@ -343,7 +358,7 @@
         doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner()));
     }
 
-    static SafePath toRealPath(SafePath safePath) throws Exception {
+    static SafePath toRealPath(SafePath safePath) throws IOException {
         return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath()));
     }
 
@@ -369,7 +384,8 @@
     }
 
     public static boolean exists(SafePath safePath) throws IOException {
-        return doPrivilegedIOWithReturn(() -> Files.exists(safePath.toPath()));
+        // Files.exist(path) is allocation intensive
+        return doPrivilegedIOWithReturn(() -> safePath.toPath().toFile().exists());
     }
 
     public static boolean isDirectory(SafePath safePath) throws IOException {
@@ -421,7 +437,7 @@
     }
 
     static Class<?> defineClass(Class<?> lookupClass, byte[] bytes) {
-        return AccessController.doPrivileged(new PrivilegedAction<>() {
+        return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
             @Override
             public Class<?> run() {
                 try {
@@ -433,7 +449,7 @@
         });
     }
 
-    static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) {
+    public static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) {
         return doPrivilegedWithReturn(() -> new Thread(runnable, threadName), new Permission[0]);
     }
 
@@ -444,4 +460,32 @@
     public static SafePath getAbsolutePath(SafePath path) throws IOException {
         return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath())));
     }
+
+    private final static class Privileged extends FileAccess {
+        @Override
+        public RandomAccessFile openRAF(File f, String mode) throws IOException {
+            return doPrivilegedIOWithReturn( () -> new RandomAccessFile(f, mode));
+        }
+
+        @Override
+        public  DirectoryStream<Path> newDirectoryStream(Path directory)  throws IOException  {
+            return doPrivilegedIOWithReturn( () -> Files.newDirectoryStream(directory));
+        }
+
+        @Override
+        public  String getAbsolutePath(File f) throws IOException {
+            return doPrivilegedIOWithReturn( () -> f.getAbsolutePath());
+        }
+        @Override
+        public long length(File f) throws IOException {
+            return doPrivilegedIOWithReturn( () -> f.length());
+        }
+
+        @Override
+        public  long fileSize(Path p) throws IOException {
+            return doPrivilegedIOWithReturn( () -> Files.size(p));
+        }
+    }
+
+
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -33,10 +33,10 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.StringJoiner;
 
+import jdk.jfr.internal.EventControl.NamedControl;
 import jdk.jfr.internal.handlers.EventHandler;
 
 final class SettingsManager {
@@ -213,18 +213,21 @@
 
     void setEventControl(EventControl ec) {
         InternalSetting is = getInternalSetting(ec);
-        Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {");
-        for (Entry<String, Control> entry : ec.getEntries()) {
+        boolean shouldLog = Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO);
+        if (shouldLog) {
+            Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {");
+        }
+        for (NamedControl nc: ec.getNamedControls()) {
             Set<String> values = null;
-            String settingName = entry.getKey();
+            String settingName = nc.name;
             if (is != null) {
                 values = is.getValues(settingName);
             }
-            Control control = entry.getValue();
+            Control control = nc.control;
             if (values != null) {
                 control.apply(values);
                 String after = control.getLastValue();
-                if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) {
+                if (shouldLog) {
                     if (Utils.isSettingVisible(control, ec.getEventType().hasEventHook())) {
                         if (values.size() > 1) {
                             StringJoiner sj = new StringJoiner(", ", "{", "}");
@@ -241,14 +244,16 @@
                 }
             } else {
                 control.setDefault();
-                if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) {
+                if (shouldLog) {
                     String message = "  " + settingName + "=\"" + control.getLastValue() + "\"";
                     Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message);
                 }
             }
         }
         ec.writeActiveSettingEvent();
-        Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}");
+        if (shouldLog) {
+            Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}");
+        }
     }
 
     private InternalSetting getInternalSetting(EventControl ec) {
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java	Mon Nov 04 11:25:55 2019 +0000
@@ -407,6 +407,7 @@
     // Purpose of this method is to mark types that are reachable
     // from registered event types. Those types that are not reachable can
     // safely be removed
+    // Returns true if type was removed
     public boolean clearUnregistered() {
         Logger.log(LogTag.JFR_METADATA, LogLevel.TRACE, "Cleaning out obsolete metadata");
         List<Type> registered = new ArrayList<>();
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Utils.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -43,6 +43,7 @@
 import java.lang.reflect.Modifier;
 import java.nio.file.Path;
 import java.time.Duration;
+import java.time.Instant;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -65,17 +66,17 @@
 
 public final class Utils {
 
+    private static final Object flushObject = new Object();
     private static final String INFINITY = "infinity";
-
-    private static Boolean SAVE_GENERATED;
-
     public static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events";
     public static final String INSTRUMENT_PACKAGE_NAME = "jdk.jfr.internal.instrument";
     public static final String HANDLERS_PACKAGE_NAME = "jdk.jfr.internal.handlers";
     public static final String REGISTER_EVENT = "registerEvent";
     public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder";
+    private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
 
-    private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk.";
+    private static Boolean SAVE_GENERATED;
+
 
     public static void checkAccessFlightRecorder() throws SecurityException {
         SecurityManager sm = System.getSecurityManager();
@@ -595,4 +596,33 @@
         String idText = recording == null ? "" :  "-id-" + Long.toString(recording.getId());
         return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr";
     }
+
+    public static void takeNap(long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+            // ok
+        }
+    }
+
+    public static void notifyFlush() {
+        synchronized (flushObject) {
+            flushObject.notifyAll();
+        }
+    }
+
+    public static void waitFlush(long timeOut) {
+        synchronized (flushObject) {
+            try {
+                flushObject.wait(timeOut);
+            } catch (InterruptedException e) {
+                // OK
+            }
+        }
+
+    }
+
+    public static long timeToNanos(Instant timestamp) {
+        return timestamp.getEpochSecond() * 1_000_000_000L + timestamp.getNano();
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/AbstractEventStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.SecuritySupport;
+
+/*
+ * Purpose of this class is to simplify the implementation of
+ * an event stream.
+ */
+abstract class AbstractEventStream implements EventStream {
+    private final static AtomicLong counter = new AtomicLong(1);
+
+    private final Object terminated = new Object();
+    private final boolean active;
+    private final Runnable flushOperation = () -> dispatcher().runFlushActions();
+    private final AccessControlContext accessControllerContext;
+    private final StreamConfiguration configuration = new StreamConfiguration();
+
+    private volatile Thread thread;
+    private Dispatcher dispatcher;
+
+    private volatile boolean closed;
+
+    AbstractEventStream(AccessControlContext acc, boolean active) throws IOException {
+        this.accessControllerContext = Objects.requireNonNull(acc);
+        this.active = active;
+    }
+
+    @Override
+    abstract public void start();
+
+    @Override
+    abstract public void startAsync();
+
+    @Override
+    abstract public void close();
+
+    protected final Dispatcher dispatcher() {
+        if (configuration.hasChanged()) {
+            synchronized (configuration) {
+                dispatcher = new Dispatcher(configuration);
+            }
+        }
+        return dispatcher;
+    }
+
+    @Override
+    public final void setOrdered(boolean ordered) {
+        configuration.setOrdered(ordered);
+    }
+
+    @Override
+    public final void setReuse(boolean reuse) {
+        configuration.setReuse(reuse);
+    }
+
+    @Override
+    public final void setStartTime(Instant startTime) {
+        Objects.nonNull(startTime);
+        synchronized (configuration) {
+            if (configuration.started) {
+                throw new IllegalStateException("Stream is already started");
+            }
+            if (startTime.isBefore(Instant.EPOCH)) {
+                startTime = Instant.EPOCH;
+            }
+            configuration.setStartTime(startTime);
+        }
+    }
+
+    @Override
+    public final void setEndTime(Instant endTime) {
+        Objects.requireNonNull(endTime);
+        synchronized (configuration) {
+            if (configuration.started) {
+                throw new IllegalStateException("Stream is already started");
+            }
+            configuration.setEndTime(endTime);
+        }
+    }
+
+    @Override
+    public final void onEvent(Consumer<RecordedEvent> action) {
+        Objects.requireNonNull(action);
+        configuration.addEventAction(action);
+    }
+
+    @Override
+    public final void onEvent(String eventName, Consumer<RecordedEvent> action) {
+        Objects.requireNonNull(eventName);
+        Objects.requireNonNull(action);
+        configuration.addEventAction(eventName, action);
+    }
+
+    @Override
+    public final void onFlush(Runnable action) {
+        Objects.requireNonNull(action);
+        configuration.addFlushAction(action);
+    }
+
+    @Override
+    public final void onClose(Runnable action) {
+        Objects.requireNonNull(action);
+        configuration.addCloseAction(action);
+    }
+
+    @Override
+    public final void onError(Consumer<Throwable> action) {
+        Objects.requireNonNull(action);
+        configuration.addErrorAction(action);
+    }
+
+    @Override
+    public final boolean remove(Object action) {
+        Objects.requireNonNull(action);
+        return configuration.remove(action);
+    }
+
+    @Override
+    public final void awaitTermination() throws InterruptedException {
+        awaitTermination(Duration.ofMillis(0));
+    }
+
+    @Override
+    public final void awaitTermination(Duration timeout) throws InterruptedException {
+        Objects.requireNonNull(timeout);
+        if (timeout.isNegative()) {
+            throw new IllegalArgumentException("timeout value is negative");
+        }
+
+        long base = System.currentTimeMillis();
+        long now = 0;
+
+        long millis;
+        try {
+            millis = Math.multiplyExact(timeout.getSeconds(), 1000);
+        } catch (ArithmeticException a) {
+            millis = Long.MAX_VALUE;
+        }
+        int nanos = timeout.toNanosPart();
+        if (nanos == 0 && millis == 0) {
+            synchronized (terminated) {
+                while (!isClosed()) {
+                    terminated.wait(0);
+                }
+            }
+        } else {
+            while (!isClosed()) {
+                long delay = millis - now;
+                if (delay <= 0) {
+                    break;
+                }
+                synchronized (terminated) {
+                    terminated.wait(delay, nanos);
+                }
+                now = System.currentTimeMillis() - base;
+            }
+        }
+    }
+
+    protected abstract void process() throws IOException;
+
+    protected final void setClosed(boolean closed) {
+        this.closed = closed;
+    }
+
+    protected final boolean isClosed() {
+        return closed;
+    }
+
+    public final void startAsync(long startNanos) {
+        startInternal(startNanos);
+        Runnable r = () -> run(accessControllerContext);
+        thread = SecuritySupport.createThreadWitNoPermissions(nextThreadName(), r);
+        thread.start();
+    }
+
+    public final void start(long startNanos) {
+        startInternal(startNanos);
+        thread = Thread.currentThread();
+        run(accessControllerContext);
+    }
+
+    protected final Runnable getFlushOperation() {
+        return flushOperation;
+    }
+
+    private void startInternal(long startNanos) {
+        synchronized (configuration) {
+            if (configuration.started) {
+                throw new IllegalStateException("Event stream can only be started once");
+            }
+            if (active && configuration.startTime == null) {
+                configuration.setStartNanos(startNanos);
+            }
+            configuration.setStarted(true);
+        }
+    }
+
+    private void execute() {
+        try {
+            process();
+        } catch (IOException ioe) {
+            // This can happen if a chunk file is removed, or
+            // a file is access that has been closed
+            // This is "normal" behavior for streaming and the
+            // stream will be closed when this happens.
+        } finally {
+            Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "Execution of stream ended.");
+            try {
+                close();
+            } finally {
+                synchronized (terminated) {
+                    terminated.notifyAll();
+                }
+            }
+        }
+    }
+
+    private void run(AccessControlContext accessControlContext) {
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override
+            public Void run() {
+                execute();
+                return null;
+            }
+        }, accessControlContext);
+    }
+
+    private String nextThreadName() {
+        counter.incrementAndGet();
+        return "JFR Event Stream " + counter;
+    }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -25,48 +25,60 @@
 
 package jdk.jfr.internal.consumer;
 
-import java.io.DataInput;
 import java.io.IOException;
 
 import jdk.jfr.internal.LogLevel;
 import jdk.jfr.internal.LogTag;
 import jdk.jfr.internal.Logger;
 import jdk.jfr.internal.MetadataDescriptor;
+import jdk.jfr.internal.Utils;
 
 public final class ChunkHeader {
+    private static final long HEADER_SIZE = 68;
+    private static final byte UPDATING_CHUNK_HEADER = (byte) 255;
+    private static final long CHUNK_SIZE_POSITION = 8;
+    private static final long DURATION_NANOS_POSITION = 40;
+    private static final long FILE_STATE_POSITION = 64;
     private static final long METADATA_TYPE_ID = 0;
     private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' };
 
     private final short major;
     private final short minor;
-    private final long chunkSize;
     private final long chunkStartTicks;
     private final long ticksPerSecond;
     private final long chunkStartNanos;
-    private final long metadataPosition;
- //   private final long absoluteInitialConstantPoolPosition;
-    private final long absoluteChunkEnd;
-    private final long absoluteEventStart;
     private final long absoluteChunkStart;
-    private final boolean lastChunk;
     private final RecordingInput input;
-    private final long durationNanos;
     private final long id;
-    private long constantPoolPosition;
+    private long absoluteEventStart;
+    private long chunkSize = 0;
+    private long constantPoolPosition = 0;
+    private long metadataPosition = 0;
+    private long durationNanos;
+    private long absoluteChunkEnd;
+    private boolean isFinished;
+    private boolean finished;
 
     public ChunkHeader(RecordingInput input) throws IOException {
         this(input, 0, 0);
     }
 
     private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException {
+        this.absoluteChunkStart = absoluteChunkStart;
+        this.absoluteEventStart = absoluteChunkStart + HEADER_SIZE;
+        if (input.getFileSize() < HEADER_SIZE) {
+            throw new IOException("Not a complete Chunk header");
+        }
+        input.setValidSize(absoluteChunkStart + HEADER_SIZE);
         input.position(absoluteChunkStart);
         if (input.position() >= input.size()) {
-            throw new IOException("Chunk contains no data");
+           throw new IOException("Chunk contains no data");
         }
         verifyMagic(input);
         this.input = input;
         this.id = id;
-        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id);
+        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: " + id);
+        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: file=" + input.getFilename());
         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart);
         major = input.readRawShort();
         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major);
@@ -75,11 +87,11 @@
         if (major != 1 && major != 2) {
             throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK.");
         }
-        chunkSize = input.readRawLong();
+        input.readRawLong(); // chunk size
         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
-        this.constantPoolPosition = input.readRawLong();
+        input.readRawLong(); // constant pool position
         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
-        metadataPosition = input.readRawLong();
+        input.readRawLong(); // metadata position
         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
         chunkStartNanos = input.readRawLong(); // nanos since epoch
         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos);
@@ -91,21 +103,98 @@
         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond);
         input.readRawInt(); // features, not used
 
-        // set up boundaries
-        this.absoluteChunkStart = absoluteChunkStart;
-        absoluteChunkEnd = absoluteChunkStart + chunkSize;
-        lastChunk = input.size() == absoluteChunkEnd;
-        absoluteEventStart = input.position();
+        refresh();
+        input.position(absoluteEventStart);
+    }
 
-        // read metadata
-        input.position(absoluteEventStart);
+    void refresh() throws IOException {
+        while (true) {
+            byte fileState1;
+            input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+            while ((fileState1 = input.readPhysicalByte()) == UPDATING_CHUNK_HEADER) {
+                Utils.takeNap(1);
+                input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+            }
+            input.positionPhysical(absoluteChunkStart + CHUNK_SIZE_POSITION);
+            long chunkSize = input.readPhysicalLong();
+            long constantPoolPosition = input.readPhysicalLong();
+            long metadataPosition = input.readPhysicalLong();
+            input.positionPhysical(absoluteChunkStart + DURATION_NANOS_POSITION);
+            long durationNanos = input.readPhysicalLong();
+            input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+            byte fileState2 =  input.readPhysicalByte();
+            if (fileState1 == fileState2) { // valid header
+                finished = fileState1 == 0;
+                if (metadataPosition != 0) {
+                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Setting input size to " + (absoluteChunkStart + chunkSize));
+                    if (finished) {
+                        // This assumes that the whole recording
+                        // is finished if the first chunk is.
+                        // This is a limitation we may want to
+                        // remove, but greatly improves performance as
+                        // data can be read across chunk boundaries
+                        // of multi-chunk files and only once.
+                        input.setValidSize(input.getFileSize());
+                    } else {
+                        input.setValidSize(absoluteChunkStart + chunkSize);
+                    }
+                    this.chunkSize = chunkSize;
+                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
+                    this.constantPoolPosition = constantPoolPosition;
+                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
+                    this.metadataPosition = metadataPosition;
+                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
+                    this.durationNanos = durationNanos;
+                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos =" + durationNanos);
+                    isFinished = fileState2 == 0;
+                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: generation=" + fileState2);
+                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: finished=" + isFinished);
+                    Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: fileSize=" + input.size());
+                    absoluteChunkEnd = absoluteChunkStart + chunkSize;
+                    return;
+                }
+            }
+        }
+    }
+
+    public void awaitFinished() throws IOException {
+        if (finished) {
+            return;
+        }
+        long pos = input.position();
+        try {
+            input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
+            while (true) {
+                byte filestate = input.readPhysicalByte();
+                if (filestate == 0) {
+                    finished = true;
+                    return;
+                }
+                Utils.takeNap(1);
+            }
+        } finally {
+            input.position(pos);
+        }
+    }
+
+    public boolean isLastChunk() throws IOException {
+        awaitFinished();
+        // streaming files only have one chunk
+        return input.getFileSize() == absoluteChunkEnd;
+   }
+
+    public boolean isFinished() throws IOException {
+        return isFinished;
     }
 
     public ChunkHeader nextHeader() throws IOException {
         return new ChunkHeader(input, absoluteChunkEnd, id + 1);
     }
+    public MetadataDescriptor readMetadata() throws IOException {
+        return readMetadata(null);
+    }
 
-    public MetadataDescriptor readMetadata() throws IOException {
+    public MetadataDescriptor readMetadata(MetadataDescriptor previous) throws IOException {
         input.position(absoluteChunkStart + metadataPosition);
         input.readInt(); // size
         long id = input.readLong(); // event type id
@@ -115,15 +204,15 @@
         input.readLong(); // start time
         input.readLong(); // duration
         long metadataId = input.readLong();
-        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId);
-        // No need to read if metadataId == lastMetadataId, but we
-        // do it for verification purposes.
-        return MetadataDescriptor.read(input);
+        if (previous != null && metadataId == previous.metadataId) {
+            return previous;
+        }
+        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "New metadata id = " + metadataId);
+        MetadataDescriptor m =  MetadataDescriptor.read(input);
+        m.metadataId = metadataId;
+        return m;
     }
 
-    public boolean isLastChunk() {
-        return lastChunk;
-    }
 
     public short getMajor() {
         return major;
@@ -137,13 +226,22 @@
         return absoluteChunkStart;
     }
 
+    public long getAbsoluteEventStart() {
+        return absoluteEventStart;
+    }
     public long getConstantPoolPosition() {
         return constantPoolPosition;
     }
 
+    public long getMetataPosition() {
+        return metadataPosition;
+    }
     public long getStartTicks() {
         return chunkStartTicks;
     }
+    public long getChunkSize() {
+        return chunkSize;
+    }
 
     public double getTicksPerSecond() {
         return ticksPerSecond;
@@ -169,7 +267,7 @@
         return input;
     }
 
-    private static void verifyMagic(DataInput input) throws IOException {
+    private static void verifyMagic(RecordingInput input) throws IOException {
         for (byte c : FILE_MAGIC) {
             if (input.readByte() != c) {
                 throw new IOException("Not a Flight Recorder file");
@@ -181,4 +279,7 @@
         return absoluteEventStart;
     }
 
+    static long headerSize() {
+        return HEADER_SIZE;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkParser.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 2016, 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.StringJoiner;
+
+import jdk.jfr.EventType;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordedObject;
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.LongMap;
+import jdk.jfr.internal.MetadataDescriptor;
+import jdk.jfr.internal.Type;
+import jdk.jfr.internal.Utils;
+
+/**
+ * Parses a chunk.
+ *
+ */
+public final class ChunkParser {
+
+    static final class ParserConfiguration {
+        private final boolean reuse;
+        private final boolean ordered;
+        private final ParserFilter eventFilter;
+
+        long filterStart;
+        long filterEnd;
+
+        ParserConfiguration(long filterStart, long filterEnd, boolean reuse, boolean ordered, ParserFilter filter) {
+            this.filterStart = filterStart;
+            this.filterEnd = filterEnd;
+            this.reuse = reuse;
+            this.ordered = ordered;
+            this.eventFilter = filter;
+        }
+
+        public ParserConfiguration() {
+            this(0, Long.MAX_VALUE, false, false, ParserFilter.ACCEPT_ALL);
+        }
+
+        public boolean isOrdered() {
+            return ordered;
+        }
+    }
+
+    private enum CheckPointType {
+        // Checkpoint that finishes a flush segment
+        FLUSH(1),
+        // Checkpoint contains chunk header information in the first pool
+        CHUNK_HEADER(2),
+        // Checkpoint contains only statics that will not change from chunk to chunk
+        STATICS(4),
+        // Checkpoint contains thread related information
+        THREAD(8);
+        private final int mask;
+        private CheckPointType(int mask) {
+            this.mask = mask;
+        }
+
+        private boolean is(int flags) {
+            return (mask & flags) != 0;
+        }
+    }
+
+    private static final long CONSTANT_POOL_TYPE_ID = 1;
+    private static final String CHUNKHEADER = "jdk.types.ChunkHeader";
+    private final RecordingInput input;
+    private final ChunkHeader chunkHeader;
+    private final MetadataDescriptor metadata;
+    private final TimeConverter timeConverter;
+    private final MetadataDescriptor previousMetadata;
+    private final LongMap<ConstantLookup> constantLookups;
+
+    private LongMap<Type> typeMap;
+    private LongMap<Parser> parsers;
+    private boolean chunkFinished;
+
+    private Runnable flushOperation;
+    private ParserConfiguration configuration;
+
+    public ChunkParser(RecordingInput input) throws IOException {
+        this(input, new ParserConfiguration());
+    }
+
+    ChunkParser(RecordingInput input, ParserConfiguration pc) throws IOException {
+       this(new ChunkHeader(input), null, pc);
+    }
+
+    private ChunkParser(ChunkParser previous) throws IOException {
+        this(new ChunkHeader(previous.input), previous, new ParserConfiguration());
+     }
+
+    private ChunkParser(ChunkHeader header, ChunkParser previous, ParserConfiguration pc) throws IOException {
+        this.configuration = pc;
+        this.input = header.getInput();
+        this.chunkHeader = header;
+        if (previous == null) {
+            this.constantLookups = new LongMap<>();
+            this.previousMetadata = null;
+        } else {
+            this.constantLookups = previous.constantLookups;
+            this.previousMetadata = previous.metadata;
+            this.configuration = previous.configuration;
+        }
+        this.metadata = header.readMetadata(previousMetadata);
+        this.timeConverter = new TimeConverter(chunkHeader, metadata.getGMTOffset());
+        if (metadata != previousMetadata) {
+            ParserFactory factory = new ParserFactory(metadata, constantLookups, timeConverter);
+            parsers = factory.getParsers();
+            typeMap = factory.getTypeMap();
+            updateConfiguration();
+        } else {
+            parsers = previous.parsers;
+            typeMap = previous.typeMap;
+        }
+        constantLookups.forEach(c -> c.newPool());
+        fillConstantPools(0);
+        constantLookups.forEach(c -> c.getLatestPool().setResolving());
+        constantLookups.forEach(c -> c.getLatestPool().resolve());
+        constantLookups.forEach(c -> c.getLatestPool().setResolved());
+
+        input.position(chunkHeader.getEventStart());
+    }
+
+    public ChunkParser nextChunkParser() throws IOException {
+        return new ChunkParser(chunkHeader.nextHeader(), this, configuration);
+    }
+
+    private void updateConfiguration() {
+        updateConfiguration(configuration, false);
+    }
+
+    void updateConfiguration(ParserConfiguration configuration, boolean resetEventCache) {
+        this.configuration = configuration;
+        parsers.forEach(p -> {
+            if (p instanceof EventParser) {
+                EventParser ep = (EventParser) p;
+                if (resetEventCache) {
+                    ep.resetCache();
+                }
+                String name = ep.getEventType().getName();
+                ep.setOrdered(configuration.ordered);
+                ep.setReuse(configuration.reuse);
+                ep.setFilterStart(configuration.filterStart);
+                ep.setFilterEnd(configuration.filterEnd);
+                long threshold = configuration.eventFilter.getThreshold(name);
+                if (threshold >= 0) {
+                    ep.setEnabled(true);
+                    ep.setThresholdNanos(threshold);
+                } else {
+                    ep.setEnabled(false);
+                    ep.setThresholdNanos(Long.MAX_VALUE);
+                }
+            }
+        });
+    }
+
+    /**
+     * Reads an event and returns null when segment or chunk ends.
+     *
+     * @param awaitNewEvents wait for new data.
+     */
+    RecordedEvent readStreamingEvent(boolean awaitNewEvents) throws IOException {
+        long absoluteChunkEnd = chunkHeader.getEnd();
+        while (true) {
+            RecordedEvent event = readEvent();
+            if (event != null) {
+                return event;
+            }
+            if (!awaitNewEvents) {
+                return null;
+            }
+            long lastValid = absoluteChunkEnd;
+            long metadataPoistion = chunkHeader.getMetataPosition();
+            long contantPosition = chunkHeader.getConstantPoolPosition();
+            chunkFinished = awaitUpdatedHeader(absoluteChunkEnd);
+            if (chunkFinished) {
+                Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "At chunk end");
+                return null;
+            }
+            absoluteChunkEnd = chunkHeader.getEnd();
+            // Read metadata and constant pools for the next segment
+            if (chunkHeader.getMetataPosition() != metadataPoistion) {
+                Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found new metadata in chunk. Rebuilding types and parsers");
+                MetadataDescriptor metadata = chunkHeader.readMetadata(previousMetadata);
+                ParserFactory factory = new ParserFactory(metadata, constantLookups, timeConverter);
+                parsers = factory.getParsers();
+                typeMap = factory.getTypeMap();
+                updateConfiguration();;
+            }
+            if (contantPosition != chunkHeader.getConstantPoolPosition()) {
+                Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found new constant pool data. Filling up pools with new values");
+                constantLookups.forEach(c -> c.getLatestPool().setAllResolved(false));
+                fillConstantPools(contantPosition + chunkHeader.getAbsoluteChunkStart());
+                constantLookups.forEach(c -> c.getLatestPool().setResolving());
+                constantLookups.forEach(c -> c.getLatestPool().resolve());
+                constantLookups.forEach(c -> c.getLatestPool().setResolved());
+            }
+            input.position(lastValid);
+        }
+    }
+
+    /**
+     * Reads an event and returns null when the chunk ends
+     */
+    public RecordedEvent readEvent() throws IOException {
+        long absoluteChunkEnd = chunkHeader.getEnd();
+        while (input.position() < absoluteChunkEnd) {
+            long pos = input.position();
+            int size = input.readInt();
+            if (size == 0) {
+                throw new IOException("Event can't have zero size");
+            }
+            long typeId = input.readLong();
+            Parser p = parsers.get(typeId);
+            if (p instanceof EventParser) {
+                // Fast path
+                EventParser ep = (EventParser) p;
+                RecordedEvent event = ep.parse(input);
+                if (event != null) {
+                    input.position(pos + size);
+                    return event;
+                }
+                // Not accepted by filter
+            } else {
+                if (typeId == 1) { // checkpoint event
+                    if (flushOperation != null) {
+                        parseCheckpoint();
+                    }
+                } else {
+                    if (typeId != 0) { // Not metadata event
+                        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Unknown event type " + typeId);
+                    }
+                }
+            }
+            input.position(pos + size);
+        }
+        return null;
+    }
+
+    private void parseCheckpoint() throws IOException {
+        // Content has been parsed previously. This
+        // is to trigger flush
+        input.readLong(); // timestamp
+        input.readLong(); // duration
+        input.readLong(); // delta
+        byte typeFlags = input.readByte();
+        if (CheckPointType.FLUSH.is(typeFlags)) {
+            flushOperation.run();
+        }
+    }
+
+    private boolean awaitUpdatedHeader(long absoluteChunkEnd) throws IOException {
+        if (Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO)) {
+            Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Waiting for more data (streaming). Read so far: " + chunkHeader.getChunkSize() + " bytes");
+        }
+        while (true) {
+            chunkHeader.refresh();
+            if (absoluteChunkEnd != chunkHeader.getEnd()) {
+                return false;
+            }
+            if (chunkHeader.isFinished()) {
+                return true;
+            }
+            Utils.waitFlush(1000);
+        }
+    }
+
+    private void fillConstantPools(long abortCP) throws IOException {
+        long thisCP = chunkHeader.getConstantPoolPosition() + chunkHeader.getAbsoluteChunkStart();
+        long lastCP = -1;
+        long delta = -1;
+        boolean logTrace = Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE);
+        while (thisCP != abortCP && delta != 0) {
+            input.position(thisCP);
+            lastCP = thisCP;
+            int size = input.readInt(); // size
+            long typeId = input.readLong();
+            if (typeId != CONSTANT_POOL_TYPE_ID) {
+                throw new IOException("Expected check point event (id = 1) at position " + lastCP + ", but found type id = " + typeId);
+            }
+            input.readLong(); // timestamp
+            input.readLong(); // duration
+            delta = input.readLong();
+            thisCP += delta;
+            boolean flush = input.readBoolean();
+            int poolCount = input.readInt();
+            final long logLastCP = lastCP;
+            final long logDelta = delta;
+            if (logTrace) {
+                Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> {
+                    return "New constant pool: startPosition=" + logLastCP + ", size=" + size + ", deltaToNext=" + logDelta + ", flush=" + flush + ", poolCount=" + poolCount;
+                });
+            }
+            for (int i = 0; i < poolCount; i++) {
+                long id = input.readLong(); // type id
+                ConstantLookup lookup = constantLookups.get(id);
+                Type type = typeMap.get(id);
+                if (lookup == null) {
+                    if (type == null) {
+                        throw new IOException(
+                                "Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + lastCP + ", " + lastCP + size + "]");
+                    }
+                    if (type.getName() != CHUNKHEADER) {
+                        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used");
+                    }
+                    ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+                    lookup = new ConstantLookup(pool, type);
+                    constantLookups.put(type.getId(), lookup);
+                }
+                Parser parser = parsers.get(id);
+                if (parser == null) {
+                    throw new IOException("Could not find constant pool type with id = " + id);
+                }
+                try {
+                    int count = input.readInt();
+                    if (count == 0) {
+                        throw new InternalError("Pool " + type.getName() + " must contain at least one element ");
+                    }
+                    if (logTrace) {
+                        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Constant Pool " + i + ": " + type.getName());
+                    }
+                    for (int j = 0; j < count; j++) {
+                        long key = input.readLong();
+                        Object resolved = lookup.getPreviousResolved(key);
+                        if (resolved == null) {
+                            Object v = parser.parse(input);
+                            logConstant(key, v, false);
+                            lookup.getLatestPool().put(key, v);
+                        } else {
+                            parser.skip(input);
+                            logConstant(key, resolved, true);
+                            lookup.getLatestPool().putResolved(key, resolved);
+                        }
+                    }
+                } catch (Exception e) {
+                    throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + lastCP + ", " + lastCP + size + "]",
+                            e);
+                }
+            }
+            if (input.position() != lastCP + size) {
+                throw new IOException("Size of check point event doesn't match content");
+            }
+        }
+    }
+
+    private void logConstant(long key, Object v, boolean preresolved) {
+        if (!Logger.shouldLog(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE)) {
+            return;
+        }
+        String valueText;
+        if (v.getClass().isArray()) {
+            Object[] array = (Object[]) v;
+            StringJoiner sj = new StringJoiner(", ", "{", "}");
+            for (int i = 0; i < array.length; i++) {
+                sj.add(textify(array[i]));
+            }
+            valueText = sj.toString();
+        } else {
+            valueText = textify(v);
+        }
+        String suffix  = preresolved ? " (presolved)" :"";
+        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Constant: " + key + " = " + valueText + suffix);
+    }
+
+    private String textify(Object o) {
+        if (o == null) { // should not happen
+            return "null";
+        }
+        if (o instanceof String) {
+            return "\"" + String.valueOf(o) + "\"";
+        }
+        if (o instanceof RecordedObject) {
+            return o.getClass().getName();
+        }
+        if (o.getClass().isArray()) {
+            Object[] array = (Object[]) o;
+            if (array.length > 0) {
+                return textify(array[0]) + "[]"; // can it be recursive?
+            }
+        }
+        return String.valueOf(o);
+    }
+
+    private String getName(long id) {
+        Type type = typeMap.get(id);
+        return type == null ? ("unknown(" + id + ")") : type.getName();
+    }
+
+    public Collection<Type> getTypes() {
+        return metadata.getTypes();
+    }
+
+    public List<EventType> getEventTypes() {
+        return metadata.getEventTypes();
+    }
+
+    public boolean isLastChunk() throws IOException {
+        return chunkHeader.isLastChunk();
+    }
+
+    ChunkParser newChunkParser() throws IOException {
+        return new ChunkParser(this);
+    }
+
+    public boolean isChunkFinished() {
+        return chunkFinished;
+    }
+
+    public void setFlushOperation(Runnable flushOperation) {
+        this.flushOperation = flushOperation;
+    }
+
+    public long getChunkDuration() {
+        return chunkHeader.getDurationNanos();
+    }
+
+    public long getStartNanos() {
+        return chunkHeader.getStartNanos();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantLookup.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019, 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 jdk.jfr.internal.consumer;
+
+import jdk.jfr.internal.Type;
+
+final class ConstantLookup {
+    private final Type type;
+    private ConstantMap current;
+    private ConstantMap previous = ConstantMap.EMPTY;
+
+    ConstantLookup(ConstantMap current, Type type) {
+        this.current = current;
+        this.type = type;
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public ConstantMap getLatestPool() {
+        return current;
+    }
+
+    public void newPool() {
+        previous = current;
+        current = new ConstantMap(current.factory, current.name);
+    }
+
+    public Object getPreviousResolved(long key) {
+        return previous.getResolved(key);
+    }
+
+    public Object getCurrentResolved(long key) {
+        return current.getResolved(key);
+    }
+
+    public Object getCurrent(long key) {
+        return current.get(key);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantMap.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2016, 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import jdk.jfr.internal.LongMap;
+
+/**
+ * Holds mapping between a set of keys and their corresponding object.
+ *
+ * If the type is a known type, i.e. {@link RecordedThread}, an
+ * {@link ObjectFactory} can be supplied which will instantiate a typed object.
+ */
+final class ConstantMap {
+
+    private static final int RESOLUTION_FINISHED = 0;
+    private static final int RESOLUTION_STARTED = 1;
+    public static final ConstantMap EMPTY = new ConstantMap();
+
+    // A temporary placeholder, so objects can
+    // reference themselves (directly, or indirectly),
+    // when making a transition from numeric id references
+    // to normal Java references.
+    private final static class Reference {
+        private final long key;
+        private final ConstantMap pool;
+
+        Reference(ConstantMap pool, long key) {
+            this.pool = pool;
+            this.key = key;
+        }
+
+        Object resolve() {
+            return pool.get(key);
+        }
+
+        public String toString() {
+            return "ref: " + pool.name + "[" + key + "]";
+        }
+    }
+
+    final ObjectFactory<?> factory;
+    final String name;
+
+    private final LongMap<Object> objects;
+
+    private boolean resolving;
+    private boolean allResolved;
+
+    private ConstantMap() {
+        this(null, "<empty>");
+        allResolved = true;
+    }
+
+    ConstantMap(ObjectFactory<?> factory, String name) {
+        this.name = name;
+        this.objects = new LongMap<>(2);
+        this.factory = factory;
+    }
+
+    Object get(long id) {
+        // fast path, all objects in pool resolved
+        if (allResolved) {
+            return objects.get(id);
+        }
+        // referenced from a pool, deal with this later
+        if (!resolving) {
+            return new Reference(this, id);
+        }
+
+        // should always have a value
+        Object value = objects.get(id);
+        if (value == null) {
+            // unless is 0 which is used to represent null
+            if (id == 0) {
+                return null;
+            }
+            throw new InternalError("Missing object id=" + id + " in pool " + name + ". All ids should reference object");
+        }
+
+        // id is resolved (but not the whole pool)
+        if (objects.isSetId(id, RESOLUTION_FINISHED)) {
+            return value;
+        }
+
+        // resolving ourself, abort to avoid infinite recursion
+        if (objects.isSetId(id, RESOLUTION_STARTED)) {
+            return null;
+        }
+
+        // mark id as STARTED if we should
+        // come back during object resolution
+        objects.setId(id, RESOLUTION_STARTED);
+
+        // resolve object!
+        Object resolved = resolve(value);
+
+        // mark id as FINISHED
+        objects.setId(id, RESOLUTION_FINISHED);
+
+        // if a factory exists, convert to RecordedThread.
+        // RecordedClass etc. and store back results
+        if (factory != null) {
+            Object factorized = factory.createObject(id, resolved);
+            objects.put(id, factorized);
+            return factorized;
+        } else {
+            objects.put(id, resolved);
+            return resolved;
+        }
+    }
+
+    private static Object resolve(Object o) {
+        if (o instanceof Reference) {
+            return resolve(((Reference) o).resolve());
+        }
+        if (o != null && o.getClass().isArray()) {
+            final Object[] array = (Object[]) o;
+            for (int i = 0; i < array.length; i++) {
+                Object element = array[i];
+                array[i] = resolve(element);
+            }
+            return array;
+        }
+        return o;
+    }
+
+    public void resolve() {
+        objects.forEachKey(k -> get(k));
+    }
+
+    public void put(long key, Object value) {
+        objects.put(key, value);
+    }
+
+    public void setResolving() {
+        resolving = true;
+        allResolved = false;
+    }
+
+    public void setResolved() {
+        allResolved = true;
+        resolving = false;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Object getResolved(long id) {
+        return objects.get(id);
+    }
+
+    public void putResolved(long id, Object object) {
+        objects.put(id, object);
+        objects.setId(id, RESOLUTION_FINISHED);
+    }
+
+    public void setAllResolved(boolean allResolved) {
+        this.allResolved = allResolved;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/Dispatcher.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import jdk.jfr.EventType;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.LongMap;
+import jdk.jfr.internal.consumer.ChunkParser.ParserConfiguration;
+
+final class Dispatcher {
+
+    final static class EventDispatcher {
+        private final static EventDispatcher[] NO_DISPATCHERS = new EventDispatcher[0];
+
+        private final String eventName;
+        private final Consumer<RecordedEvent> action;
+
+        public EventDispatcher(String eventName, Consumer<RecordedEvent> action) {
+            this.eventName = eventName;
+            this.action = action;
+        }
+
+        private void offer(RecordedEvent event) {
+            action.accept(event);
+        }
+
+        private boolean accepts(EventType eventType) {
+            return (eventName == null || eventType.getName().equals(eventName));
+        }
+
+        public Consumer<RecordedEvent> getAction() {
+            return action;
+        }
+    }
+
+    private final Consumer<Throwable>[] errorActions;
+    private final Runnable[] flushActions;
+    private final Runnable[] closeActions;
+    private final EventDispatcher[] dispatchers;
+    private final LongMap<EventDispatcher[]> dispatcherLookup = new LongMap<>();
+    final ParserConfiguration parserConfiguration;
+    final Instant startTime;
+    final Instant endTime;
+    final long startNanos;
+    final long endNanos;
+
+    // Cache
+    private EventType cacheEventType;
+    private EventDispatcher[] cacheDispatchers;
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public Dispatcher(StreamConfiguration c) {
+        this.flushActions = c.flushActions.toArray(new Runnable[0]);
+        this.closeActions = c.closeActions.toArray(new Runnable[0]);
+        this.errorActions = c.errorActions.toArray(new Consumer[0]);
+        this.dispatchers = c.eventActions.toArray(new EventDispatcher[0]);
+        this.parserConfiguration = new ParserConfiguration(0, Long.MAX_VALUE, c.reuse, c.ordered, buildFilter(dispatchers));
+        this.startTime = c.startTime;
+        this.endTime = c.endTime;
+        this.startNanos = c.startNanos;
+        this.endNanos = c.endNanos;
+    }
+
+    public void runFlushActions() {
+        Runnable[] flushActions = this.flushActions;
+        for (int i = 0; i < flushActions.length; i++) {
+            try {
+                flushActions[i].run();
+            } catch (Exception e) {
+                handleError(e);
+            }
+        }
+    }
+
+    public void runCloseActions() {
+        Runnable[] closeActions = this.closeActions;
+        for (int i = 0; i < closeActions.length; i++) {
+            try {
+                closeActions[i].run();
+            } catch (Exception e) {
+                handleError(e);
+            }
+        }
+    }
+
+    private static ParserFilter buildFilter(EventDispatcher[] dispatchers) {
+        ParserFilter ef = new ParserFilter();
+        for (EventDispatcher ed : dispatchers) {
+            String name = ed.eventName;
+            if (name == null) {
+                return ParserFilter.ACCEPT_ALL;
+            }
+            ef.setThreshold(name, 0);
+        }
+        return ef;
+    }
+
+    void dispatch(RecordedEvent event) {
+        EventType type = event.getEventType();
+        EventDispatcher[] dispatchers = null;
+        if (type == cacheEventType) {
+            dispatchers = cacheDispatchers;
+        } else {
+            dispatchers = dispatcherLookup.get(type.getId());
+            if (dispatchers == null) {
+                List<EventDispatcher> list = new ArrayList<>();
+                for (EventDispatcher e : this.dispatchers) {
+                    if (e.accepts(type)) {
+                        list.add(e);
+                    }
+                }
+                dispatchers = list.isEmpty() ? EventDispatcher.NO_DISPATCHERS : list.toArray(new EventDispatcher[0]);
+                dispatcherLookup.put(type.getId(), dispatchers);
+            }
+            cacheDispatchers = dispatchers;
+        }
+        // Expected behavior if exception occurs in onEvent:
+        //
+        // Synchronous:
+        //  - User has added onError action:
+        //     Catch exception, call onError and continue with next event
+        //     Let Errors propagate to caller of EventStream::start
+        //  - Default action
+        //     Catch exception, e.printStackTrace() and continue with next event
+        //     Let Errors propagate to caller of EventStream::start
+        //
+        // Asynchronous
+        //  - User has added onError action
+        //     Catch exception, call onError and continue with next event
+        //     Let Errors propagate, shutdown thread and stream
+        //  - Default action
+        //    Catch exception, e.printStackTrace() and continue with next event
+        //    Let Errors propagate and shutdown thread and stream
+        //
+        for (int i = 0; i < dispatchers.length; i++) {
+            try {
+                dispatchers[i].offer(event);
+            } catch (Exception e) {
+                handleError(e);
+            }
+        }
+    }
+
+    private void handleError(Throwable e) {
+        Consumer<?>[] consumers = this.errorActions;
+        if (consumers.length == 0) {
+            defaultErrorHandler(e);
+            return;
+        }
+        for (int i = 0; i < consumers.length; i++) {
+            @SuppressWarnings("unchecked")
+            Consumer<Throwable> consumer = (Consumer<Throwable>) consumers[i];
+            consumer.accept(e);
+        }
+    }
+
+    private void defaultErrorHandler(Throwable e) {
+        e.printStackTrace();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.AccessControlContext;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
+
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.JVM;
+import jdk.jfr.internal.Utils;
+import jdk.jfr.internal.consumer.ChunkParser.ParserConfiguration;
+
+/**
+ * Implementation of an {@code EventStream}} that operates against a directory
+ * with chunk files.
+ *
+ */
+public final class EventDirectoryStream extends AbstractEventStream {
+
+    private final static Comparator<? super RecordedEvent> EVENT_COMPARATOR = JdkJfrConsumer.instance().eventComparator();
+
+    private final RepositoryFiles repositoryFiles;
+    private final boolean active;
+    private final FileAccess fileAccess;
+
+    private ChunkParser currentParser;
+    private long currentChunkStartNanos;
+    private RecordedEvent[] sortedCache;
+    private int threadExclusionLevel = 0;
+
+    public EventDirectoryStream(AccessControlContext acc, Path p, FileAccess fileAccess, boolean active) throws IOException {
+        super(acc, active);
+        this.fileAccess = Objects.requireNonNull(fileAccess);
+        this.active = active;
+        this.repositoryFiles = new RepositoryFiles(fileAccess, p);
+    }
+
+    @Override
+    public void close() {
+        setClosed(true);
+        dispatcher().runCloseActions();
+        repositoryFiles.close();
+    }
+
+    @Override
+    public void start() {
+        start(Utils.timeToNanos(Instant.now()));
+    }
+
+    @Override
+    public void startAsync() {
+        startAsync(Utils.timeToNanos(Instant.now()));
+    }
+
+    @Override
+    protected void process() throws IOException {
+        JVM jvm = JVM.getJVM();
+        Thread t = Thread.currentThread();
+        try {
+            if (jvm.isExcluded(t)) {
+                threadExclusionLevel++;
+            } else {
+                jvm.exclude(t);
+            }
+            processRecursionSafe();
+        } finally {
+            if (threadExclusionLevel > 0) {
+                threadExclusionLevel--;
+            } else {
+                jvm.include(t);
+            }
+        }
+    }
+
+    protected void processRecursionSafe() throws IOException {
+        Dispatcher disp = dispatcher();
+
+        Path path;
+        boolean validStartTime = active || disp.startTime != null;
+        if (validStartTime) {
+            path = repositoryFiles.firstPath(disp.startNanos);
+        } else {
+            path = repositoryFiles.lastPath();
+        }
+        if (path == null) { // closed
+            return;
+        }
+        currentChunkStartNanos = repositoryFiles.getTimestamp(path);
+        try (RecordingInput input = new RecordingInput(path.toFile(), fileAccess)) {
+            currentParser = new ChunkParser(input, disp.parserConfiguration);
+            long segmentStart = currentParser.getStartNanos() + currentParser.getChunkDuration();
+            long filterStart = validStartTime ? disp.startNanos : segmentStart;
+            long filterEnd = disp.endTime != null ? disp.endNanos: Long.MAX_VALUE;
+
+            while (!isClosed()) {
+                boolean awaitnewEvent = false;
+                while (!isClosed() && !currentParser.isChunkFinished()) {
+                    disp = dispatcher();
+                    ParserConfiguration pc = disp.parserConfiguration;
+                    pc.filterStart = filterStart;
+                    pc.filterEnd = filterEnd;
+                    currentParser.updateConfiguration(pc, true);
+                    currentParser.setFlushOperation(getFlushOperation());
+                    if (pc.isOrdered()) {
+                        awaitnewEvent = processOrdered(disp, awaitnewEvent);
+                    } else {
+                        awaitnewEvent = processUnordered(disp, awaitnewEvent);
+                    }
+                    if (currentParser.getStartNanos() + currentParser.getChunkDuration() > filterEnd) {
+                        close();
+                        return;
+                    }
+                }
+
+                if (isClosed()) {
+                    return;
+                }
+                long durationNanos = currentParser.getChunkDuration();
+                if (durationNanos == 0) {
+                    // Avoid reading the same chunk again and again if
+                    // duration is 0 ns
+                    durationNanos++;
+                }
+                path = repositoryFiles.nextPath(currentChunkStartNanos + durationNanos);
+                if (path == null) {
+                    return; // stream closed
+                }
+                currentChunkStartNanos = repositoryFiles.getTimestamp(path);
+                input.setFile(path);
+                currentParser = currentParser.newChunkParser();
+                // TODO: Optimization. No need filter when we reach new chunk
+                // Could set start = 0;
+            }
+        }
+    }
+
+    private boolean processOrdered(Dispatcher c, boolean awaitNewEvents) throws IOException {
+        if (sortedCache == null) {
+            sortedCache = new RecordedEvent[100_000];
+        }
+        int index = 0;
+        while (true) {
+            RecordedEvent e = currentParser.readStreamingEvent(awaitNewEvents);
+            if (e == null) {
+                // wait for new event with next call to
+                // readStreamingEvent()
+                awaitNewEvents = true;
+                break;
+            }
+            awaitNewEvents = false;
+            if (index == sortedCache.length) {
+                sortedCache = Arrays.copyOf(sortedCache, sortedCache.length * 2);
+            }
+            sortedCache[index++] = e;
+        }
+
+        // no events found
+        if (index == 0 && currentParser.isChunkFinished()) {
+            return awaitNewEvents;
+        }
+        // at least 2 events, sort them
+        if (index > 1) {
+            Arrays.sort(sortedCache, 0, index, EVENT_COMPARATOR);
+        }
+        for (int i = 0; i < index; i++) {
+            c.dispatch(sortedCache[i]);
+        }
+        return awaitNewEvents;
+    }
+
+    private boolean processUnordered(Dispatcher c, boolean awaitNewEvents) throws IOException {
+        while (true) {
+            RecordedEvent e = currentParser.readStreamingEvent(awaitNewEvents);
+            if (e == null) {
+                return true;
+            } else {
+                c.dispatch(e);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.AccessControlContext;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
+
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.consumer.Dispatcher;
+import jdk.jfr.internal.consumer.FileAccess;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Implementation of an event stream that operates against a recording file.
+ *
+ */
+public final class EventFileStream extends AbstractEventStream {
+    private final static Comparator<? super RecordedEvent> EVENT_COMPARATOR = JdkJfrConsumer.instance().eventComparator();
+
+    private final RecordingInput input;
+
+    private ChunkParser currentParser;
+    private RecordedEvent[] cacheSorted;
+
+    public EventFileStream(AccessControlContext acc, Path path) throws IOException {
+        super(acc, false);
+        Objects.requireNonNull(path);
+        this.input = new RecordingInput(path.toFile(), FileAccess.UNPRIVILIGED);
+    }
+
+    @Override
+    public void start() {
+        start(0);
+    }
+
+    @Override
+    public void startAsync() {
+        startAsync(0);
+    }
+
+    @Override
+    public void close() {
+        setClosed(true);
+        dispatcher().runCloseActions();
+        try {
+            input.close();
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    @Override
+    protected void process() throws IOException {
+        Dispatcher disp = dispatcher();
+        long start = 0;
+        long end = Long.MAX_VALUE;
+        if (disp.startTime != null) {
+            start = disp.startNanos;
+        }
+        if (disp.endTime != null) {
+            end = disp.endNanos;
+        }
+
+        currentParser = new ChunkParser(input, disp.parserConfiguration);
+        while (!isClosed()) {
+            if (currentParser.getStartNanos() > end) {
+                close();
+                return;
+            }
+            disp = dispatcher();
+            disp.parserConfiguration.filterStart = start;
+            disp.parserConfiguration.filterEnd = end;
+            currentParser.updateConfiguration(disp.parserConfiguration, true);
+            currentParser.setFlushOperation(getFlushOperation());
+            if (disp.parserConfiguration.isOrdered()) {
+                processOrdered(disp);
+            } else {
+                processUnordered(disp);
+            }
+            if (isClosed() || currentParser.isLastChunk()) {
+                return;
+            }
+            currentParser = currentParser.nextChunkParser();
+        }
+    }
+
+    private void processOrdered(Dispatcher c) throws IOException {
+        if (cacheSorted == null) {
+            cacheSorted = new RecordedEvent[10_000];
+        }
+        RecordedEvent event;
+        int index = 0;
+        while (true) {
+            event = currentParser.readEvent();
+            if (event == null) {
+                Arrays.sort(cacheSorted, 0, index, EVENT_COMPARATOR);
+                for (int i = 0; i < index; i++) {
+                    c.dispatch(cacheSorted[i]);
+                }
+                return;
+            }
+            if (index == cacheSorted.length) {
+                RecordedEvent[] tmp = cacheSorted;
+                cacheSorted = new RecordedEvent[2 * tmp.length];
+                System.arraycopy(tmp, 0, cacheSorted, 0, tmp.length);
+            }
+            cacheSorted[index++] = event;
+        }
+    }
+
+    private void processUnordered(Dispatcher c) throws IOException {
+        while (!isClosed()) {
+            RecordedEvent event = currentParser.readEvent();
+            if (event == null) {
+                return;
+            }
+            c.dispatch(event);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventParser.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2016, 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION;
+
+import java.io.IOException;
+import java.util.List;
+
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.consumer.Parser;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Parses an event and returns a {@link RecordedEvent}.
+ *
+ */
+final class EventParser extends Parser {
+
+    private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
+
+    private final Parser[] parsers;
+    private final EventType eventType;
+    private final TimeConverter timeConverter;
+    private final boolean hasDuration;
+    private final List<ValueDescriptor> valueDescriptors;
+    private final int startIndex;
+    private final int length;
+    private final RecordedEvent unorderedEvent;
+    private final ObjectContext objectContext;
+
+    private RecordedEvent[] cached;
+    private int cacheIndex;
+
+    private boolean enabled = true;
+    private boolean ordered;
+    private long filterStart;
+    private long filterEnd = Long.MAX_VALUE;
+    private long thresholdNanos = -1;
+
+    EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) {
+        this.timeConverter = timeConverter;
+        this.parsers = parsers;
+        this.eventType = type;
+        this.hasDuration = type.getField(FIELD_DURATION) != null;
+        this.startIndex = hasDuration ? 2 : 1;
+        this.length = parsers.length - startIndex;
+        this.valueDescriptors = type.getFields();
+        this.objectContext = new ObjectContext(type, valueDescriptors, timeConverter);
+        this.unorderedEvent = PRIVATE_ACCESS.newRecordedEvent(objectContext, new Object[length], 0L, 0L);
+    }
+
+    private RecordedEvent cachedEvent() {
+        if (ordered) {
+            if (cacheIndex == cached.length) {
+                RecordedEvent[] old = cached;
+                cached = new RecordedEvent[cached.length * 2];
+                System.arraycopy(old, 0, cached, 0, old.length);
+            }
+            RecordedEvent event = cached[cacheIndex];
+            if (event == null) {
+                event = PRIVATE_ACCESS.newRecordedEvent(objectContext, new Object[length], 0L, 0L);
+                cached[cacheIndex] = event;
+            }
+            cacheIndex++;
+            return event;
+        } else {
+            return unorderedEvent;
+        }
+    }
+
+    public EventType getEventType() {
+        return eventType;
+    }
+
+    public void setThresholdNanos(long thresholdNanos) {
+        this.thresholdNanos = thresholdNanos;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    public RecordedEvent parse(RecordingInput input) throws IOException {
+        if (!enabled) {
+            return null;
+        }
+
+        long startTicks = input.readLong();
+        long endTicks = startTicks;
+        if (hasDuration) {
+            long durationTicks = input.readLong();
+            if (thresholdNanos > 0L) {
+                if (timeConverter.convertTimespan(durationTicks) < thresholdNanos) {
+                    return null;
+                }
+            }
+            endTicks += durationTicks;
+        }
+        if (filterStart != 0L || filterEnd != Long.MAX_VALUE) {
+            long eventEnd = timeConverter.convertTimestamp(endTicks);
+            if (eventEnd < filterStart) {
+                return null;
+            }
+            if (eventEnd > filterEnd) {
+                return null;
+            }
+        }
+
+        if (cached != null) {
+            RecordedEvent event = cachedEvent();
+            JdkJfrConsumer access = PRIVATE_ACCESS;
+            access.setStartTicks(event, startTicks);
+            access.setEndTicks(event, endTicks);
+            Object[] values = access.eventValues(event);
+            for (int i = 0; i < values.length; i++) {
+                values[i] = parsers[startIndex + i].parse(input);
+            }
+            return event;
+        }
+
+        Object[] values = new Object[length];
+        for (int i = 0; i < values.length; i++) {
+            values[i] = parsers[startIndex + i].parse(input);
+        }
+        return PRIVATE_ACCESS.newRecordedEvent(objectContext, values, startTicks, endTicks);
+    }
+
+    @Override
+    public void skip(RecordingInput input) throws IOException {
+        throw new InternalError("Should not call this method. More efficent to read event size and skip ahead");
+    }
+
+    public void resetCache() {
+        cacheIndex = 0;
+    }
+
+    private boolean hasReuse() {
+        return cached != null;
+    }
+
+    public void setReuse(boolean reuse) {
+        if (reuse == hasReuse()) {
+            return;
+        }
+        if (reuse) {
+            cached = new RecordedEvent[2];
+            cacheIndex = 0;
+        } else {
+            cached = null;
+        }
+    }
+
+    public void setFilterStart(long filterStart) {
+        this.filterStart = filterStart;
+    }
+
+    public void setFilterEnd(long filterEnd) {
+        this.filterEnd = filterEnd;
+    }
+
+    public void setOrdered(boolean ordered) {
+        if (this.ordered == ordered) {
+            return;
+        }
+        this.ordered = ordered;
+        this.cacheIndex = 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/FileAccess.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019, 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 jdk.jfr.internal.consumer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+// Protected by modular boundaries.
+public abstract class FileAccess {
+    public final static FileAccess UNPRIVILIGED = new UnPriviliged();
+
+    public abstract RandomAccessFile openRAF(File f, String mode) throws IOException;
+
+    public abstract DirectoryStream<Path> newDirectoryStream(Path repository) throws IOException;
+
+    public abstract String getAbsolutePath(File f) throws IOException;
+
+    public abstract long length(File f) throws IOException;
+
+    public abstract long fileSize(Path p) throws IOException;
+
+    private static class UnPriviliged extends FileAccess {
+        @Override
+        public RandomAccessFile openRAF(File f, String mode) throws IOException {
+            return new RandomAccessFile(f, mode);
+        }
+
+        @Override
+        public DirectoryStream<Path> newDirectoryStream(Path dir) throws IOException {
+            return Files.newDirectoryStream(dir);
+        }
+
+        @Override
+        public String getAbsolutePath(File f) throws IOException {
+            return f.getAbsolutePath();
+        }
+
+        @Override
+        public long length(File f) throws IOException {
+            return f.length();
+        }
+
+        @Override
+        public long fileSize(Path p) throws IOException {
+            return Files.size(p);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/JdkJfrConsumer.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.List;
+
+import jdk.jfr.consumer.RecordedClass;
+import jdk.jfr.consumer.RecordedClassLoader;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordedFrame;
+import jdk.jfr.consumer.RecordedMethod;
+import jdk.jfr.consumer.RecordedObject;
+import jdk.jfr.consumer.RecordedStackTrace;
+import jdk.jfr.consumer.RecordedThread;
+import jdk.jfr.consumer.RecordedThreadGroup;
+import jdk.jfr.consumer.RecordingFile;
+import jdk.jfr.internal.Type;
+/*
+ * Purpose of this class is to give package private access to
+ * the jdk.jfr.consumer package
+ */
+public abstract class JdkJfrConsumer {
+
+    private static JdkJfrConsumer instance;
+
+    // Initialization will trigger setAccess being called
+    private static void forceInitializetion() {
+        try {
+            Class<?> c = RecordedObject.class;
+            Class.forName(c.getName(), true, c.getClassLoader());
+        } catch (ClassNotFoundException e) {
+            throw new InternalError("Should not happen");
+        }
+    }
+
+    public static void setAccess(JdkJfrConsumer access) {
+        instance = access;
+    }
+
+    public static JdkJfrConsumer instance() {
+        if (instance == null) {
+            forceInitializetion();
+        }
+        return instance;
+    }
+
+    public abstract List<Type> readTypes(RecordingFile file) throws IOException;
+
+    public abstract boolean isLastEventInChunk(RecordingFile file);
+
+    public abstract Object getOffsetDataTime(RecordedObject event, String name);
+
+    public abstract RecordedClass newRecordedClass(ObjectContext objectContext, long id, Object[] values);
+
+    public abstract RecordedClassLoader newRecordedClassLoader(ObjectContext objectContext, long id, Object[] values);
+
+    public abstract RecordedStackTrace newRecordedStackTrace(ObjectContext objectContext, Object[] values);
+
+    public abstract RecordedThreadGroup newRecordedThreadGroup(ObjectContext objectContext, Object[] values);
+
+    public abstract RecordedFrame newRecordedFrame(ObjectContext objectContext, Object[] values);
+
+    public abstract RecordedThread newRecordedThread(ObjectContext objectContext, long id, Object[] values);
+
+    public abstract RecordedMethod newRecordedMethod(ObjectContext objectContext, Object[] values);
+
+    public abstract RecordedEvent newRecordedEvent(ObjectContext objectContext, Object[] objects, long l, long m);
+
+    public abstract Comparator<? super RecordedEvent> eventComparator();
+
+    public abstract void setStartTicks(RecordedEvent event, long startTicks);
+
+    public abstract void setEndTicks(RecordedEvent event, long endTicks);
+
+    public abstract Object[] eventValues(RecordedEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ObjectContext.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.time.ZoneId;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+
+public final class ObjectContext {
+    private final Map<ValueDescriptor, ObjectContext> contextLookup;
+    private final TimeConverter timeConverter;
+
+    public final EventType eventType;
+    public final List<ValueDescriptor> fields;
+
+    ObjectContext(EventType eventType, List<ValueDescriptor> fields, TimeConverter timeConverter) {
+        this.contextLookup = new HashMap<>();
+        this.eventType = eventType;
+        this.fields = fields;
+        this.timeConverter = timeConverter;
+    }
+
+    private ObjectContext(ObjectContext parent, ValueDescriptor descriptor) {
+        this.eventType = parent.eventType;
+        this.contextLookup = parent.contextLookup;
+        this.timeConverter = parent.timeConverter;
+        this.fields = descriptor.getFields();
+    }
+
+    public ObjectContext getInstance(ValueDescriptor descriptor) {
+        ObjectContext context = contextLookup.get(descriptor);
+        if (context == null) {
+            context = new ObjectContext(this, descriptor);
+            contextLookup.put(descriptor, context);
+        }
+        return context;
+    }
+
+    public long convertTimestamp(long ticks) {
+        return timeConverter.convertTimestamp(ticks);
+    }
+
+    public long convertTimespan(long ticks) {
+        return timeConverter.convertTimespan(ticks);
+    }
+
+    public ZoneId getZoneOffset() {
+        return timeConverter.getZoneOffset();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ObjectFactory.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2016, 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import jdk.jfr.consumer.RecordedClass;
+import jdk.jfr.consumer.RecordedClassLoader;
+import jdk.jfr.consumer.RecordedFrame;
+import jdk.jfr.consumer.RecordedMethod;
+import jdk.jfr.consumer.RecordedStackTrace;
+import jdk.jfr.consumer.RecordedThread;
+import jdk.jfr.consumer.RecordedThreadGroup;
+import jdk.jfr.internal.Type;
+
+/**
+ * Abstract factory for creating specialized types
+ */
+public abstract class ObjectFactory<T> {
+    private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
+
+    private final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types.";
+    private final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX;
+    public final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame";
+    public final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame";
+
+    static ObjectFactory<?> create(Type type, TimeConverter timeConverter) {
+        switch (type.getName()) {
+        case "java.lang.Thread":
+            return createThreadFactory(type, timeConverter);
+        case TYPE_PREFIX_VERSION_1 + "StackFrame":
+        case TYPE_PREFIX_VERSION_2 + "StackFrame":
+            return createFrameFactory(type, timeConverter);
+        case TYPE_PREFIX_VERSION_1 + "Method":
+        case TYPE_PREFIX_VERSION_2 + "Method":
+            return createMethodFactory(type, timeConverter);
+        case TYPE_PREFIX_VERSION_1 + "ThreadGroup":
+        case TYPE_PREFIX_VERSION_2 + "ThreadGroup":
+            return createdThreadGroupFactory(type, timeConverter);
+        case TYPE_PREFIX_VERSION_1 + "StackTrace":
+        case TYPE_PREFIX_VERSION_2 + "StackTrace":
+            return createStackTraceFactory(type, timeConverter);
+        case TYPE_PREFIX_VERSION_1 + "ClassLoader":
+        case TYPE_PREFIX_VERSION_2 + "ClassLoader":
+            return createClassLoaderFactory(type, timeConverter);
+        case "java.lang.Class":
+            return createClassFactory(type, timeConverter);
+        }
+        return null;
+    }
+
+    private static ObjectFactory<RecordedClass> createClassFactory(Type type, TimeConverter timeConverter) {
+        return new ObjectFactory<RecordedClass>(type, timeConverter) {
+            @Override
+            RecordedClass createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return PRIVATE_ACCESS.newRecordedClass(objectContext, id, values);
+            }
+        };
+    }
+
+    private static ObjectFactory<?> createClassLoaderFactory(Type type, TimeConverter timeConverter) {
+        return new ObjectFactory<RecordedClassLoader>(type, timeConverter) {
+            @Override
+            RecordedClassLoader createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return PRIVATE_ACCESS.newRecordedClassLoader(objectContext, id, values);
+            }
+        };
+    }
+
+    private static ObjectFactory<RecordedStackTrace> createStackTraceFactory(Type type, TimeConverter timeConverter) {
+        return new ObjectFactory<RecordedStackTrace>(type, timeConverter) {
+            @Override
+            RecordedStackTrace createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return PRIVATE_ACCESS.newRecordedStackTrace(objectContext, values);
+            }
+        };
+    }
+
+    private static ObjectFactory<RecordedThreadGroup> createdThreadGroupFactory(Type type, TimeConverter timeConverter) {
+        return new ObjectFactory<RecordedThreadGroup>(type, timeConverter) {
+            @Override
+            RecordedThreadGroup createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return PRIVATE_ACCESS.newRecordedThreadGroup(objectContext, values);
+            }
+        };
+    }
+
+    private static ObjectFactory<RecordedMethod> createMethodFactory(Type type, TimeConverter timeConverter) {
+        return new ObjectFactory<RecordedMethod>(type, timeConverter) {
+            @Override
+            RecordedMethod createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return PRIVATE_ACCESS.newRecordedMethod(objectContext, values);
+            }
+        };
+    }
+
+    private static ObjectFactory<RecordedFrame> createFrameFactory(Type type, TimeConverter timeConverter) {
+        return new ObjectFactory<RecordedFrame>(type, timeConverter) {
+            @Override
+            RecordedFrame createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return PRIVATE_ACCESS.newRecordedFrame(objectContext, values);
+            }
+        };
+    }
+
+    private static ObjectFactory<RecordedThread> createThreadFactory(Type type, TimeConverter timeConverter) {
+        return new ObjectFactory<RecordedThread>(type, timeConverter) {
+            @Override
+            RecordedThread createTyped(ObjectContext objectContext, long id, Object[] values) {
+                return PRIVATE_ACCESS.newRecordedThread(objectContext, id, values);
+            }
+        };
+    }
+
+    private final ObjectContext objectContext;
+
+    private ObjectFactory(Type type, TimeConverter timeConverter) {
+        this.objectContext = new ObjectContext(null, type.getFields(), timeConverter);
+    }
+
+    T createObject(long id, Object value) {
+        if (value == null) {
+            return null;
+        }
+        if (value instanceof Object[]) {
+            return createTyped(objectContext, id, (Object[]) value);
+        }
+        throw new InternalError("Object factory must have struct type. Type was " + value.getClass().getName());
+    }
+
+    abstract T createTyped(ObjectContext objectContextm, long id, Object[] values);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/Parser.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016, 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+
+/**
+ * Base class for parsing data from a {@link RecordingInput}.
+ */
+abstract class Parser {
+    /**
+     * Parses data from a {@link RecordingInput} and return an object.
+     *
+     * @param input input to read from
+     * @return an object
+     * @throws IOException if operation couldn't be completed due to I/O
+     *         problems
+     */
+    public abstract Object parse(RecordingInput input) throws IOException;
+
+    /**
+     * Skips data that would usually be by parsed the {@code #parse(RecordingInput)} method.
+     *
+     * @param input input to read from
+     * @throws IOException if operation couldn't be completed due to I/O
+     *         problems
+     */
+    public abstract void skip(RecordingInput input) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2016, 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import jdk.jfr.EventType;
+import jdk.jfr.ValueDescriptor;
+import jdk.jfr.internal.LongMap;
+import jdk.jfr.internal.MetadataDescriptor;
+import jdk.jfr.internal.PrivateAccess;
+import jdk.jfr.internal.Type;
+import jdk.jfr.internal.consumer.Parser;
+import jdk.jfr.internal.consumer.RecordingInput;
+
+/**
+ * Class that create parsers suitable for reading events and constant pools
+ */
+final class ParserFactory {
+    private final LongMap<Parser> parsers = new LongMap<>();
+    private final TimeConverter timeConverter;
+    private final LongMap<Type> types = new LongMap<>();
+    private final LongMap<ConstantLookup> constantLookups;
+
+    public ParserFactory(MetadataDescriptor metadata, LongMap<ConstantLookup> constantLookups, TimeConverter timeConverter) throws IOException {
+        this.constantLookups = constantLookups;
+        this.timeConverter = timeConverter;
+        for (Type t : metadata.getTypes()) {
+            types.put(t.getId(), t);
+        }
+        // Add to separate list
+        // so createCompositeParser can throw
+        // IOException outside lambda
+        List<Type> typeList = new ArrayList<>();
+        types.forEach(typeList::add);
+        for (Type t : typeList) {
+            if (!t.getFields().isEmpty()) { // Avoid primitives
+                CompositeParser cp = createCompositeParser(t, false);
+                if (t.isSimpleType()) { // Reduce to nested parser
+                    parsers.put(t.getId(), cp.parsers[0]);
+                }
+            }
+        }
+        // Override event types with event parsers
+        for (EventType t : metadata.getEventTypes()) {
+            parsers.put(t.getId(), createEventParser(t));
+        }
+    }
+
+    public LongMap<Parser> getParsers() {
+        return parsers;
+    }
+
+    public LongMap<Type> getTypeMap() {
+        return types;
+    }
+
+    private EventParser createEventParser(EventType eventType) throws IOException {
+        List<Parser> parsers = new ArrayList<Parser>();
+        for (ValueDescriptor f : eventType.getFields()) {
+            parsers.add(createParser(f, true));
+        }
+        return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0]));
+    }
+
+    private Parser createParser(ValueDescriptor v, boolean event) throws IOException {
+        boolean constantPool = PrivateAccess.getInstance().isConstantPool(v);
+        if (v.isArray()) {
+            Type valueType = PrivateAccess.getInstance().getType(v);
+            ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null);
+            return new ArrayParser(createParser(element, event));
+        }
+        long id = v.getTypeId();
+        Type type = types.get(id);
+        if (type == null) {
+            throw new IOException("Type '" + v.getTypeName() + "' is not defined");
+        }
+        if (constantPool) {
+            ConstantLookup lookup = constantLookups.get(id);
+            if (lookup == null) {
+                ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+                lookup = new ConstantLookup(pool, type);
+                constantLookups.put(id, lookup);
+            }
+            if (event) {
+                return new EventValueConstantParser(lookup);
+            }
+            return new ConstantValueParser(lookup);
+        }
+        Parser parser = parsers.get(id);
+        if (parser == null) {
+            if (!v.getFields().isEmpty()) {
+                return createCompositeParser(type, event);
+            } else {
+                return registerParserType(type, createPrimitiveParser(type, constantPool));
+            }
+        }
+        return parser;
+    }
+
+    private Parser createPrimitiveParser(Type type, boolean event) throws IOException {
+        switch (type.getName()) {
+        case "int":
+            return new IntegerParser();
+        case "long":
+            return new LongParser();
+        case "float":
+            return new FloatParser();
+        case "double":
+            return new DoubleParser();
+        case "char":
+            return new CharacterParser();
+        case "boolean":
+            return new BooleanParser();
+        case "short":
+            return new ShortParser();
+        case "byte":
+            return new ByteParser();
+        case "java.lang.String":
+            ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName());
+            ConstantLookup lookup = new ConstantLookup(pool, type);
+            constantLookups.put(type.getId(), lookup);
+            return new StringParser(lookup, event);
+        default:
+            throw new IOException("Unknown primitive type " + type.getName());
+        }
+    }
+
+    private Parser registerParserType(Type t, Parser parser) {
+        Parser p = parsers.get(t.getId());
+        // check if parser exists (known type)
+        if (p != null) {
+            return p;
+        }
+        parsers.put(t.getId(), parser);
+        return parser;
+    }
+
+    private CompositeParser createCompositeParser(Type type, boolean event) throws IOException {
+        List<ValueDescriptor> vds = type.getFields();
+        Parser[] parsers = new Parser[vds.size()];
+        CompositeParser composite = new CompositeParser(parsers);
+        // need to pre-register so recursive types can be handled
+        registerParserType(type, composite);
+
+        int index = 0;
+        for (ValueDescriptor vd : vds) {
+            parsers[index++] = createParser(vd, event);
+        }
+        return composite;
+    }
+
+    private static final class BooleanParser extends Parser {
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE;
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.skipBytes(1);
+        }
+    }
+
+    private static final class ByteParser extends Parser {
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            return Byte.valueOf(input.readByte());
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.skipBytes(1);
+        }
+    }
+
+    private static final class LongParser extends Parser {
+        private Object lastLongObject = Long.valueOf(0);
+        private long last = 0;
+
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            long l = input.readLong();
+            if (l == last) {
+                return lastLongObject;
+            }
+            last = l;
+            lastLongObject = Long.valueOf(l);
+            return lastLongObject;
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.readLong();
+        }
+    }
+
+    private static final class IntegerParser extends Parser {
+        private Integer lastIntegergObject = Integer.valueOf(0);
+        private int last = 0;
+
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            int i = input.readInt();
+            if (i != last) {
+                last = i;
+                lastIntegergObject = Integer.valueOf(i);
+            }
+            return lastIntegergObject;
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.readInt();
+        }
+    }
+
+    private static final class ShortParser extends Parser {
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            return Short.valueOf(input.readShort());
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.readShort();
+        }
+    }
+
+    private static final class CharacterParser extends Parser {
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            return Character.valueOf(input.readChar());
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.readChar();
+        }
+    }
+
+    private static final class FloatParser extends Parser {
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            return Float.valueOf(input.readFloat());
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.skipBytes(Float.SIZE);
+        }
+    }
+
+    private static final class DoubleParser extends Parser {
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            return Double.valueOf(input.readDouble());
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.skipBytes(Double.SIZE);
+        }
+    }
+
+    private final static class ArrayParser extends Parser {
+        private final Parser elementParser;
+
+        public ArrayParser(Parser elementParser) {
+            this.elementParser = elementParser;
+        }
+
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            final int size = input.readInt();
+            final Object[] array = new Object[size];
+            for (int i = 0; i < size; i++) {
+                array[i] = elementParser.parse(input);
+            }
+            return array;
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            final int size = input.readInt();
+            for (int i = 0; i < size; i++) {
+                elementParser.skip(input);
+            }
+        }
+    }
+
+    private final static class CompositeParser extends Parser {
+        private final Parser[] parsers;
+
+        public CompositeParser(Parser[] valueParsers) {
+            this.parsers = valueParsers;
+        }
+
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            final Object[] values = new Object[parsers.length];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = parsers[i].parse(input);
+            }
+            return values;
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            for (int i = 0; i < parsers.length; i++) {
+                parsers[i].skip(input);
+            }
+        }
+    }
+
+    private static final class EventValueConstantParser extends Parser {
+        private final ConstantLookup lookup;
+        private Object lastValue = 0;
+        private long lastKey = -1;
+        EventValueConstantParser(ConstantLookup lookup) {
+            this.lookup = lookup;
+        }
+
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            long key = input.readLong();
+            if (key == lastKey) {
+                return lastValue;
+            }
+            lastKey = key;
+            lastValue = lookup.getCurrentResolved(key);
+            return lastValue;
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.readLong();
+        }
+    }
+
+    private static final class ConstantValueParser extends Parser {
+        private final ConstantLookup lookup;
+        ConstantValueParser(ConstantLookup lookup) {
+            this.lookup = lookup;
+        }
+
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            return lookup.getCurrent(input.readLong());
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            input.readLong();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFilter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
+
+final class ParserFilter {
+    public static final ParserFilter ACCEPT_ALL = new ParserFilter(true, Map.of());
+
+    private final Map<String, Long> thresholds;
+    private final boolean acceptAll;
+
+    public ParserFilter() {
+        this(false, new HashMap<>());
+    }
+
+    private ParserFilter(boolean acceptAll, Map<String, Long> thresholds) {
+        this.acceptAll = acceptAll;
+        this.thresholds = thresholds;
+    }
+
+    public void setThreshold(String eventName, long nanos) {
+        Long l = thresholds.get(eventName);
+        if (l != null) {
+            l = Math.min(l, nanos);
+        } else {
+            l = nanos;
+        }
+        thresholds.put(eventName, l);
+    }
+
+    public long getThreshold(String eventName) {
+        if (acceptAll) {
+            return 0L;
+        }
+        Long l = thresholds.get(eventName);
+        if (l != null) {
+            return l;
+        }
+        return -1;
+    }
+
+    public String toString() {
+        if (acceptAll) {
+            return "ACCEPT ALL";
+        }
+
+        StringJoiner sb = new StringJoiner(", ");
+        for (String key : thresholds.keySet().toArray(new String[0])) {
+            Long value = thresholds.get(key);
+            sb.add(key + " = " + value);
+        }
+        return sb.toString();
+    }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -30,61 +30,82 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
-import java.nio.charset.Charset;
+import java.nio.file.Path;
 
 public final class RecordingInput implements DataInput, AutoCloseable {
 
-    public static final byte STRING_ENCODING_NULL = 0;
-    public static final byte STRING_ENCODING_EMPTY_STRING = 1;
-    public static final byte STRING_ENCODING_CONSTANT_POOL = 2;
-    public static final byte STRING_ENCODING_UTF8_BYTE_ARRAY = 3;
-    public static final byte STRING_ENCODING_CHAR_ARRAY = 4;
-    public static final byte STRING_ENCODING_LATIN1_BYTE_ARRAY = 5;
-
-    private final static int DEFAULT_BLOCK_SIZE = 16 * 1024 * 1024;
-    private final static Charset UTF8 = Charset.forName("UTF-8");
-    private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
+    private final static int DEFAULT_BLOCK_SIZE = 64_000;
 
     private static final class Block {
         private byte[] bytes = new byte[0];
         private long blockPosition;
+        private long blockPositionEnd;
 
         boolean contains(long position) {
-            return position >= blockPosition && position < blockPosition + bytes.length;
+            return position >= blockPosition && position < blockPositionEnd;
         }
 
         public void read(RandomAccessFile file, int amount) throws IOException {
             blockPosition = file.getFilePointer();
             // reuse byte array, if possible
-            if (amount != bytes.length) {
+            if (amount > bytes.length) {
                 bytes = new byte[amount];
             }
-            file.readFully(bytes);
+            this.blockPositionEnd = blockPosition + amount;
+            file.readFully(bytes, 0, amount);
         }
 
         public byte get(long position) {
             return bytes[(int) (position - blockPosition)];
         }
+
+        public void reset() {
+            blockPosition = 0;
+            blockPositionEnd = 0;
+        }
     }
-
-    private final RandomAccessFile file;
-    private final long size;
+    private final int blockSize;
+    private final FileAccess fileAccess;
+    private RandomAccessFile file;
+    private String filename;
     private Block currentBlock = new Block();
     private Block previousBlock = new Block();
     private long position;
-    private final int blockSize;
+    private long size = -1; // Fail fast if setSize(...) has not been called
+                            // before parsing
+
+    RecordingInput(File f, FileAccess fileAccess, int blockSize) throws IOException {
+        this.blockSize = blockSize;
+        this.fileAccess = fileAccess;
+        initialize(f);
+    }
 
-    private RecordingInput(File f, int blockSize) throws IOException {
-        this.size = f.length();
-        this.blockSize = blockSize;
-        this.file = new RandomAccessFile(f, "r");
-        if (size < 8) {
-            throw new IOException("Not a valid Flight Recorder file. File length is only " + size + " bytes.");
+    private void initialize(File f) throws IOException {
+        this.filename = fileAccess.getAbsolutePath(f);
+        this.file = fileAccess.openRAF(f, "r");
+        this.position = 0;
+        this.size = -1;
+        this.currentBlock.reset();
+        previousBlock.reset();
+        if (fileAccess.length(f) < 8) {
+            throw new IOException("Not a valid Flight Recorder file. File length is only " + fileAccess.length(f) + " bytes.");
         }
     }
 
-    public RecordingInput(File f) throws IOException {
-        this(f, DEFAULT_BLOCK_SIZE);
+    public RecordingInput(File f, FileAccess fileAccess) throws IOException {
+        this(f, fileAccess, DEFAULT_BLOCK_SIZE);
+    }
+
+    void positionPhysical(long position) throws IOException {
+        file.seek(position);
+    }
+
+    byte readPhysicalByte() throws IOException {
+        return file.readByte();
+    }
+
+    long readPhysicalLong() throws IOException {
+        return file.readLong();
     }
 
     @Override
@@ -109,7 +130,7 @@
         readFully(dst, 0, dst.length);
     }
 
-    public final short readRawShort() throws IOException {
+    short readRawShort() throws IOException {
         // copied from java.io.Bits
         byte b0 = readByte();
         byte b1 = readByte();
@@ -117,18 +138,18 @@
     }
 
     @Override
-    public final double readDouble() throws IOException {
+    public double readDouble() throws IOException {
         // copied from java.io.Bits
         return Double.longBitsToDouble(readRawLong());
     }
 
     @Override
-    public final float readFloat() throws IOException {
+    public float readFloat() throws IOException {
         // copied from java.io.Bits
         return Float.intBitsToFloat(readRawInt());
     }
 
-    public final int readRawInt() throws IOException {
+    int readRawInt() throws IOException {
         // copied from java.io.Bits
         byte b0 = readByte();
         byte b1 = readByte();
@@ -137,7 +158,7 @@
         return ((b3 & 0xFF)) + ((b2 & 0xFF) << 8) + ((b1 & 0xFF) << 16) + ((b0) << 24);
     }
 
-    public final long readRawLong() throws IOException {
+    long readRawLong() throws IOException {
         // copied from java.io.Bits
         byte b0 = readByte();
         byte b1 = readByte();
@@ -150,20 +171,20 @@
         return ((b7 & 0xFFL)) + ((b6 & 0xFFL) << 8) + ((b5 & 0xFFL) << 16) + ((b4 & 0xFFL) << 24) + ((b3 & 0xFFL) << 32) + ((b2 & 0xFFL) << 40) + ((b1 & 0xFFL) << 48) + (((long) b0) << 56);
     }
 
-    public final long position() throws IOException {
+    public final long position() {
         return position;
     }
 
     public final void position(long newPosition) throws IOException {
         if (!currentBlock.contains(newPosition)) {
             if (!previousBlock.contains(newPosition)) {
-                if (newPosition > size()) {
-                    throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size() + " bytes.");
+                if (newPosition > size) {
+                    throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size + " bytes.");
                 }
                 long blockStart = trimToFileSize(calculateBlockStart(newPosition));
                 file.seek(blockStart);
                 // trim amount to file size
-                long amount = Math.min(size() - blockStart, blockSize);
+                long amount = Math.min(size - blockStart, blockSize);
                 previousBlock.read(file, (int) amount);
             }
             // swap previous and current
@@ -191,11 +212,12 @@
         return newPosition - blockSize / 2;
     }
 
-    public final long size() throws IOException {
+    long size() {
         return size;
     }
 
-    public final void close() throws IOException {
+    @Override
+    public void close() throws IOException {
         file.close();
     }
 
@@ -245,34 +267,7 @@
     // 4, means ""
     @Override
     public String readUTF() throws IOException {
-        return readEncodedString(readByte());
-    }
-
-    public String readEncodedString(byte encoding) throws IOException {
-        if (encoding == STRING_ENCODING_NULL) {
-            return null;
-        }
-        if (encoding == STRING_ENCODING_EMPTY_STRING) {
-            return "";
-        }
-        int size = readInt();
-        if (encoding == STRING_ENCODING_CHAR_ARRAY) {
-            char[] c = new char[size];
-            for (int i = 0; i < size; i++) {
-                c[i] = readChar();
-            }
-            return new String(c);
-        }
-        byte[] bytes = new byte[size];
-        readFully(bytes); // TODO: optimize, check size, and copy only if needed
-        if (encoding == STRING_ENCODING_UTF8_BYTE_ARRAY) {
-            return new String(bytes, UTF8);
-        }
-
-        if (encoding == STRING_ENCODING_LATIN1_BYTE_ARRAY) {
-            return new String(bytes, LATIN1);
-        }
-        throw new IOException("Unknown string encoding " + encoding);
+        throw new UnsupportedOperationException("Use StringParser");
     }
 
     @Override
@@ -292,48 +287,152 @@
 
     @Override
     public long readLong() throws IOException {
-        // can be optimized by branching checks, but will do for now
+        final byte[] bytes = currentBlock.bytes;
+        final int index = (int) (position - currentBlock.blockPosition);
+
+        if (index + 8 < bytes.length && index >= 0) {
+            byte b0 = bytes[index];
+            long ret = (b0 & 0x7FL);
+            if (b0 >= 0) {
+                position += 1;
+                return ret;
+            }
+            int b1 = bytes[index + 1];
+            ret += (b1 & 0x7FL) << 7;
+            if (b1 >= 0) {
+                position += 2;
+                return ret;
+            }
+            int b2 = bytes[index + 2];
+            ret += (b2 & 0x7FL) << 14;
+            if (b2 >= 0) {
+                position += 3;
+                return ret;
+            }
+            int b3 = bytes[index + 3];
+            ret += (b3 & 0x7FL) << 21;
+            if (b3 >= 0) {
+                position += 4;
+                return ret;
+            }
+            int b4 = bytes[index + 4];
+            ret += (b4 & 0x7FL) << 28;
+            if (b4 >= 0) {
+                position += 5;
+                return ret;
+            }
+            int b5 = bytes[index + 5];
+            ret += (b5 & 0x7FL) << 35;
+            if (b5 >= 0) {
+                position += 6;
+                return ret;
+            }
+            int b6 = bytes[index + 6];
+            ret += (b6 & 0x7FL) << 42;
+            if (b6 >= 0) {
+                position += 7;
+                return ret;
+            }
+            int b7 = bytes[index + 7];
+            ret += (b7 & 0x7FL) << 49;
+            if (b7 >= 0) {
+                position += 8;
+                return ret;
+            }
+            int b8 = bytes[index + 8];// read last byte raw
+            position += 9;
+            return ret + (((long) (b8 & 0XFF)) << 56);
+        } else {
+            return readLongSlow();
+        }
+    }
+
+    private long readLongSlow() throws IOException {
         byte b0 = readByte();
         long ret = (b0 & 0x7FL);
         if (b0 >= 0) {
             return ret;
         }
+
         int b1 = readByte();
         ret += (b1 & 0x7FL) << 7;
         if (b1 >= 0) {
             return ret;
         }
+
         int b2 = readByte();
         ret += (b2 & 0x7FL) << 14;
         if (b2 >= 0) {
             return ret;
         }
+
         int b3 = readByte();
         ret += (b3 & 0x7FL) << 21;
         if (b3 >= 0) {
             return ret;
         }
+
         int b4 = readByte();
         ret += (b4 & 0x7FL) << 28;
         if (b4 >= 0) {
             return ret;
         }
+
         int b5 = readByte();
         ret += (b5 & 0x7FL) << 35;
         if (b5 >= 0) {
             return ret;
         }
+
         int b6 = readByte();
         ret += (b6 & 0x7FL) << 42;
         if (b6 >= 0) {
             return ret;
         }
+
         int b7 = readByte();
         ret += (b7 & 0x7FL) << 49;
         if (b7 >= 0) {
             return ret;
+
         }
+
         int b8 = readByte(); // read last byte raw
         return ret + (((long) (b8 & 0XFF)) << 56);
     }
+
+    public void setValidSize(long size) {
+        if (size > this.size) {
+            this.size = size;
+        }
+    }
+
+    public long getFileSize() throws IOException {
+        return file.length();
+    }
+
+    public String getFilename() {
+        return filename;
+    }
+
+    // Purpose of this method is to reuse block cache from a
+    // previous RecordingInput
+    public void setFile(Path path) throws IOException {
+        try {
+            file.close();
+        } catch (IOException e) {
+            // perhaps deleted
+        }
+        file = null;
+        initialize(path.toFile());
+    }
+/*
+
+
+
+
+
+ *
+ *
+ */
 }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInternals.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2018, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.jfr.internal.consumer;
-
-import java.io.IOException;
-import java.util.List;
-
-import jdk.jfr.consumer.RecordedEvent;
-import jdk.jfr.consumer.RecordedObject;
-import jdk.jfr.consumer.RecordingFile;
-import jdk.jfr.internal.Type;
-
-public abstract class RecordingInternals {
-
-    public static RecordingInternals INSTANCE;
-
-    public abstract boolean isLastEventInChunk(RecordingFile file);
-
-    public abstract Object getOffsetDataTime(RecordedObject event, String name);
-
-    public abstract List<Type> readTypes(RecordingFile file) throws IOException;
-
-    public abstract void sort(List<RecordedEvent> events);
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.Repository;
+import jdk.jfr.internal.SecuritySupport.SafePath;
+
+public final class RepositoryFiles {
+    private static final Object WAIT_OBJECT = new Object();
+    public static void notifyNewFile() {
+        synchronized (WAIT_OBJECT) {
+            WAIT_OBJECT.notifyAll();
+        }
+    }
+
+    private final FileAccess fileAccess;
+    private final NavigableMap<Long, Path> pathSet = new TreeMap<>();
+    private final Map<Path, Long> pathLookup = new HashMap<>();
+    private final Path repository;
+    private final Object waitObject;
+
+    private volatile boolean closed;
+
+    RepositoryFiles(FileAccess fileAccess, Path repository) {
+        this.repository = repository;
+        this.fileAccess = fileAccess;
+        this.waitObject = repository == null ? WAIT_OBJECT : new Object();
+    }
+
+    long getTimestamp(Path p) {
+        return pathLookup.get(p);
+    }
+
+    Path lastPath() {
+        if (waitForPaths()) {
+            return pathSet.lastEntry().getValue();
+        }
+        return null; // closed
+    }
+
+    Path firstPath(long startTimeNanos) {
+        if (waitForPaths()) {
+            // Pick closest chunk before timestamp
+            Long time = pathSet.floorKey(startTimeNanos);
+            if (time != null) {
+                startTimeNanos = time;
+            }
+            return path(startTimeNanos);
+        }
+        return null; // closed
+    }
+
+    private boolean waitForPaths() {
+        while (!closed) {
+            try {
+                if (updatePaths()) {
+                    break;
+                }
+            } catch (IOException e) {
+                Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "IOException during repository file scan " + e.getMessage());
+                // This can happen if a chunk is being removed
+                // between the file was discovered and an instance
+                // was accessed, or if new file has been written yet
+                // Just ignore, and retry later.
+            }
+            nap();
+        }
+        return !closed;
+    }
+
+    Path nextPath(long startTimeNanos) {
+        if (closed) {
+            return null;
+        }
+        // Try to get the 'exact' path first
+        // to avoid skipping files if repository
+        // is updated while DirectoryStream
+        // is traversing it
+        Path path = pathSet.get(startTimeNanos);
+        if (path != null) {
+            return path;
+        }
+        // Update paths
+        try {
+            updatePaths();
+        } catch (IOException e) {
+            // ignore
+        }
+        // try to get the next file
+        return path(startTimeNanos);
+    }
+
+    private Path path(long timestamp) {
+        if (closed) {
+            return null;
+        }
+        while (true) {
+            SortedMap<Long, Path> after = pathSet.tailMap(timestamp);
+            if (!after.isEmpty()) {
+                Path path = after.get(after.firstKey());
+                if (Logger.shouldLog(LogTag.JFR_SYSTEM_STREAMING, LogLevel.TRACE)) {
+                    Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.TRACE, "Return path " + path + " for start time nanos " + timestamp);
+                }
+                return path;
+            }
+            if (!waitForPaths()) {
+                return null; // closed
+            }
+        }
+    }
+
+    private void nap() {
+        try {
+            synchronized (waitObject) {
+                waitObject.wait(1000);
+            }
+        } catch (InterruptedException e) {
+            // ignore
+        }
+    }
+
+    private boolean updatePaths() throws IOException {
+        boolean foundNew = false;
+        Path repoPath = repository;
+        if (repoPath == null) {
+            // Always get the latest repository if 'jcmd JFR.configure
+            // repositorypath=...' has been executed
+            SafePath sf = Repository.getRepository().getRepositoryPath();
+            if (sf == null) {
+                return false; // not initialized
+            }
+            repoPath = sf.toPath();
+        }
+
+        try (DirectoryStream<Path> dirStream = fileAccess.newDirectoryStream(repoPath)) {
+            List<Path> added = new ArrayList<>();
+            Set<Path> current = new HashSet<>();
+            for (Path p : dirStream) {
+                if (!pathLookup.containsKey(p)) {
+                    String s = p.toString();
+                    if (s.endsWith(".jfr")) {
+                        added.add(p);
+                        Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "New file found: " + p.toAbsolutePath());
+                    }
+                    current.add(p);
+                }
+            }
+            List<Path> removed = new ArrayList<>();
+            for (Path p : pathLookup.keySet()) {
+                if (!current.contains(p)) {
+                    removed.add(p);
+                }
+            }
+
+            for (Path remove : removed) {
+                Long time = pathLookup.get(remove);
+                pathSet.remove(time);
+                pathLookup.remove(remove);
+            }
+            Collections.sort(added, (p1, p2) -> p1.compareTo(p2));
+            for (Path p : added) {
+                // Only add files that have a complete header
+                // as the JVM may be in progress writing the file
+                long size = fileAccess.fileSize(p);
+                if (size >= ChunkHeader.headerSize()) {
+                    long startNanos = readStartTime(p);
+                    pathSet.put(startNanos, p);
+                    pathLookup.put(p, startNanos);
+                    foundNew = true;
+                }
+            }
+            return foundNew;
+        }
+    }
+
+    private long readStartTime(Path p) throws IOException {
+        try (RecordingInput in = new RecordingInput(p.toFile(), fileAccess, 100)) {
+            Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Parsing header for chunk start time");
+            ChunkHeader c = new ChunkHeader(in);
+            return c.getStartNanos();
+        }
+    }
+
+    void close() {
+        synchronized (waitObject) {
+            this.closed = true;
+            waitObject.notify();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StreamConfiguration.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.Utils;
+import jdk.jfr.internal.consumer.Dispatcher.EventDispatcher;
+
+final class StreamConfiguration {
+    final List<Runnable> closeActions = new ArrayList<>();
+    final List<Runnable> flushActions = new ArrayList<>();
+    final List<EventDispatcher> eventActions = new ArrayList<>();
+    final List<Consumer<Throwable>> errorActions = new ArrayList<>();
+
+    boolean reuse = true;
+    boolean ordered = true;
+    Instant startTime = null;
+    Instant endTime = null;
+    boolean started = false;
+    long startNanos = 0;
+    long endNanos = Long.MAX_VALUE;
+
+    private volatile boolean changed = true;
+
+    public synchronized boolean remove(Object action) {
+        boolean removed = false;
+        removed |= flushActions.removeIf(e -> e == action);
+        removed |= closeActions.removeIf(e -> e == action);
+        removed |= errorActions.removeIf(e -> e == action);
+        removed |= eventActions.removeIf(e -> e.getAction() == action);
+        if (removed) {
+            changed = true;
+        }
+        return removed;
+    }
+
+    public synchronized void addEventAction(String name, Consumer<RecordedEvent> consumer) {
+        eventActions.add(new EventDispatcher(name, consumer));
+        changed = true;
+    }
+
+    public void addEventAction(Consumer<RecordedEvent> action) {
+        addEventAction(null, action);
+    }
+
+    public synchronized void addFlushAction(Runnable action) {
+        flushActions.add(action);
+        changed = true;
+    }
+
+    public synchronized void addCloseAction(Runnable action) {
+        closeActions.add(action);
+        changed = true;
+    }
+
+    public synchronized void addErrorAction(Consumer<Throwable> action) {
+        errorActions.add(action);
+        changed = true;
+    }
+
+    public synchronized void setReuse(boolean reuse) {
+        this.reuse = reuse;
+        changed = true;
+    }
+
+    public synchronized void setOrdered(boolean ordered) {
+        this.ordered = ordered;
+        changed = true;
+    }
+
+    public synchronized void setEndTime(Instant endTime) {
+        this.endTime = endTime;
+        this.endNanos = Utils.timeToNanos(endTime);
+        changed = true;
+    }
+
+    public synchronized void setStartTime(Instant startTime) {
+        this.startTime = startTime;
+        this.startNanos = Utils.timeToNanos(startTime);
+        changed = true;
+    }
+
+    public synchronized void setStartNanos(long startNanos) {
+        this.startNanos = startNanos;
+        changed = true;
+    }
+
+    public synchronized void setStarted(boolean started) {
+        this.started = started;
+        changed = true;
+    }
+
+    public boolean hasChanged() {
+        return changed;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+public final class StringParser extends Parser {
+
+    public enum Encoding {
+        NULL(0),
+        EMPTY_STRING(1),
+        CONSTANT_POOL(2),
+        UT8_BYTE_ARRAY(3),
+        CHAR_ARRAY(4),
+        LATIN1_BYTE_ARRAY(5);
+
+        private byte byteValue;
+
+        private Encoding(int byteValue) {
+            this.byteValue = (byte) byteValue;
+        }
+
+        public byte byteValue() {
+            return byteValue;
+        }
+
+        public boolean is(byte value) {
+            return value == byteValue;
+        }
+
+    }
+    private final static Charset UTF8 = Charset.forName("UTF-8");
+    private final static Charset LATIN1 = Charset.forName("ISO-8859-1");
+
+    private final static class CharsetParser extends Parser {
+        private final Charset charset;
+        private int lastSize;
+        private byte[] buffer = new byte[16];
+        private String lastString;
+
+        CharsetParser(Charset charset) {
+            this.charset = charset;
+        }
+
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            int size = input.readInt();
+            ensureSize(size);
+            if (lastSize == size) {
+                boolean equalsLastString = true;
+                for (int i = 0; i < size; i++) {
+                    // TODO: No need to read byte per byte
+                    byte b = input.readByte();
+                    if (buffer[i] != b) {
+                        equalsLastString = false;
+                        buffer[i] = b;
+                    }
+                }
+                if (equalsLastString) {
+                    return lastString;
+                }
+            } else {
+                for (int i = 0; i < size; i++) {
+                    buffer[i] = input.readByte();
+                }
+            }
+            lastString = new String(buffer, 0, size, charset);
+            lastSize = size;
+            return lastString;
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            int size = input.readInt();
+            input.skipBytes(size);
+        }
+
+        private void ensureSize(int size) {
+            if (buffer.length < size) {
+                buffer = new byte[size];
+            }
+        }
+    }
+
+    private final static class CharArrayParser extends Parser {
+        private char[] buffer = new char[16];
+        private int lastSize = -1;
+        private String lastString = null;
+
+        @Override
+        public Object parse(RecordingInput input) throws IOException {
+            int size = input.readInt();
+            ensureSize(size);
+            if (lastSize == size) {
+                boolean equalsLastString = true;
+                for (int i = 0; i < size; i++) {
+                    char c = input.readChar();
+                    if (buffer[i] != c) {
+                        equalsLastString = false;
+                        buffer[i] = c;
+                    }
+                }
+                if (equalsLastString) {
+                    return lastString;
+                }
+            } else {
+                for (int i = 0; i < size; i++) {
+                    buffer[i] = input.readChar();
+                }
+            }
+            lastString = new String(buffer, 0, size);
+            lastSize = size;
+            return lastString;
+        }
+
+        @Override
+        public void skip(RecordingInput input) throws IOException {
+            int size = input.readInt();
+            for (int i = 0; i < size; i++) {
+                input.readChar();
+            }
+        }
+
+        private void ensureSize(int size) {
+            if (buffer.length < size) {
+                buffer = new char[size];
+            }
+        }
+    }
+
+    private final ConstantLookup stringLookup;
+    private final CharArrayParser charArrayParser = new CharArrayParser();
+    private final CharsetParser utf8parser = new CharsetParser(UTF8);
+    private final CharsetParser latin1parser = new CharsetParser(LATIN1);
+    private final boolean event;
+
+    public StringParser(ConstantLookup stringLookup, boolean event) {
+        this.stringLookup = stringLookup;
+        this.event = event;
+    }
+
+    @Override
+    public Object parse(RecordingInput input) throws IOException {
+        byte encoding = input.readByte();
+        if (Encoding.CONSTANT_POOL.is(encoding)) {
+            long key = input.readLong();
+            if (event) {
+                return stringLookup.getCurrentResolved(key);
+            } else {
+                return stringLookup.getCurrent(key);
+            }
+        }
+        if (Encoding.NULL.is(encoding)) {
+            return null;
+        }
+        if (Encoding.EMPTY_STRING.is(encoding)) {
+            return "";
+        }
+        if (Encoding.CHAR_ARRAY.is(encoding)) {
+            return charArrayParser.parse(input);
+        }
+        if (Encoding.UT8_BYTE_ARRAY.is(encoding)) {
+            return utf8parser.parse(input);
+        }
+        if (Encoding.LATIN1_BYTE_ARRAY.is(encoding)) {
+            return latin1parser.parse(input);
+        }
+        throw new IOException("Unknown string encoding " + encoding);
+    }
+
+    @Override
+    public void skip(RecordingInput input) throws IOException {
+        byte encoding = input.readByte();
+        if (Encoding.CONSTANT_POOL.is(encoding)) {
+            input.readLong();
+            return;
+        }
+        if (Encoding.EMPTY_STRING.is(encoding)) {
+            return;
+        }
+        if (Encoding.NULL.is(encoding)) {
+            return;
+        }
+        if (Encoding.CHAR_ARRAY.is(encoding)) {
+            charArrayParser.skip(input);
+            return;
+        }
+        if (Encoding.UT8_BYTE_ARRAY.is(encoding)) {
+            utf8parser.skip(input);
+            return;
+        }
+        if (Encoding.LATIN1_BYTE_ARRAY.is(encoding)) {
+            latin1parser.skip(input);
+            return;
+        }
+        throw new IOException("Unknown string encoding " + encoding);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/TimeConverter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2016, 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.internal.consumer;
+
+import java.time.DateTimeException;
+import java.time.ZoneOffset;
+
+import jdk.jfr.internal.LogLevel;
+import jdk.jfr.internal.LogTag;
+import jdk.jfr.internal.Logger;
+import jdk.jfr.internal.consumer.ChunkHeader;
+
+/**
+ * Converts ticks to nanoseconds
+ */
+final class TimeConverter {
+    private final long startTicks;
+    private final long startNanos;
+    private final double divisor;
+    private final ZoneOffset zoneOffet;
+
+    TimeConverter(ChunkHeader chunkHeader, int rawOffset) {
+        this.startTicks = chunkHeader.getStartTicks();
+        this.startNanos = chunkHeader.getStartNanos();
+        this.divisor = chunkHeader.getTicksPerSecond() / 1000_000_000L;
+        this.zoneOffet = zoneOfSet(rawOffset);
+    }
+
+    public long convertTimestamp(long ticks) {
+        return startNanos + (long) ((ticks - startTicks) / divisor);
+    }
+
+    public long convertTimespan(long ticks) {
+        return (long) (ticks / divisor);
+    }
+
+    public ZoneOffset getZoneOffset() {
+        return zoneOffet;
+    }
+
+    private ZoneOffset zoneOfSet(int rawOffset) {
+        try {
+            return ZoneOffset.ofTotalSeconds(rawOffset / 1000);
+        } catch (DateTimeException dte) {
+            Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Could not create ZoneOffset from raw offset " + rawOffset);
+        }
+        return ZoneOffset.UTC;
+    }
+}
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -27,10 +27,12 @@
 
 
 
+import jdk.jfr.FlightRecorder;
 import jdk.jfr.internal.LogLevel;
 import jdk.jfr.internal.LogTag;
 import jdk.jfr.internal.Logger;
 import jdk.jfr.internal.Options;
+import jdk.jfr.internal.PrivateAccess;
 import jdk.jfr.internal.Repository;
 import jdk.jfr.internal.SecuritySupport.SafePath;
 
@@ -89,6 +91,9 @@
                 SafePath s = new SafePath(repositoryPath);
                 Repository.getRepository().setBasePath(s);
                 Logger.log(LogTag.JFR, LogLevel.INFO, "Base repository path set to " + repositoryPath);
+                if (FlightRecorder.isInitialized()) {
+                    PrivateAccess.getInstance().getPlatformRecorder().rotateIfRecordingToDisk();;
+                }
             } catch (Exception e) {
                 throw new DCmdException("Could not use " + repositoryPath + " as repository. " + e.getMessage(), e);
             }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -81,7 +81,7 @@
      * @throws DCmdException if recording could not be started
      */
     @SuppressWarnings("resource")
-    public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
+    public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Long flush, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
         if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
             Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name +
                     ", settings=" + Arrays.asList(settings) +
@@ -90,6 +90,7 @@
                     ", disk=" + disk+
                     ", filename=" + path +
                     ", maxage=" + maxAge +
+                    ", flush=" + flush +
                     ", maxsize=" + maxSize +
                     ", dumponexit =" + dumpOnExit +
                     ", path-to-gc-roots=" + pathToGcRoots);
@@ -136,6 +137,12 @@
             }
         }
 
+        if (flush != null) {
+            if (Boolean.FALSE.equals(disk)) {
+                throw new DCmdException("Flush can only be set for recordings that are to disk.");
+            }
+        }
+
         if (!FlightRecorder.isInitialized() && delay == null) {
             initializeWithForcedInstrumentation(s);
         }
@@ -148,6 +155,7 @@
         if (disk != null) {
             recording.setToDisk(disk.booleanValue());
         }
+
         recording.setSettings(s);
         SafePath safePath = null;
 
@@ -177,6 +185,10 @@
             recording.setMaxAge(Duration.ofNanos(maxAge));
         }
 
+        if (flush != null) {
+            recording.setFlushInterval(Duration.ofNanos(flush));
+        }
+
         if (maxSize != null) {
             recording.setMaxSize(maxSize);
         }
@@ -222,6 +234,7 @@
             print("Use jcmd " + getPid() + " JFR." + cmd + " " + recordingspecifier + " " + fileOption + "to copy recording data to file.");
             println();
         }
+
         return getResult();
     }
 
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Disassemble.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -41,6 +41,7 @@
 import java.util.List;
 
 import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.FileAccess;
 import jdk.jfr.internal.consumer.RecordingInput;
 
 final class Disassemble extends Command {
@@ -163,7 +164,7 @@
     }
 
     private List<Long> findChunkSizes(Path p) throws IOException {
-        try (RecordingInput input = new RecordingInput(p.toFile())) {
+        try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILIGED)) {
             List<Long> sizes = new ArrayList<>();
             ChunkHeader ch = new ChunkHeader(input);
             sizes.add(ch.getSize());
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/EventPrintWriter.java	Mon Nov 04 11:25:55 2019 +0000
@@ -30,6 +30,7 @@
 import java.io.PrintWriter;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -42,7 +43,7 @@
 import jdk.jfr.consumer.RecordedEvent;
 import jdk.jfr.consumer.RecordedObject;
 import jdk.jfr.consumer.RecordingFile;
-import jdk.jfr.internal.consumer.RecordingInternals;
+import jdk.jfr.internal.consumer.JdkJfrConsumer;
 
 abstract class EventPrintWriter extends StructuredWriter {
 
@@ -52,6 +53,7 @@
 
     protected static final String STACK_TRACE_FIELD = "stackTrace";
     protected static final String EVENT_THREAD_FIELD = "eventThread";
+    private static final JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
 
     private Predicate<EventType> eventFilter = x -> true;
     private int stackDepth;
@@ -74,8 +76,8 @@
                 if (acceptEvent(event)) {
                     events.add(event);
                 }
-                if (RecordingInternals.INSTANCE.isLastEventInChunk(file)) {
-                    RecordingInternals.INSTANCE.sort(events);
+                if (PRIVATE_ACCESS.isLastEventInChunk(file)) {
+                    Collections.sort(events, PRIVATE_ACCESS.eventComparator());
                     print(events);
                     events.clear();
                 }
@@ -121,7 +123,7 @@
         case TIMESPAN:
             return object.getDuration(v.getName());
         case TIMESTAMP:
-            return RecordingInternals.INSTANCE.getOffsetDataTime(object, v.getName());
+            return PRIVATE_ACCESS.getOffsetDataTime(object, v.getName());
         default:
             return object.getValue(v.getName());
         }
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java	Mon Nov 04 11:25:55 2019 +0000
@@ -35,10 +35,12 @@
 
 import jdk.jfr.consumer.RecordingFile;
 import jdk.jfr.internal.Type;
-import jdk.jfr.internal.consumer.RecordingInternals;
+import jdk.jfr.internal.consumer.JdkJfrConsumer;
 
 final class Metadata extends Command {
 
+    private final static JdkJfrConsumer PRIVATE_ACCESS = JdkJfrConsumer.instance();
+
     private static class TypeComparator implements Comparator<Type> {
 
         @Override
@@ -89,6 +91,7 @@
         }
     }
 
+
     @Override
     public String getName() {
         return "metadata";
@@ -125,7 +128,7 @@
             PrettyWriter prettyWriter = new PrettyWriter(pw);
             prettyWriter.setShowIds(showIds);
             try (RecordingFile rf = new RecordingFile(file)) {
-                List<Type> types = RecordingInternals.INSTANCE.readTypes(rf);
+                List<Type> types = PRIVATE_ACCESS.readTypes(rf);
                 Collections.sort(types, new TypeComparator());
                 for (Type type : types) {
                     prettyWriter.printType(type);
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Summary.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Summary.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -42,6 +42,7 @@
 import jdk.jfr.internal.MetadataDescriptor;
 import jdk.jfr.internal.Type;
 import jdk.jfr.internal.consumer.ChunkHeader;
+import jdk.jfr.internal.consumer.FileAccess;
 import jdk.jfr.internal.consumer.RecordingInput;
 
 final class Summary extends Command {
@@ -91,7 +92,7 @@
         long totalDuration = 0;
         long chunks = 0;
 
-        try (RecordingInput input = new RecordingInput(p.toFile())) {
+        try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILIGED)) {
             ChunkHeader first = new ChunkHeader(input);
             ChunkHeader ch = first;
             String eventPrefix = Type.EVENT_NAME_PREFIX;
--- a/src/jdk.jfr/share/conf/jfr/default.jfc	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/conf/jfr/default.jfc	Mon Nov 04 11:25:55 2019 +0000
@@ -406,7 +406,7 @@
       <setting name="enabled" control="gc-enabled-normal">true</setting>
       <setting name="threshold">0 ms</setting>
     </event>
- 
+
     <event name="jdk.G1BasicIHOP">
       <setting name="enabled" control="gc-enabled-normal">true</setting>
     </event>
@@ -670,6 +670,36 @@
       <setting name="enabled">true</setting>
     </event>
 
+    <event name="jdk.Flush">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushStorage">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushStacktrace">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushStringPool">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushMetadata">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushTypeSet">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
     <event name="jdk.DataLoss">
       <setting name="enabled">true</setting>
     </event>
--- a/src/jdk.jfr/share/conf/jfr/profile.jfc	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jfr/share/conf/jfr/profile.jfc	Mon Nov 04 11:25:55 2019 +0000
@@ -406,7 +406,7 @@
       <setting name="enabled" control="gc-enabled-normal">true</setting>
       <setting name="threshold">0 ms</setting>
     </event>
-    
+
     <event name="jdk.G1BasicIHOP">
       <setting name="enabled" control="gc-enabled-normal">true</setting>
     </event>
@@ -670,6 +670,36 @@
       <setting name="enabled">true</setting>
     </event>
 
+    <event name="jdk.Flush">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushStorage">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushStacktrace">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushStringPool">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushMetadata">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
+    <event name="jdk.FlushTypeSet">
+      <setting name="enabled">true</setting>
+      <setting name="threshold">0 ns</setting>
+    </event>
+
     <event name="jdk.DataLoss">
       <setting name="enabled">true</setting>
     </event>
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Mon Nov 04 11:25:55 2019 +0000
@@ -50,6 +50,7 @@
 
     static {
         CATEGORIES_ORDER.add(Category.FILTER);
+        CATEGORIES_ORDER.add(Category.ADDER);
         CATEGORIES_ORDER.add(Category.TRANSFORMER);
         CATEGORIES_ORDER.add(Category.MODULEINFO_TRANSFORMER);
         CATEGORIES_ORDER.add(Category.SORTER);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -325,7 +325,7 @@
 
                                 Map<String, String> m = addArgumentMap(plugin);
                                 // handle one or more arguments
-                                if (arg.indexOf(':') == -1) {
+                                if (plugin.hasRawArgument() || arg.indexOf(':') == -1) {
                                     // single argument case
                                     m.put(option, arg);
                                 } else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/AddOptionsPlugin.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.tools.jlink.internal.plugins;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.*;
+import jdk.tools.jlink.plugin.*;
+
+/**
+ * Plugin to add VM command-line options, by storing them in a resource
+ * that's read by the VM at startup
+ */
+public final class AddOptionsPlugin extends AddResourcePlugin {
+
+    public AddOptionsPlugin() {
+        super("add-options", "/java.base/jdk/internal/vm/options");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/AddResourcePlugin.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.tools.jlink.internal.plugins;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.*;
+import jdk.tools.jlink.plugin.*;
+
+/**
+ * Base plugin to add a resource
+ */
+abstract class AddResourcePlugin implements Plugin {
+
+    private final String name;
+    private final String path;
+    private String value;
+
+    protected AddResourcePlugin(String n, String p) {
+        name = n;
+        path = p;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(name);
+    }
+
+    @Override
+    public Category getType() {
+        return Category.ADDER;
+    }
+
+    @Override
+    public boolean hasArguments() {
+        return true;
+    }
+
+    @Override
+    public boolean hasRawArgument() {
+        return true;
+    }
+
+    @Override
+    public String getArgumentsDescription() {
+       return PluginsResourceBundle.getArgument(name);
+    }
+
+    @Override
+    public void configure(Map<String, String> config) {
+        var v = config.get(name);
+        if (v == null)
+            throw new AssertionError();
+        value = v;
+    }
+
+    @Override
+    public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
+        in.transformAndCopy(Function.identity(), out);
+        out.add(ResourcePoolEntry.create(path,
+                                         value.getBytes(StandardCharsets.UTF_8)));
+        return out.build();
+    }
+
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Mon Nov 04 11:25:55 2019 +0000
@@ -36,7 +36,6 @@
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import jdk.internal.access.SharedSecrets;
 import jdk.internal.access.JavaLangInvokeAccess;
@@ -75,13 +74,13 @@
     private static final JavaLangInvokeAccess JLIA
             = SharedSecrets.getJavaLangInvokeAccess();
 
-    Set<String> speciesTypes = Set.of();
+    private final TreeSet<String> speciesTypes = new TreeSet<>();
 
-    Set<String> invokerTypes = Set.of();
+    private final TreeSet<String> invokerTypes = new TreeSet<>();
 
-    Set<String> callSiteTypes = Set.of();
+    private final TreeSet<String> callSiteTypes = new TreeSet<>();
 
-    Map<String, Set<String>> dmhMethods = Map.of();
+    private final Map<String, Set<String>> dmhMethods = new TreeMap<>();
 
     String mainArgument;
 
@@ -187,21 +186,31 @@
         mainArgument = config.get(NAME);
     }
 
+    private void addSpeciesType(String type) {
+        speciesTypes.add(expandSignature(type));
+    }
+
+    private void addInvokerType(String methodType) {
+        validateMethodType(methodType);
+        invokerTypes.add(methodType);
+    }
+
+    private void addCallSiteType(String csType) {
+        validateMethodType(csType);
+        callSiteTypes.add(csType);
+    }
+
     public void initialize(ResourcePool in) {
         // Start with the default configuration
-        speciesTypes = defaultSpecies().stream()
-                .map(type -> expandSignature(type))
-                .collect(Collectors.toSet());
+        defaultSpecies().stream().forEach(this::addSpeciesType);
 
-        invokerTypes = defaultInvokers();
-        validateMethodTypes(invokerTypes);
+        defaultInvokers().stream().forEach(this::validateMethodType);
 
-        callSiteTypes = defaultCallSiteTypes();
+        defaultCallSiteTypes().stream().forEach(this::addCallSiteType);
 
-        dmhMethods = defaultDMHMethods();
-        for (Set<String> dmhMethodTypes : dmhMethods.values()) {
-            validateMethodTypes(dmhMethodTypes);
-        }
+        defaultDMHMethods().entrySet().stream().forEach(e -> {
+            e.getValue().stream().forEach(type -> addDMHMethodType(e.getKey(), type));
+        });
 
         // Extend the default configuration with the contents in the supplied
         // input file - if none was supplied we look for the default file
@@ -225,18 +234,6 @@
     }
 
     private void readTraceConfig(Stream<String> lines) {
-        // Use TreeSet/TreeMap to keep things sorted in a deterministic
-        // order to avoid scrambling the layout on small changes and to
-        // ease finding methods in the generated code
-        speciesTypes = new TreeSet<>(speciesTypes);
-        invokerTypes = new TreeSet<>(invokerTypes);
-        callSiteTypes = new TreeSet<>(callSiteTypes);
-
-        TreeMap<String, Set<String>> newDMHMethods = new TreeMap<>();
-        for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
-            newDMHMethods.put(entry.getKey(), new TreeSet<>(entry.getValue()));
-        }
-        dmhMethods = newDMHMethods;
         lines.map(line -> line.split(" "))
              .forEach(parts -> {
                 switch (parts[0]) {
@@ -245,19 +242,18 @@
                         if (parts.length == 3 && parts[1].startsWith("java.lang.invoke.BoundMethodHandle$Species_")) {
                             String species = parts[1].substring("java.lang.invoke.BoundMethodHandle$Species_".length());
                             if (!"L".equals(species)) {
-                                speciesTypes.add(expandSignature(species));
+                                addSpeciesType(species);
                             }
                         }
                         break;
                     case "[LF_RESOLVE]":
                         String methodType = parts[3];
-                        validateMethodType(methodType);
                         if (parts[1].equals(INVOKERS_HOLDER_NAME)) {
                             if ("linkToTargetMethod".equals(parts[2]) ||
                                     "linkToCallSite".equals(parts[2])) {
-                                callSiteTypes.add(methodType);
+                                addCallSiteType(methodType);
                             } else {
-                                invokerTypes.add(methodType);
+                                addInvokerType(methodType);
                             }
                         } else if (parts[1].contains("DirectMethodHandle")) {
                             String dmh = parts[2];
@@ -291,12 +287,6 @@
         }
     }
 
-    private void validateMethodTypes(Set<String> dmhMethodTypes) {
-        for (String type : dmhMethodTypes) {
-            validateMethodType(type);
-        }
-    }
-
     private void validateMethodType(String type) {
         String[] typeParts = type.split("_");
         // check return type (second part)
@@ -340,9 +330,10 @@
         generateHolderClasses(out);
 
         // Let it go
-        speciesTypes = null;
-        invokerTypes = null;
-        dmhMethods = null;
+        speciesTypes.clear();
+        invokerTypes.clear();
+        callSiteTypes.clear();
+        dmhMethods.clear();
 
         return out.build();
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/VendorBugURLPlugin.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.tools.jlink.internal.plugins;
+
+/**
+ * Plugin to set the vendor bug URL, by redefining the static field
+ * java.lang.VersionProps.VENDOR_URL_BUG
+ */
+public final class VendorBugURLPlugin extends VersionPropsPlugin {
+
+    public VendorBugURLPlugin() {
+        super("VENDOR_URL_BUG", "vendor-bug-url");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/VendorVMBugURLPlugin.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.tools.jlink.internal.plugins;
+
+/**
+ * Plugin to set the vendor VM bug URL, by redefining the static field
+ * java.lang.VersionProps.VENDOR_URL_VM_BUG
+ */
+public final class VendorVMBugURLPlugin extends VersionPropsPlugin {
+
+    public VendorVMBugURLPlugin() {
+        super("VENDOR_URL_VM_BUG", "vendor-vm-bug-url");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/VendorVersionPlugin.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.tools.jlink.internal.plugins;
+
+/**
+ * Plugin to set the vendor version string, by redefining the static field
+ * java.lang.VersionProps.VENDOR_VERSION
+ */
+public final class VendorVersionPlugin extends VersionPropsPlugin {
+
+    public VendorVersionPlugin() {
+        super("VENDOR_VERSION");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/VersionPropsPlugin.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.tools.jlink.internal.plugins;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.*;
+import jdk.tools.jlink.plugin.*;
+import jdk.internal.org.objectweb.asm.*;
+
+import static java.lang.System.out;
+
+/**
+ * Base plugin to update a static field in java.lang.VersionProps
+ */
+abstract class VersionPropsPlugin implements Plugin {
+
+    private static final String VERSION_PROPS_CLASS
+        = "/java.base/java/lang/VersionProps.class";
+
+    private final String name;
+    private final String field;
+    private String value;
+
+    /**
+     * @param field The name of the java.lang.VersionProps field to be redefined
+     * @param option The option name
+     */
+    protected VersionPropsPlugin(String field, String option) {
+        this.field = field;
+        this.name = option;
+    }
+
+    /**
+     * Shorthand constructor for when the option name can be derived from the
+     * name of the field.
+     *
+     * @param field The name of the java.lang.VersionProps field to be redefined
+     */
+    protected VersionPropsPlugin(String field) {
+        this(field, field.toLowerCase().replace('_', '-'));
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getDescription() {
+        return PluginsResourceBundle.getDescription(name);
+    }
+
+    @Override
+    public Category getType() {
+        return Category.TRANSFORMER;
+    }
+
+    @Override
+    public boolean hasArguments() {
+        return true;
+    }
+
+    @Override
+    public boolean hasRawArgument() {
+        return true;
+    }
+
+    @Override
+    public String getArgumentsDescription() {
+       return PluginsResourceBundle.getArgument(name);
+    }
+
+    @Override
+    public void configure(Map<String, String> config) {
+        var v = config.get(name);
+        if (v == null)
+            throw new AssertionError();
+        value = v;
+    }
+
+    private boolean redefined = false;
+
+    private byte[] redefine(byte[] classFile) {
+
+        var cr = new ClassReader(classFile);
+        var cw = new ClassWriter(0);
+
+        cr.accept(new ClassVisitor(Opcodes.ASM7, cw) {
+
+                public MethodVisitor visitMethod(int access,
+                                                 String name,
+                                                 String desc,
+                                                 String sig,
+                                                 String[] xs)
+                {
+                    if (name.equals("<clinit>"))
+                        return new MethodVisitor(Opcodes.ASM7,
+                                                 super.visitMethod(access,
+                                                                   name,
+                                                                   desc,
+                                                                   sig,
+                                                                   xs))
+                            {
+
+                                public void visitFieldInsn(int opcode,
+                                                           String owner,
+                                                           String name,
+                                                           String desc)
+                                {
+                                    if (opcode == Opcodes.PUTSTATIC
+                                        && name.equals(field))
+                                    {
+                                        // Discard the original value
+                                        super.visitInsn(Opcodes.POP);
+                                        // Load the value that we want
+                                        super.visitLdcInsn(value);
+                                        redefined = true;
+                                    }
+                                    super.visitFieldInsn(opcode, owner,
+                                                         name, desc);
+                                }
+
+                        };
+                    else
+                        return super.visitMethod(access, name, desc, sig, xs);
+                }
+
+            }, 0);
+
+        return cw.toByteArray();
+
+    }
+
+    @Override
+    public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
+        in.transformAndCopy(res -> {
+                if (res.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)) {
+                    if (res.path().equals(VERSION_PROPS_CLASS)) {
+                        return res.copyWithContent(redefine(res.contentBytes()));
+                    }
+                }
+                return res;
+            }, out);
+        if (!redefined)
+            throw new AssertionError(field);
+        return out.build();
+    }
+
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -39,6 +39,7 @@
      * Order of categories matches the plugin sort order.
      * <ol>
      * <li>FILTER: Filter in/out resources or files.</li>
+     * <li>ADDER: Add resources or files.</li>
      * <li>TRANSFORMER: Transform resources or files(eg: refactoring, bytecode
      * manipulation).</li>
      * <li>MODULEINFO_TRANSFORMER: Transform only module-info.class</li>
@@ -52,6 +53,7 @@
      */
     public enum Category {
         FILTER("FILTER"),
+        ADDER("ADDER"),
         TRANSFORMER("TRANSFORMER"),
         MODULEINFO_TRANSFORMER("MODULEINFO_TRANSFORMER"),
         SORTER("SORTER"),
@@ -152,6 +154,10 @@
         return false;
     }
 
+    public default boolean hasRawArgument() {
+        return false;
+    }
+
     /**
      * The plugin argument(s) description.
      * @return  The argument(s) description.
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2019, 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
@@ -23,6 +23,13 @@
 # questions.
 #
 
+add-options.argument=<options>
+
+add-options.description=\
+Prepend the specified <options> string, which may include\n\
+whitespace, before any other options when invoking the virtual machine\n\
+in the resulting image.
+
 release-info.argument=<file>|add:<key1>=<value1>:<key2>=<value2>:...|del:<key list>
 
 release-info.description=\
@@ -105,6 +112,24 @@
 strip-native-commands.description=\
 Exclude native commands (such as java/java.exe) from the image
 
+vendor-version.argument=<vendor-version>
+
+vendor-version.description=\
+Override the vendor version string baked into the build, if any.\n\
+The value of the system property "java.vendor.version" will be <vendor-version>.
+
+vendor-bug-url.argument=<vendor-bug-url>
+
+vendor-bug-url.description=\
+Override the vendor bug URL baked into the build.  The value\n\
+of the system property "java.vendor.url.bug" will be <vendor-url-bug>.
+
+vendor-vm-bug-url.argument=<vendor-vm-bug-url>
+
+vendor-vm-bug-url.description=\
+Override the vendor VM bug URL baked into the build.  The URL\n\
+displayed in VM error logs will be <vendor-vm-bug-url>.
+
 vm.argument=<client|server|minimal|all>
 
 vm.description=\
--- a/src/jdk.jlink/share/classes/module-info.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.jlink/share/classes/module-info.java	Mon Nov 04 11:25:55 2019 +0000
@@ -72,5 +72,10 @@
         jdk.tools.jlink.internal.plugins.ExcludeVMPlugin,
         jdk.tools.jlink.internal.plugins.IncludeLocalesPlugin,
         jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin,
-        jdk.tools.jlink.internal.plugins.ReleaseInfoPlugin;
- }
+        jdk.tools.jlink.internal.plugins.ReleaseInfoPlugin,
+        jdk.tools.jlink.internal.plugins.AddOptionsPlugin,
+        jdk.tools.jlink.internal.plugins.VendorBugURLPlugin,
+        jdk.tools.jlink.internal.plugins.VendorVMBugURLPlugin,
+        jdk.tools.jlink.internal.plugins.VendorVersionPlugin;
+
+}
--- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java	Mon Nov 04 11:25:55 2019 +0000
@@ -739,7 +739,20 @@
                         Files.copy(eSrc.file, u.file, REPLACE_EXISTING);
                     }
                 }
+            } else if (eSrc.type == Entry.CEN && eSrc.method != defaultCompressionMethod) {
+
+                /**
+                 * We are copying a file within the same Zip file using a
+                 * different compression method.
+                 */
+                try (InputStream in = newInputStream(src);
+                     OutputStream out = newOutputStream(dst,
+                             CREATE, TRUNCATE_EXISTING, WRITE)) {
+                    in.transferTo(out);
+                }
+                u = getEntry(dst);
             }
+
             if (!hasCopyAttrs)
                 u.mtime = u.atime= u.ctime = System.currentTimeMillis();
             update(u);
@@ -789,7 +802,8 @@
                     return os;
                 }
                 return getOutputStream(supportPosix ?
-                    new PosixEntry((PosixEntry)e, Entry.NEW) : new Entry(e, Entry.NEW));
+                    new PosixEntry((PosixEntry)e, Entry.NEW, defaultCompressionMethod)
+                        : new Entry(e, Entry.NEW, defaultCompressionMethod));
             } else {
                 if (!hasCreate && !hasCreateNew)
                     throw new NoSuchFileException(getString(path));
@@ -2338,6 +2352,11 @@
             this.file = file;
         }
 
+        Entry(Entry e, int type, int compressionMethod) {
+            this(e, type);
+            this.method = compressionMethod;
+        }
+
         Entry(Entry e, int type) {
             name(e.name);
             this.isdir     = e.isdir;
@@ -2905,6 +2924,11 @@
             super(name, file, type, attrs);
         }
 
+        PosixEntry(PosixEntry e, int type, int compressionMethod) {
+            super(e, type);
+            this.method = compressionMethod;
+        }
+
         PosixEntry(PosixEntry e, int type) {
             super(e, type);
             this.owner = e.owner;
--- a/test/hotspot/gtest/jfr/test_networkUtilization.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/gtest/jfr/test_networkUtilization.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -51,7 +51,7 @@
 namespace {
 
   class MockFastUnorderedElapsedCounterSource : public ::FastUnorderedElapsedCounterSource {
-  public:
+   public:
     static jlong current_ticks;
     static Type now() {
       return current_ticks;
@@ -65,7 +65,7 @@
   typedef TimeInterval<CounterRepresentation, MockFastUnorderedElapsedCounterSource> MockJfrTickspan;
 
   class MockJfrCheckpointWriter {
-  public:
+   public:
     traceid current;
     std::map<traceid, std::string> ids;
 
@@ -78,44 +78,97 @@
     void write_key(traceid id) {
       current = id;
     }
-    void write(const char* data) {
-      ids[current] = data;
-    }
+    void write_type(JfrTypeId id) {}
+    MockJfrCheckpointWriter() {}
+    void write(const char* data) {}
     void set_context(const JfrCheckpointContext ctx) { }
-    void write_count(u4 nof_entries, jlong offset) { }
+    void write_count(u4 nof_entries) { }
   };
 
   class MockJfrSerializer {
-  public:
-    static MockJfrSerializer* current;
-
-    static bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, MockJfrSerializer* serializer) {
-      current = serializer;
+   public:
+    static bool register_serializer(JfrTypeId id, bool permit_cache, MockJfrSerializer* serializer) {
       return true;
     }
+    virtual void on_rotation() {}
+    virtual void serialize(MockJfrCheckpointWriter& writer) {}
+  };
 
-    virtual void serialize(MockJfrCheckpointWriter& writer) = 0;
+  struct MockNetworkInterface {
+    std::string name;
+    uint64_t bytes_in;
+    uint64_t bytes_out;
+    traceid id;
+    MockNetworkInterface(std::string name, uint64_t bytes_in, uint64_t bytes_out, traceid id) :
+      name(name), bytes_in(bytes_in), bytes_out(bytes_out), id(id) {}
+
+    bool operator==(const MockNetworkInterface& rhs) const {
+      return name == rhs.name;
+    }
+  };
+
+  class NetworkInterface : public ::NetworkInterface {
+   public:
+    NetworkInterface(const char* name, uint64_t bytes_in, uint64_t bytes_out, NetworkInterface* next) :
+      ::NetworkInterface(name, bytes_in, bytes_out, next) {}
+    NetworkInterface* next(void) const {
+      return reinterpret_cast<NetworkInterface*>(::NetworkInterface::next());
+    }
   };
 
-  MockJfrSerializer* MockJfrSerializer::current;
+  class MockJfrOSInterface {
+    static std::list<MockNetworkInterface> _interfaces;
+   public:
+    MockJfrOSInterface() {}
+    static int network_utilization(NetworkInterface** network_interfaces) {
+      *network_interfaces = NULL;
+      for (std::list<MockNetworkInterface>::const_iterator i = _interfaces.begin();
+           i != _interfaces.end();
+           ++i) {
+        NetworkInterface* cur = new NetworkInterface(i->name.c_str(), i->bytes_in, i->bytes_out, *network_interfaces);
+        *network_interfaces = cur;
+      }
+      return OS_OK;
+    }
+    static MockNetworkInterface& add_interface(const std::string& name, traceid id) {
+      MockNetworkInterface iface(name, 0, 0, id);
+      _interfaces.push_front(iface);
+      return _interfaces.front();
+    }
+    static void remove_interface(const MockNetworkInterface& iface) {
+      _interfaces.remove(iface);
+    }
+    static void clear_interfaces() {
+      _interfaces.clear();
+    }
+    static const MockNetworkInterface& get_interface(traceid id) {
+      std::list<MockNetworkInterface>::const_iterator i = _interfaces.begin();
+      for (; i != _interfaces.end(); ++i) {
+        if (i->id == id) {
+          break;
+        }
+      }
+      return *i;
+    }
+  };
 
-  class MockEventNetworkUtilization : public ::EventNetworkUtilization
-  {
-  public:
+  std::list<MockNetworkInterface> MockJfrOSInterface::_interfaces;
+
+  class MockEventNetworkUtilization : public ::EventNetworkUtilization {
+   public:
     std::string iface;
     s8 readRate;
     s8 writeRate;
     static std::vector<MockEventNetworkUtilization> committed;
     MockJfrCheckpointWriter writer;
 
-  public:
+   public:
     MockEventNetworkUtilization(EventStartTime timing=TIMED) :
-    ::EventNetworkUtilization(timing) {
-    }
+    ::EventNetworkUtilization(timing) {}
 
     void set_networkInterface(traceid new_value) {
-      MockJfrSerializer::current->serialize(writer);
-      iface = writer.ids[new_value];
+      const MockNetworkInterface& entry  = MockJfrOSInterface::get_interface(new_value);
+      iface = entry.name;
     }
     void set_readRate(s8 new_value) {
       readRate = new_value;
@@ -129,7 +182,6 @@
     }
 
     void set_starttime(const MockJfrTicks& time) {}
-
     void set_endtime(const MockJfrTicks& time) {}
 
     static const MockEventNetworkUtilization& get_committed(const std::string& name) {
@@ -149,62 +201,6 @@
 
   jlong MockFastUnorderedElapsedCounterSource::current_ticks;
 
-  struct MockNetworkInterface {
-    std::string name;
-    uint64_t bytes_in;
-    uint64_t bytes_out;
-    MockNetworkInterface(std::string name, uint64_t bytes_in, uint64_t bytes_out)
-    : name(name),
-    bytes_in(bytes_in),
-    bytes_out(bytes_out) {
-
-    }
-    bool operator==(const MockNetworkInterface& rhs) const {
-      return name == rhs.name;
-    }
-  };
-
-  class NetworkInterface : public ::NetworkInterface {
-  public:
-    NetworkInterface(const char* name, uint64_t bytes_in, uint64_t bytes_out, NetworkInterface* next)
-    : ::NetworkInterface(name, bytes_in, bytes_out, next) {
-    }
-    NetworkInterface* next(void) const {
-      return reinterpret_cast<NetworkInterface*>(::NetworkInterface::next());
-    }
-  };
-
-  class MockJfrOSInterface {
-    static std::list<MockNetworkInterface> _interfaces;
-
-  public:
-    MockJfrOSInterface() {
-    }
-    static int network_utilization(NetworkInterface** network_interfaces) {
-      *network_interfaces = NULL;
-      for (std::list<MockNetworkInterface>::const_iterator i = _interfaces.begin();
-           i != _interfaces.end();
-           ++i) {
-        NetworkInterface* cur = new NetworkInterface(i->name.c_str(), i->bytes_in, i->bytes_out, *network_interfaces);
-        *network_interfaces = cur;
-      }
-      return OS_OK;
-    }
-    static MockNetworkInterface& add_interface(const std::string& name) {
-      MockNetworkInterface iface(name, 0, 0);
-      _interfaces.push_back(iface);
-      return _interfaces.back();
-    }
-    static void remove_interface(const MockNetworkInterface& iface) {
-      _interfaces.remove(iface);
-    }
-    static void clear_interfaces() {
-      _interfaces.clear();
-    }
-  };
-
-  std::list<MockNetworkInterface> MockJfrOSInterface::_interfaces;
-
 // Reincluding source files in the anonymous namespace unfortunately seems to
 // behave strangely with precompiled headers (only when using gcc though)
 #ifndef DONT_USE_PRECOMPILED_HEADER
@@ -248,7 +244,7 @@
 
 TEST_VM_F(JfrTestNetworkUtilization, RequestFunctionBasic) {
 
-  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
+  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0", 1);
   JfrNetworkUtilization::send_events();
   ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());
 
@@ -265,9 +261,9 @@
 
 TEST_VM_F(JfrTestNetworkUtilization, RequestFunctionMultiple) {
 
-  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
-  MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1");
-  MockNetworkInterface& ppp0 = MockJfrOSInterface::add_interface("ppp0");
+  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0", 2);
+  MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1", 3);
+  MockNetworkInterface& ppp0 = MockJfrOSInterface::add_interface("ppp0", 4);
   JfrNetworkUtilization::send_events();
   ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());
 
@@ -296,8 +292,8 @@
 }
 
 TEST_VM_F(JfrTestNetworkUtilization, InterfaceRemoved) {
-  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
-  MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1");
+  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0", 5);
+  MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1", 6);
   JfrNetworkUtilization::send_events();
   ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());
 
@@ -333,7 +329,7 @@
 }
 
 TEST_VM_F(JfrTestNetworkUtilization, InterfaceReset) {
-  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
+  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0", 7);
   JfrNetworkUtilization::send_events();
   ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());
 
--- a/test/hotspot/gtest/jfr/test_threadCpuLoad.cpp	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/gtest/jfr/test_threadCpuLoad.cpp	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -36,11 +36,10 @@
 #include "jfr/jfrEvents.hpp"
 #include "jfr/support/jfrThreadId.hpp"
 #include "jfr/support/jfrThreadLocal.hpp"
+#include "jfr/utilities/jfrThreadIterator.hpp"
 #include "jfr/utilities/jfrTime.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "runtime/os.hpp"
-#include "runtime/thread.inline.hpp"
-#include "runtime/threadSMR.inline.hpp"
 
 #include "unittest.hpp"
 
@@ -81,11 +80,18 @@
     MockJavaThread() : ::JavaThread() {}
   };
 
-  class MockJavaThreadIteratorWithHandle
+  class MockJfrJavaThreadIterator
   {
   public:
     MockJavaThread* next() { return NULL; }
-    int length() { return 0; }
+    bool has_next() const { return false; }
+  };
+
+  class MockJfrJavaThreadIteratorAdapter
+  {
+  public:
+    MockJavaThread* next() { return NULL; }
+    bool has_next() const { return false; }
   };
 
 // Reincluding source files in the anonymous namespace unfortunately seems to
@@ -97,7 +103,8 @@
 #define os MockOs
 #define EventThreadCPULoad MockEventThreadCPULoad
 #define JavaThread MockJavaThread
-#define JavaThreadIteratorWithHandle MockJavaThreadIteratorWithHandle
+#define JfrJavaThreadIterator MockJfrJavaThreadIterator
+#define JfrJavaThreadIteratorAdapter MockJfrJavaThreadIteratorAdapter
 
 #include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
 #include "jfr/periodic/jfrThreadCPULoadEvent.cpp"
@@ -105,7 +112,8 @@
 #undef os
 #undef EventThreadCPULoad
 #undef JavaThread
-#undef JavaThreadIteratorWithHandle
+#define JfrJavaThreadIterator MockJfrJavaThreadIterator
+#define JfrJavaThreadIteratorAdapter MockJfrJavaThreadIteratorAdapter
 
 } // anonymous namespace
 
--- a/test/hotspot/jtreg/ProblemList.txt	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/ProblemList.txt	Mon Nov 04 11:25:55 2019 +0000
@@ -50,8 +50,8 @@
 compiler/jvmci/compilerToVM/InvalidateInstalledCodeTest.java 8163894 generic-all
 compiler/tiered/LevelTransitionTest.java 8067651 generic-all
 
-compiler/types/correctness/CorrectnessTest.java 8230019,8225620 generic-all,solaris-sparcv9
-compiler/types/correctness/OffTest.java         8230019,8225620 generic-all,solaris-sparcv9
+compiler/types/correctness/CorrectnessTest.java 8225620 solaris-sparcv9
+compiler/types/correctness/OffTest.java         8225620 solaris-sparcv9
 
 compiler/c2/Test6852078.java 8194310 generic-all
 compiler/c2/Test8004741.java 8214904 generic-all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/codegen/TestSignedMultiplyLong.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Arm Limited. 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.
+ */
+
+/*
+ * @test
+ * @bug 8232591
+ * @summary Test some cases of combined signed multiply long operation
+ * @library /test/lib
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions
+ *                   -Xcomp -XX:-TieredCompilation -XX:-Inline
+ *                   compiler.codegen.TestSignedMultiplyLong
+ */
+
+package compiler.codegen;
+
+import jdk.test.lib.Asserts;
+
+public class TestSignedMultiplyLong {
+
+    private static final int   minInt = Integer.MIN_VALUE; // -2147483648
+    private static final int   maxInt = Integer.MAX_VALUE; //  2147483647
+    private static final long minLong =    Long.MIN_VALUE; // -9223372036854775808
+    private static final long maxLong =    Long.MAX_VALUE; //  9223372036854775807
+
+    private static Case[] testCases = {
+        // case:     a       b       c         resSmaddl          resSmsubl          resSmnegl
+        new Case(  1000,   -200, 500000L,           300000L,           700000L,           200000L),
+        new Case(maxInt,      1,      1L, (long)maxInt + 1L, (long)minInt + 2L,  (long)minInt + 1L),
+        new Case(minInt,     -1,      1L, (long)maxInt + 2L, (long)minInt + 1L,       (long)minInt),
+        new Case(   -10, minInt,      1L,      21474836481L,     -21474836479L,      -21474836480L),
+        new Case(     1,      1, maxLong,           minLong,      maxLong - 1L,                -1L),
+        new Case(     1,     -1, minLong,           maxLong,      minLong + 1L,                 1L),
+        new Case(    -1,     -1, 0xffffffffeL << 32, 0xfffffffe00000001L, 0xfffffffdffffffffL, -1L)
+    };
+
+    private static class Case {
+
+        private int a;
+        private int b;
+        private long c;
+        private long resSmaddl;
+        private long resSmsubl;
+        private long resSmnegl;
+
+        public Case(int a, int b, long c, long resSmaddl, long resSmsubl, long resSmnegl) {
+            this.a = a;
+            this.b = b;
+            this.c = c;
+            this.resSmaddl = resSmaddl;
+            this.resSmsubl = resSmsubl;
+            this.resSmnegl = resSmnegl;
+        }
+
+        public void verify() {
+            Asserts.assertEQ(smaddl(a, b, c), resSmaddl,
+                "Unexpected result from signed multiply-add long.");
+            Asserts.assertEQ(smsubl(a, b, c), resSmsubl,
+                "Unexpected result from signed multiply-sub long.");
+            Asserts.assertEQ(smnegl(a, b), resSmnegl,
+                "Unexpected result from signed multiply-neg long.");
+        }
+    }
+
+    private static long smaddl(int a, int b, long c) {
+        return c + a * (long) b;
+    }
+
+    private static long smsubl(int a, int b, long c) {
+        return c - a * (long) b;
+    }
+
+    private static long smnegl(int a, int b) {
+        return a * (-(long) b);
+    }
+
+    public static void main(String[] args) {
+        for (Case c : testCases) {
+            c.verify();
+        }
+    }
+}
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/DebugInfoTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/DebugInfoTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -62,32 +62,32 @@
             /*
              * Ensure that any objects mentioned in the VirtualObjects are also in the OopMap.
              */
-            List<Location> newLocations = new ArrayList<Location>(Arrays.asList(objects));
-            List<Location> newDerived = new ArrayList<Location>(Arrays.asList(derivedBase));
+            List<Location> newLocations = new ArrayList<>(Arrays.asList(objects));
+            List<Location> newDerived = new ArrayList<>(Arrays.asList(derivedBase));
             int[] newSizeInBytes = sizeInBytes;
             VirtualObject[] vobjs = compiler.compile(asm, values);
             if (vobjs != null) {
                 for (VirtualObject obj : vobjs) {
                     JavaValue[] objValues = obj.getValues();
                     for (int i = 0; i < objValues.length; i++) {
-                            if (obj.getSlotKind(i) == JavaKind.Object) {
-                                    Location oopLocation = null;
-                                    int bytes = -1;
-                                    if (objValues[i] instanceof RegisterValue) {
-                                            RegisterValue reg = (RegisterValue) objValues[i];
-                                            oopLocation = Location.register(reg.getRegister());
-                                            bytes = reg.getValueKind().getPlatformKind().getSizeInBytes();
-                                    } else if (objValues[i] instanceof StackSlot) {
-                                            StackSlot slot = (StackSlot) objValues[i];
-                                            oopLocation = Location.stack(asm.getOffset(slot));
-                                            bytes = slot.getValueKind().getPlatformKind().getSizeInBytes();
-                                    }
-                                    if (oopLocation != null && !newLocations.contains(oopLocation)) {
-                                            newLocations.add(oopLocation);
-                                            newDerived.add(null);
-                                            newSizeInBytes = Arrays.copyOf(newSizeInBytes, newSizeInBytes.length + 1);
-                                        newSizeInBytes[newSizeInBytes.length - 1] = bytes;
-                                }
+                        if (obj.getSlotKind(i) == JavaKind.Object) {
+                            Location oopLocation = null;
+                            int bytes = -1;
+                            if (objValues[i] instanceof RegisterValue) {
+                                RegisterValue reg = (RegisterValue) objValues[i];
+                                oopLocation = Location.register(reg.getRegister());
+                                bytes = reg.getValueKind().getPlatformKind().getSizeInBytes();
+                            } else if (objValues[i] instanceof StackSlot) {
+                                StackSlot slot = (StackSlot) objValues[i];
+                                oopLocation = Location.stack(asm.getOffset(slot));
+                                bytes = slot.getValueKind().getPlatformKind().getSizeInBytes();
+                            }
+                            if (oopLocation != null && !newLocations.contains(oopLocation)) {
+                                newLocations.add(oopLocation);
+                                newDerived.add(null);
+                                newSizeInBytes = Arrays.copyOf(newSizeInBytes, newSizeInBytes.length + 1);
+                                newSizeInBytes[newSizeInBytes.length - 1] = bytes;
+                            }
                         }
                     }
                 }
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/NativeCallTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/NativeCallTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -77,7 +77,8 @@
     @Test
     public void testF32SDILDS() {
         int sCount = 32;
-        Object[] remainingArgs = new Object[]{ // Pairs of <Object>, <Class>
+        // Pairs of <Object>, <Class>
+        Object[] remainingArgs = new Object[]{
                         1.2345678F, float.class,
                         3.212434D, double.class,
                         43921652, int.class,
@@ -101,7 +102,8 @@
     @Test
     public void testI32SDILDS() {
         int sCount = 32;
-        Object[] remainingArgs = new Object[]{ // Pairs of <Object>, <Class>
+        // Pairs of <Object>, <Class>
+        Object[] remainingArgs = new Object[]{
                         1.2345678F, float.class,
                         3.212434D, double.class,
                         43921652, int.class,
@@ -143,6 +145,8 @@
         }
     }
 
+    // Checkstyle: stop
+
     public static native long getFF();
 
     public static native float _FF(float a, float b);
@@ -187,6 +191,7 @@
                     double d18, double d19, double d1a, double d1b, double d1c, double d1d, double d1e, double d1f,
                     float a, double b, int c, long d, double e, float f);
 
+    @SuppressWarnings("unused")
     public static float D32SDILDS(double d00, double d01, double d02, double d03, double d04, double d05, double d06, double d07,
                     double d08, double d09, double d0a, double d0b, double d0c, double d0d, double d0e, double d0f,
                     double d10, double d11, double d12, double d13, double d14, double d15, double d16, double d17,
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestAssembler.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestAssembler.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -206,7 +206,7 @@
 
     private StackSlot deoptRescue;
 
-    public ValueKindFactory<TestValueKind> valueKindFactory = new ValueKindFactory<TestAssembler.TestValueKind>() {
+    public ValueKindFactory<TestValueKind> valueKindFactory = new ValueKindFactory<>() {
         public TestValueKind getValueKind(JavaKind javaKind) {
             return (TestValueKind) TestAssembler.this.getValueKind(javaKind);
         }
@@ -389,7 +389,7 @@
     public abstract void emitLoad(AllocatableValue av, Object prim);
 
     /**
-     * Emit a call to a fixed address <code>addr</code>
+     * Emit a call to a fixed address <code>addr</code>.
      */
     public abstract void emitCall(long addr);
 
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -42,6 +42,7 @@
 
     public final int classMirrorHandleOffset = getFieldOffset("Klass::_java_mirror", Integer.class, "OopHandle");
 
+    // Checkstyle: stop
     public final int MARKID_DEOPT_HANDLER_ENTRY = getConstant("CodeInstaller::DEOPT_HANDLER_ENTRY", Integer.class);
     public final long handleDeoptStub = getFieldValue("CompilerToVM::Data::SharedRuntime_deopt_blob_unpack", Long.class, "address");
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/VirtualObjectFormattingTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, 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 jdk.vm.ci.code.test;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import jdk.vm.ci.code.VirtualObject;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaValue;
+import jdk.vm.ci.meta.ResolvedJavaType;
+
+public class VirtualObjectFormattingTest extends VirtualObjectTestBase {
+
+    @Test
+    public void testFormat() {
+        testBase();
+    }
+
+    @Override
+    protected void test(ResolvedJavaType klass, JavaValue[] kinds, JavaKind[] values, boolean malformed) {
+        // Verify that VirtualObject.toString will produce output without throwing exceptions or
+        // asserting.
+        VirtualObject virtual = VirtualObject.get(klass, 0);
+        virtual.setValues(kinds, values);
+        Assert.assertTrue(!virtual.toString().equals(""));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/VirtualObjectTestBase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2019, 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 jdk.vm.ci.code.test;
+
+import java.util.Arrays;
+
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaValue;
+import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.runtime.JVMCI;
+
+public abstract class VirtualObjectTestBase {
+
+    public static class SimpleObject {
+        int i1;
+        int i2;
+        int i3;
+        int i4;
+        int i5;
+        int i6;
+    }
+
+    public static JavaConstant getValue(JavaKind kind) {
+        long dummyValue = kind.ordinal();
+        dummyValue = dummyValue | dummyValue << 8;
+        dummyValue = dummyValue | dummyValue << 16;
+        dummyValue = dummyValue | dummyValue << 32;
+        if (kind.isNumericInteger()) {
+            return JavaConstant.forIntegerKind(kind, dummyValue);
+        } else if (kind == JavaKind.Float) {
+            return JavaConstant.forDouble(Double.longBitsToDouble(dummyValue));
+        } else if (kind == JavaKind.Float) {
+            return JavaConstant.forFloat(Float.intBitsToFloat((int) dummyValue));
+        } else {
+            return JavaConstant.NULL_POINTER;
+        }
+    }
+
+    public static JavaValue[] getJavaValues(JavaKind[] kinds) {
+        JavaValue[] values = new JavaValue[kinds.length];
+        for (int i = 0; i < kinds.length; i++) {
+            values[i] = getValue(kinds[i]);
+        }
+        return values;
+    }
+
+    /**
+     * Subclasses are expected to override this method to provide their own verification logic using
+     * the normal JUnit {@link org.junit.Assert} methods.
+     *
+     * @param klass class for the {@link jdk.vm.ci.code.VirtualObject}
+     * @param kinds {@link JavaKind Javakinds} for values
+     * @param values {@link JavaValue values} for materializing the
+     *            {@link jdk.vm.ci.code.VirtualObject}
+     * @param malformed indicates whether the resulting virtual object is considered to be properly
+     *            formed relative to the fields of {@code klass}
+     * @throws AssertionError if a problem is detected
+     */
+    protected abstract void test(ResolvedJavaType klass, JavaValue[] kinds, JavaKind[] values, boolean malformed);
+
+    public void testBase() {
+        MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
+
+        ResolvedJavaType simple = metaAccess.lookupJavaType(SimpleObject.class);
+        ResolvedJavaField[] fields = simple.getInstanceFields(true);
+
+        JavaKind[] fieldKinds = new JavaKind[fields.length];
+        for (int i = 0; i < fields.length; i++) {
+            fieldKinds[i] = fields[i].getType().getJavaKind();
+        }
+
+        // Generate a straightforward VirtualObject with values that match to declared field types.
+        JavaKind[] kinds = fieldKinds.clone();
+        JavaValue[] values = getJavaValues(kinds);
+        test(simple, values, kinds, false);
+
+        // Spread a long value across two int fields
+        kinds = Arrays.copyOf(fieldKinds, fieldKinds.length - 1);
+        kinds[1] = JavaKind.Long;
+        test(simple, getJavaValues(kinds), kinds, false);
+
+        // Produce a long value for the final int field so there is no matching int field for the
+        // second half of the long
+        kinds = fieldKinds.clone();
+        kinds[kinds.length - 1] = JavaKind.Long;
+        test(simple, getJavaValues(kinds), kinds, true);
+
+        // Not enough values for the fields.
+        kinds = Arrays.copyOf(fieldKinds, fieldKinds.length - 1);
+        test(simple, getJavaValues(kinds), kinds, true);
+
+        // Too many values for the fields.
+        kinds = Arrays.copyOf(fieldKinds, fieldKinds.length + 1);
+        kinds[kinds.length - 1] = JavaKind.Int;
+        test(simple, getJavaValues(kinds), kinds, true);
+    }
+}
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MethodHandleAccessProviderData.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MethodHandleAccessProviderData.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -173,6 +173,7 @@
     }
 
     // can't use nested classes for storing these test methods. see JDK-8010319
+    @SuppressWarnings("unused")
     private void privateMethod() {
         // empty
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/VirtualObjectLayoutTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019, 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 jdk.vm.ci.hotspot.test;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import jdk.vm.ci.code.VirtualObject;
+import jdk.vm.ci.code.test.VirtualObjectTestBase;
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.hotspot.HotSpotCompiledCode;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaValue;
+import jdk.vm.ci.meta.ResolvedJavaType;
+
+public class VirtualObjectLayoutTest extends VirtualObjectTestBase {
+
+    @Override
+    protected void test(ResolvedJavaType klass, JavaValue[] values, JavaKind[] kinds, boolean error) {
+        // Verify that the layout checking will correctly report errors
+        VirtualObject virtual = VirtualObject.get(klass, 0);
+        virtual.setValues(values, kinds);
+        try {
+            HotSpotCompiledCode.verifyVirtualObject(virtual);
+        } catch (JVMCIError e) {
+            Assert.assertTrue("Unexpected error verifying " + virtual, error);
+            return;
+        }
+        Assert.assertFalse("Expected error but passed verifying " + virtual, error);
+    }
+
+    @Test
+    public void testFormat() {
+        testBase();
+    }
+}
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestSpeculationLog.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestSpeculationLog.java	Mon Nov 04 11:25:55 2019 +0000
@@ -4,9 +4,7 @@
  *
  * 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
+ * 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
--- a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass/Launcher.java	Mon Nov 04 11:25:55 2019 +0000
@@ -31,8 +31,7 @@
  * @build compiler.profiling.spectrapredefineclass.Agent
  * @run driver ClassFileInstaller compiler.profiling.spectrapredefineclass.Agent
  * @run driver compiler.profiling.spectrapredefineclass.Launcher
- * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation
- *                   -XX:CompileThreshold=10000
+ * @run main/othervm -XX:CompilationMode=high-only -XX:-BackgroundCompilation -XX:CompileThreshold=10000
  *                   -XX:-UseOnStackReplacement -XX:TypeProfileLevel=222
  *                   -XX:ReservedCodeCacheSize=3M -Djdk.attach.allowAttachSelf
  *                   compiler.profiling.spectrapredefineclass.Agent
--- a/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/compiler/profiling/spectrapredefineclass_classloaders/Launcher.java	Mon Nov 04 11:25:55 2019 +0000
@@ -34,8 +34,7 @@
  *        compiler.profiling.spectrapredefineclass_classloaders.B
  * @run driver ClassFileInstaller compiler.profiling.spectrapredefineclass_classloaders.Agent
  * @run driver compiler.profiling.spectrapredefineclass_classloaders.Launcher
- * @run main/othervm -XX:-TieredCompilation -XX:-BackgroundCompilation
- *                   -XX:CompileThreshold=10000
+ * @run main/othervm -XX:CompilationMode=high-only -XX:-BackgroundCompilation -XX:CompileThreshold=10000
  *                   -XX:-UseOnStackReplacement -XX:TypeProfileLevel=222
  *                   -XX:ReservedCodeCacheSize=3M -Djdk.attach.allowAttachSelf
  *                   compiler.profiling.spectrapredefineclass_classloaders.Agent
--- a/test/hotspot/jtreg/runtime/LoadClass/LongBCP.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/runtime/LoadClass/LongBCP.java	Mon Nov 04 11:25:55 2019 +0000
@@ -102,8 +102,6 @@
               .shouldHaveExitValue(0);
 
         // relative path tests
-        // We currently cannot handle relative path specified in the
-        // -Xbootclasspath/a on windows.
         //
         // relative path length within the file system limit
         int fn_max_length = 255;
@@ -128,6 +126,22 @@
         output.shouldContain("Hello World")
               .shouldHaveExitValue(0);
 
+        // Test a relative path for a jar file < MAX_PATH, but where the
+        // absolute path is > MAX_PATH.
+        Path jarDir = Paths.get(".");
+        for (int i = 0; i < 21; ++i) {
+            jarDir = jarDir.resolve("0123456789");
+        }
+        Files.createDirectories(jarDir);
+        Path jarPath = jarDir.resolve("hello.jar");
+        Files.copy(Paths.get(helloJar), jarPath);
+        bootCP = "-Xbootclasspath/a:" + jarPath.toString();
+        pb = ProcessTools.createJavaProcessBuilder(bootCP, "Hello");
+
+        output = new OutputAnalyzer(pb.start());
+        output.shouldContain("Hello World")
+              .shouldHaveExitValue(0);
+
         // total relative path length exceeds MAX_PATH
         destDir = Paths.get(destDir.toString(), "yyyyyyyy");
 
--- a/test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * @test
- * @bug 8167108
- * @summary Stress test java.lang.Thread.countStackFrames() at thread exit.
- * @run main/othervm -Xlog:thread+smr=debug CountStackFramesAtExit
- */
-
-import java.util.concurrent.CountDownLatch;
-
-public class CountStackFramesAtExit extends Thread {
-    final static int N_THREADS = 32;
-    final static int N_LATE_CALLS = 1000;
-
-    public CountDownLatch exitSyncObj = new CountDownLatch(1);
-    public CountDownLatch startSyncObj = new CountDownLatch(1);
-
-    @Override
-    public void run() {
-        // Tell main thread we have started.
-        startSyncObj.countDown();
-        try {
-            // Wait for main thread to interrupt us so we
-            // can race to exit.
-            exitSyncObj.await();
-        } catch (InterruptedException e) {
-            // ignore because we expect one
-        }
-    }
-
-    public static void main(String[] args) {
-        CountStackFramesAtExit threads[] = new CountStackFramesAtExit[N_THREADS];
-
-        for (int i = 0; i < N_THREADS; i++ ) {
-            threads[i] = new CountStackFramesAtExit();
-            int late_count = 1;
-            threads[i].start();
-            try {
-                // Wait for the worker thread to get going.
-                threads[i].startSyncObj.await();
-
-                // This interrupt() call will break the worker out of
-                // the exitSyncObj.await() call and the countStackFrames()
-                // calls will come in during thread exit.
-                threads[i].interrupt();
-                for (; late_count <= N_LATE_CALLS; late_count++) {
-                    try {
-                        threads[i].countStackFrames();
-                    } catch (IllegalThreadStateException itse) {
-                        // ignore because we expect it
-                    }
-
-                    if (!threads[i].isAlive()) {
-                        // Done with Thread.countStackFrames() calls since
-                        // thread is not alive.
-                        break;
-                    }
-                }
-            } catch (InterruptedException e) {
-                throw new Error("Unexpected: " + e);
-            }
-
-            System.out.println("INFO: thread #" + i + ": made " + late_count +
-                               " late calls to java.lang.Thread.countStackFrames()");
-            System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
-                               N_LATE_CALLS + " value is " +
-                               ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
-                               "large enough to cause a Thread.countStackFrames() " +
-                               "call after thread exit.");
-
-            try {
-                threads[i].join();
-            } catch (InterruptedException e) {
-                throw new Error("Unexpected: " + e);
-            }
-            threads[i].countStackFrames();
-            if (threads[i].isAlive()) {
-                throw new Error("Expected !Thread.isAlive() after thread #" +
-                                i + " has been join()'ed");
-            }
-        }
-
-        String cmd = System.getProperty("sun.java.command");
-        if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
-            // Exit with success in a non-JavaTest environment:
-            System.exit(0);
-        }
-    }
-}
--- a/test/hotspot/jtreg/serviceability/dcmd/vm/FlagsTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/hotspot/jtreg/serviceability/dcmd/vm/FlagsTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -34,7 +34,7 @@
  *          java.compiler
  *          java.management
  *          jdk.internal.jvmstat/sun.jvmstat.monitor
- * @run testng/othervm -Xmx129m -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions -XX:+ThereShouldNotBeAnyVMOptionNamedLikeThis_Right -XX:-TieredCompilation FlagsTest
+ * @run testng/othervm -Xmx129m -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions -XX:+ThereShouldNotBeAnyVMOptionNamedLikeThis_Right FlagsTest
  */
 public class FlagsTest {
     public void run(CommandExecutor executor) {
@@ -43,7 +43,6 @@
         /* The following are interpreted by the JVM as actual "flags" */
         output.shouldContain("-XX:+UnlockDiagnosticVMOptions");
         output.shouldContain("-XX:+IgnoreUnrecognizedVMOptions");
-        output.shouldContain("-XX:-TieredCompilation");
 
         /* The following are not */
         output.shouldNotContain("-Xmx129m");
--- a/test/jdk/ProblemList.txt	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/ProblemList.txt	Mon Nov 04 11:25:55 2019 +0000
@@ -773,6 +773,7 @@
 javax/swing/MultiUIDefaults/4300666/bug4300666.java 7105119 macosx-all
 javax/swing/UITest/UITest.java 8198392 generic-all
 javax/swing/plaf/basic/BasicComboBoxEditor/Test8015336.java 8198394 generic-all
+javax/swing/plaf/basic/BasicGraphicsUtils/8132119/bug8132119.java linux-all
 javax/swing/plaf/metal/MetalLookAndFeel/Test8039750.java 8198395 generic-all
 javax/swing/text/DevanagariEditor.java 8198397 generic-all
 javax/swing/SpringLayout/4726194/bug4726194.java 8198399 generic-all
@@ -884,7 +885,7 @@
 jdk/jfr/event/io/EvilInstrument.java                            8221331    generic-all
 jdk/jfr/event/runtime/TestNetworkUtilizationEvent.java          8228990,8229370    generic-all
 jdk/jfr/event/compiler/TestCodeSweeper.java                     8225209    generic-all
-jdk/jfr/event/oldobject/TestLargeRootSet.java                   8205651    generic-all
+jdk/jfr/api/consumer/recordingstream/TestSetStartTime.java      8233217    generic-all
 
 ############################################################################
 
--- a/test/jdk/com/sun/jdi/JdwpAttachTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/com/sun/jdi/JdwpAttachTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -32,6 +32,7 @@
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
+import java.net.UnknownHostException;
 import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.Iterator;
@@ -41,7 +42,6 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
 
 /*
  * @test
@@ -54,24 +54,36 @@
  */
 public class JdwpAttachTest {
 
+    private static final boolean IsWindows = System.getProperty("os.name").toLowerCase().contains("windows");
+
+    // Set to true to perform testing of attach from wrong address (expected to fail).
+    // It's off by default as it caused significant test time increase\
+    // (tests <number_of_addresses> * <number_of_addresses> cases, each case fails by timeout).
+    private static boolean testFailedAttach = false;
+
     public static void main(String[] args) throws Exception {
         List<InetAddress> addresses = getAddresses();
 
         boolean ipv4EnclosedTested = false;
         boolean ipv6EnclosedTested = false;
         for (InetAddress addr: addresses) {
-            // also test that addresses enclosed in square brackets are supported
-            attachTest(addr.getHostAddress(), addr.getHostAddress());
+            if (testFailedAttach) {
+                for (InetAddress connectAddr : addresses) {
+                    attachTest(addr.getHostAddress(), connectAddr.getHostAddress(), addr.equals(connectAddr));
+                }
+            } else {
+                attachTest(addr.getHostAddress(), addr.getHostAddress(), true);
+            }
             // listening on "*" should accept connections from all addresses
-            attachTest("*", addr.getHostAddress());
+            attachTest("*", addr.getHostAddress(), true);
 
-            // test that addresses enclosed in square brackets are supported.
+            // also test that addresses enclosed in square brackets are supported.
             if (addr instanceof Inet4Address && !ipv4EnclosedTested) {
-                attachTest("[" + addr.getHostAddress() + "]", "[" + addr.getHostAddress() + "]");
+                attachTest("[" + addr.getHostAddress() + "]", "[" + addr.getHostAddress() + "]", true);
                 ipv4EnclosedTested = true;
             }
             if (addr instanceof Inet6Address && !ipv6EnclosedTested) {
-                attachTest("[" + addr.getHostAddress() + "]", "[" + addr.getHostAddress() + "]");
+                attachTest("[" + addr.getHostAddress() + "]", "[" + addr.getHostAddress() + "]", true);
                 ipv6EnclosedTested = true;
             }
         }
@@ -80,13 +92,14 @@
         // we should be able to attach to both IPv4 and IPv6 addresses (127.0.0.1 & ::1)
         InetAddress localAddresses[] = InetAddress.getAllByName("localhost");
         for (int i = 0; i < localAddresses.length; i++) {
-            attachTest(localAddresses[i].getHostAddress(), "");
+            attachTest(localAddresses[i].getHostAddress(), "", true);
         }
     }
 
-    private static void attachTest(String listenAddress, String connectAddresses)
+    private static void attachTest(String listenAddress, String connectAddress, boolean expectedResult)
             throws Exception {
-        log("Starting listening at " + listenAddress);
+        log("\nTest: listen on '" + listenAddress + "', attach to '" + connectAddress + "'");
+        log("  Starting listening at " + listenAddress);
         ListeningConnector connector = getListenConnector();
         Map<String, Connector.Argument> args = connector.defaultArguments();
         setConnectorArg(args, "localAddress", listenAddress);
@@ -100,31 +113,48 @@
             throw new RuntimeException("values from connector.startListening (" + actualPort
                     + " is not equal to values from arguments (" + port + ")");
         }
-        log("Listening port: " + port);
+        log("  Listening port: " + port);
 
-        log("Attaching from " + connectAddresses);
+        log("  Attaching from " + connectAddress);
         try {
             ExecutorService executor = Executors.newSingleThreadExecutor();
             executor.submit((Callable<Exception>)() -> {
                 VirtualMachine vm = connector.accept(args);
-                log("ACCEPTED.");
                 vm.dispose();
                 return null;
             });
             executor.shutdown();
 
-            LingeredApp debuggee = LingeredApp.startApp(
-                    Arrays.asList("-agentlib:jdwp=transport=dt_socket"
-                                +",address=" + connectAddresses + ":" + port
-                                + ",server=n,suspend=n"));
-            debuggee.stopApp();
-
-            executor.awaitTermination(20, TimeUnit.SECONDS);
+            try {
+                LingeredApp debuggee = LingeredApp.startApp(
+                        Arrays.asList("-agentlib:jdwp=transport=dt_socket"
+                                + ",address=" + connectAddress + ":" + port
+                                + ",server=n,suspend=n"
+                                // if failure is expected set small timeout (default is 20 sec)
+                                + (!expectedResult ? ",timeout=1000" : "")));
+                debuggee.stopApp();
+                if (expectedResult) {
+                    log("OK: attached as expected");
+                } else {
+                    throw new RuntimeException("ERROR: LingeredApp.startApp was able to attach");
+                }
+            } catch (Exception ex) {
+                if (expectedResult) {
+                    throw new RuntimeException("ERROR: LingeredApp.startApp was able to attach");
+                } else {
+                    log("OK: failed to attach as expected");
+                }
+            }
         } finally {
             connector.stopListening(args);
         }
     }
 
+    private static void addAddr(List<InetAddress> list, InetAddress addr) {
+        log(" - (" + addr.getClass().getSimpleName() + ") " + addr.getHostAddress());
+        list.add(addr);
+    }
+
     private static List<InetAddress> getAddresses() {
         List<InetAddress> result = new LinkedList<>();
         try {
@@ -136,17 +166,37 @@
                         Enumeration<InetAddress> addresses = iface.getInetAddresses();
                         while (addresses.hasMoreElements()) {
                             InetAddress addr = addresses.nextElement();
-                            // Java reports link local addresses with named scope,
-                            // but Windows sockets routines support only numeric scope id.
-                            // skip such addresses.
+                            // Java reports link local addresses with symbolic scope,
+                            // but on Windows java.net.NetworkInterface generates its own scope names
+                            // which are incompatible with native Windows routines.
+                            // So on Windows test only addresses with numeric scope.
+                            // On other platforms test both symbolic and numeric scopes.
                             if (addr instanceof Inet6Address) {
                                 Inet6Address addr6 = (Inet6Address)addr;
-                                if (addr6.getScopedInterface() != null) {
-                                    continue;
+                                NetworkInterface scopeIface = addr6.getScopedInterface();
+                                if (scopeIface != null && scopeIface.getName() != null) {
+                                    // On some test machines VPN creates link local addresses
+                                    // which we cannot connect to.
+                                    // Skip them.
+                                    if (scopeIface.isPointToPoint()) {
+                                        continue;
+                                    }
+
+                                    try {
+                                        // the same address with numeric scope
+                                        addAddr(result, Inet6Address.getByAddress(null, addr6.getAddress(), addr6.getScopeId()));
+                                    } catch (UnknownHostException e) {
+                                        // cannot happen!
+                                        throw new RuntimeException("Unexpected", e);
+                                    }
+
+                                    if (IsWindows) {
+                                        // don't add addresses with symbolic scope
+                                        continue;
+                                    }
                                 }
                             }
-                            log(" - (" + addr.getClass().getSimpleName() + ") " + addr.getHostAddress());
-                            result.add(addr);
+                            addAddr(result, addr);
                         }
                     }
                 } catch (SocketException e) {
@@ -184,8 +234,11 @@
         arg.setValue(value);
     }
 
+    private static long startTime = System.currentTimeMillis();
+
     private static void log(Object o) {
-        System.out.println(String.valueOf(o));
+        long time = System.currentTimeMillis() - startTime;
+        System.out.println(String.format("[%7.3f] %s", (time / 1000f), String.valueOf(o)));
     }
 
 }
--- a/test/jdk/com/sun/jdi/JdwpListenTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/com/sun/jdi/JdwpListenTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -34,6 +34,7 @@
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
+import java.net.UnknownHostException;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -51,6 +52,8 @@
  */
 public class JdwpListenTest {
 
+    private static final boolean IsWindows = System.getProperty("os.name").toLowerCase().contains("windows");
+
     public static void main(String[] args) throws Exception {
         List<InetAddress> addresses = getAddresses();
 
@@ -60,6 +63,8 @@
             for (InetAddress attach: addresses) {
                 // can connect only from the same address
                 // IPv6 cannot connect to IPv4 (::1 to 127.0.0.1) and vice versa.
+                // Note: for IPv6 addresses equals() does not compare scopes
+                // (so addresses with symbolic and numeric scopes are equals).
                 listenTest(listen.getHostAddress(), attach.getHostAddress(), attach.equals(listen));
             }
             // test that addresses enclosed in square brackets are supported.
@@ -80,6 +85,8 @@
 
     private static void listenTest(String listenAddress, String connectAddress, boolean expectedResult)
             throws IOException {
+        log("\nTest: listen at " + listenAddress + ", attaching from " + connectAddress
+                + ", expected: " + (expectedResult ? "SUCCESS" : "FAILURE"));
         log("Starting listening debuggee at " + listenAddress);
         try (Debuggee debuggee = Debuggee.launcher("HelloWorld").setAddress(listenAddress + ":0").launch()) {
             log("Debuggee is listening on " + listenAddress + ":" + debuggee.getAddress());
@@ -98,6 +105,11 @@
         }
     }
 
+    private static void addAddr(List<InetAddress> list, InetAddress addr) {
+        log(" - (" + addr.getClass().getSimpleName() + ") " + addr.getHostAddress());
+        list.add(addr);
+    }
+
     private static List<InetAddress> getAddresses() {
         List<InetAddress> result = new LinkedList<>();
         try {
@@ -109,17 +121,37 @@
                         Enumeration<InetAddress> addresses = iface.getInetAddresses();
                         while (addresses.hasMoreElements()) {
                             InetAddress addr = addresses.nextElement();
-                            // Java reports link local addresses with named scope,
-                            // but Windows sockets routines support only numeric scope id.
-                            // skip such addresses.
+                            // Java reports link local addresses with symbolic scope,
+                            // but on Windows java.net.NetworkInterface generates its own scope names
+                            // which are incompatible with native Windows routines.
+                            // So on Windows test only addresses with numeric scope.
+                            // On other platforms test both symbolic and numeric scopes.
                             if (addr instanceof Inet6Address) {
                                 Inet6Address addr6 = (Inet6Address)addr;
-                                if (addr6.getScopedInterface() != null) {
-                                    continue;
+                                NetworkInterface scopeIface = addr6.getScopedInterface();
+                                if (scopeIface != null && scopeIface.getName() != null) {
+                                    // On some test machines VPN creates link local addresses
+                                    // which we cannot connect to.
+                                    // Skip them.
+                                    if (scopeIface.isPointToPoint()) {
+                                        continue;
+                                    }
+
+                                    try {
+                                        // the same address with numeric scope
+                                        addAddr(result, Inet6Address.getByAddress(null, addr6.getAddress(), addr6.getScopeId()));
+                                    } catch (UnknownHostException e) {
+                                        // cannot happen!
+                                        throw new RuntimeException("Unexpected", e);
+                                    }
+
+                                    if (IsWindows) {
+                                        // don't add addresses with symbolic scope
+                                        continue;
+                                    }
                                 }
                             }
-                            log(" - (" + addr.getClass().getSimpleName() + ") " + addr.getHostAddress());
-                            result.add(addr);
+                            addAddr(result, addr);
                         }
                     }
                 } catch (SocketException e) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/Thread/CountStackFrames.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/* @test
+ * @bug 8205132
+ * @summary Test Thread.countStackFrames()
+ * @run testng CountStackFrames
+ */
+
+import org.testng.annotations.Test;
+
+public class CountStackFrames {
+
+    // current thread
+    @Test(expectedExceptions = UnsupportedOperationException.class)
+    public void testCurrentThread() {
+        Thread.currentThread().countStackFrames();
+    }
+
+    // unstarted thread
+    @Test(expectedExceptions = UnsupportedOperationException.class)
+    public void testUnstartedThread() {
+        Thread thread = new Thread(() -> { });
+        thread.countStackFrames();
+    }
+
+    // terminated thread
+    @Test(expectedExceptions = UnsupportedOperationException.class)
+    public void testTerminatedThread() throws Exception {
+        Thread thread = new Thread(() -> { });
+        thread.start();
+        thread.join();
+        thread.countStackFrames();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/lookup/LookupClassTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8173975
+ * @summary Lookup::in throws IAE if the target class is a primitive class or array class
+ * @run testng/othervm LookupClassTest
+ */
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class LookupClassTest {
+    private static final LookupClassTest[] ARRAY = new LookupClassTest[0];
+    @BeforeTest
+    public void test() {
+        assertTrue(ARRAY.getClass().isArray());
+        assertSamePackage(MethodHandles.lookup(), ARRAY.getClass());
+        assertSamePackage(MethodHandles.publicLookup(), int.class);
+    }
+
+    private void assertSamePackage(Lookup lookup, Class<?> targetClass) {
+        assertEquals(lookup.lookupClass().getPackageName(), targetClass.getPackageName());
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void arrayLookupClass() {
+        Lookup lookup = MethodHandles.lookup();
+        lookup.in(ARRAY.getClass());
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void primitiveLookupClass() {
+        Lookup lookup = MethodHandles.publicLookup();
+        lookup.in(int.class);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void voidLookupClass() {
+        Lookup lookup = MethodHandles.publicLookup();
+        lookup.in(void.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/HttpURLConnection/HttpURLConnUsingProxy.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/* @test
+ * @bug 8231632
+ * @summary HttpURLConnection::usingProxy could specify that it lazily evaluates the fact
+ * @modules java.base/sun.net.www
+ * @library /test/lib
+ * @run main/othervm HttpURLConnUsingProxy
+ */
+
+import java.io.*;
+import java.net.*;
+import java.nio.charset.StandardCharsets;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import jdk.test.lib.net.URIBuilder;
+
+public class HttpURLConnUsingProxy {
+    static HttpServer server;
+    static Proxy proxy;
+    static InetSocketAddress isa;
+
+    static class Handler implements HttpHandler {
+
+        @Override
+        public void handle(HttpExchange exchange) throws IOException {
+            byte[] response = "Hello World!".getBytes(StandardCharsets.UTF_8);
+            try (InputStream req = exchange.getRequestBody()) {
+                req.readAllBytes();
+            }
+            exchange.sendResponseHeaders(200, response.length);
+            try (OutputStream resp = exchange.getResponseBody()) {
+                resp.write(response);
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        try {
+            InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
+            InetSocketAddress addr = new InetSocketAddress(loopbackAddress, 0);
+            server = HttpServer.create(addr, 0);
+            server.createContext("/HttpURLConnUsingProxy/http1/", new Handler());
+            server.start();
+
+            ProxyServer pserver = new ProxyServer(loopbackAddress,
+                    server.getAddress().getPort());
+            // Start proxy server
+            new Thread(pserver).start();
+
+            URL url = URIBuilder.newBuilder()
+                    .scheme("http")
+                    .loopback()
+                    .port(server.getAddress().getPort())
+                    .path("/HttpURLConnUsingProxy/http1/x.html")
+                    .toURLUnchecked();
+
+            // NO_PROXY
+            try {
+                HttpURLConnection urlc =
+                        (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
+                assertEqual(urlc.usingProxy(), false);
+                urlc.getResponseCode();
+                assertEqual(urlc.usingProxy(), false);
+                urlc.disconnect();
+            } catch (IOException ioe) {
+                throw new RuntimeException("Direct connection should succeed: "
+                        + ioe.getMessage());
+            }
+
+            // Non-existing proxy
+            try {
+                isa = InetSocketAddress.createUnresolved("inexistent", 8080);
+                proxy = new Proxy(Proxy.Type.HTTP, isa);
+                HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
+                assertEqual(urlc.usingProxy(), true);
+                InputStream is = urlc.getInputStream();
+                is.close();
+                throw new RuntimeException("Non-existing proxy should cause IOException");
+            } catch (IOException ioe) {
+                // expected
+            }
+
+            // Normal proxy settings
+            try {
+                isa = InetSocketAddress.createUnresolved(loopbackAddress.getHostAddress(),
+                        pserver.getPort());
+                proxy = new Proxy(Proxy.Type.HTTP, isa);
+                HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
+                assertEqual(urlc.usingProxy(), true);
+                urlc.getResponseCode();
+                assertEqual(urlc.usingProxy(), true);
+                urlc.disconnect();
+            } catch (IOException ioe) {
+                throw new RuntimeException("Connection through local proxy should succeed: "
+                        + ioe.getMessage());
+            }
+
+            // Reuse proxy with new HttpURLConnection
+            try {
+                HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
+                assertEqual(urlc.usingProxy(), true);
+                urlc.getResponseCode();
+                assertEqual(urlc.usingProxy(), true);
+                read(urlc.getInputStream());
+                assertEqual(urlc.usingProxy(), true);
+            } catch (IOException ioe) {
+                throw new RuntimeException("Connection through local proxy should succeed: "
+                        + ioe.getMessage());
+            }
+
+            // Reuse proxy with existing HttpURLConnection
+            try {
+                HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
+                assertEqual(urlc.usingProxy(), true);
+                urlc.getResponseCode();
+                assertEqual(urlc.usingProxy(), true);
+                read(urlc.getInputStream());
+                assertEqual(urlc.usingProxy(), true);
+                urlc.disconnect();
+            } catch (IOException ioe) {
+                throw new RuntimeException("Connection through local proxy should succeed: "
+                        + ioe.getMessage());
+            }
+
+            // ProxySelector with normal proxy settings
+            try {
+                ProxySelector.setDefault(ProxySelector.of(isa));
+                HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
+                assertEqual(urlc.usingProxy(), false);
+                urlc.getResponseCode();
+                assertEqual(urlc.usingProxy(), true);
+                read(urlc.getInputStream());
+                assertEqual(urlc.usingProxy(), true);
+                urlc.disconnect();
+                assertEqual(urlc.usingProxy(), true);
+            } catch (IOException ioe) {
+                throw new RuntimeException("Connection through local proxy should succeed: "
+                        + ioe.getMessage());
+            }
+
+            // ProxySelector with proxying disabled
+            try {
+                ProxySelector.setDefault(ProxySelector.of(null));
+                HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
+                assertEqual(urlc.usingProxy(), false);
+                urlc.getResponseCode();
+                assertEqual(urlc.usingProxy(), false);
+                read(urlc.getInputStream());
+                assertEqual(urlc.usingProxy(), false);
+            } catch (IOException ioe) {
+                throw new RuntimeException("Direct connection should succeed: "
+                        + ioe.getMessage());
+            }
+
+            // ProxySelector overwritten
+            try {
+                ProxySelector.setDefault(ProxySelector.of(isa));
+                HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
+                assertEqual(urlc.usingProxy(), false);
+                urlc.disconnect();
+            } catch (IOException ioe) {
+                throw new RuntimeException("Direct connection should succeed: "
+                        + ioe.getMessage());
+            }
+
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (server != null) {
+                server.stop(0);
+            }
+        }
+    }
+
+    static class ProxyServer extends Thread {
+        private static ServerSocket ss = null;
+
+        // Client requesting a tunnel
+        private Socket clientSocket = null;
+
+        /*
+         * Origin server's address and port that the client
+         * wants to establish the tunnel for communication.
+         */
+        private InetAddress serverInetAddr;
+        private int serverPort;
+
+        public ProxyServer(InetAddress server, int port) throws IOException {
+            serverInetAddr = server;
+            serverPort = port;
+            ss = new ServerSocket(0, 0, InetAddress.getLoopbackAddress());
+        }
+
+        public void run() {
+            while (true) {
+                try {
+                    clientSocket = ss.accept();
+                    processRequests();
+                } catch (Exception e) {
+                    System.out.println("Proxy failed: " + e);
+                    e.printStackTrace();
+                    try {
+                        ss.close();
+                    } catch (IOException ioe) {
+                        System.out.println("ProxyServer close error: " + ioe);
+                        ioe.printStackTrace();
+                    }
+                }
+            }
+        }
+
+        private void processRequests() throws Exception {
+            // Connection set to tunneling mode
+
+            Socket serverSocket = new Socket(serverInetAddr, serverPort);
+            ProxyTunnel clientToServer = new ProxyTunnel(
+                    clientSocket, serverSocket);
+            ProxyTunnel serverToClient = new ProxyTunnel(
+                    serverSocket, clientSocket);
+            clientToServer.start();
+            serverToClient.start();
+            System.out.println("Proxy: Started tunneling...");
+
+            clientToServer.join();
+            serverToClient.join();
+            System.out.println("Proxy: Finished tunneling...");
+
+            clientToServer.close();
+            serverToClient.close();
+        }
+
+        /**
+         * **************************************************************
+         * Helper methods follow
+         * **************************************************************
+         */
+        public int getPort() {
+            return ss.getLocalPort();
+        }
+
+        /*
+         * This inner class provides unidirectional data flow through the sockets
+         * by continuously copying bytes from input socket to output socket
+         * while both sockets are open and EOF has not been received.
+         */
+        static class ProxyTunnel extends Thread {
+            Socket sockIn;
+            Socket sockOut;
+            InputStream input;
+            OutputStream output;
+
+            public ProxyTunnel(Socket sockIn, Socket sockOut) throws Exception {
+                this.sockIn = sockIn;
+                this.sockOut = sockOut;
+                input = sockIn.getInputStream();
+                output = sockOut.getOutputStream();
+            }
+
+            public void run() {
+                int BUFFER_SIZE = 400;
+                byte[] buf = new byte[BUFFER_SIZE];
+                int bytesRead = 0;
+                int count = 0;    // Keep track of amount of data transferred
+
+                try {
+                    while ((bytesRead = input.read(buf)) >= 0) {
+                        output.write(buf, 0, bytesRead);
+                        output.flush();
+                        count += bytesRead;
+                    }
+                } catch (IOException e) {
+                    /*
+                     * Peer end has closed connection
+                     * so we close tunnel
+                     */
+                    close();
+                }
+            }
+
+            public void close() {
+                try {
+                    if (!sockIn.isClosed())
+                        sockIn.close();
+                    if (!sockOut.isClosed())
+                        sockOut.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+    }
+
+    private static void assertEqual(boolean usingProxy, boolean expected) {
+        if (usingProxy != expected) {
+            throw new RuntimeException("Expected: " + expected
+                    + " but usingProxy returned: " + usingProxy);
+        }
+    }
+
+    private static String read(InputStream inputStream) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        BufferedReader bufferedReader = new BufferedReader(
+                new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+        int i = bufferedReader.read();
+        while (i != -1) {
+            sb.append((char) i);
+            i = bufferedReader.read();
+        }
+        bufferedReader.close();
+        return sb.toString();
+    }
+}
--- a/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/SubscriberWhiteboxVerification.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/net/httpclient/reactivestreams-tck/org/reactivestreams/tck/SubscriberWhiteboxVerification.java	Mon Nov 04 11:25:55 2019 +0000
@@ -278,8 +278,9 @@
       @Override
       public void run(WhiteboxTestStage stage) throws InterruptedException {
         stage.puppet().triggerRequest(1);
+        stage.expectRequest();
         stage.puppet().signalCancel();
-        stage.expectRequest();
+        stage.expectCancelling();
         stage.signalNext();
 
         stage.puppet().triggerRequest(1);
@@ -824,11 +825,17 @@
      * Before sending any element to the subscriber, the TCK must wait for the subscriber to request that element, and
      * must be prepared for the subscriber to only request one element at a time, it is not enough for the TCK to
      * simply invoke this method before sending elements.
+     * <p>
+     * An invocation of {@link #signalCancel()} may be coalesced into any elements that have not yet been requested,
+     * such that only a cancel signal is emitted.
      */
     void triggerRequest(long elements);
 
     /**
-     * Trigger {@code cancel()} on your {@link Subscriber}
+     * Trigger {@code cancel()} on your {@link Subscriber}.
+     * <p>
+     * An invocation of this method may be coalesced into any outstanding requests, as requested by
+     *{@link #triggerRequest(long)}, such that only a cancel signal is emitted.
      */
     void signalCancel();
   }
--- a/test/jdk/java/nio/channels/DatagramChannel/AdaptDatagramSocket.java	Wed Oct 30 12:25:22 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-/*
- * Copyright (c) 2001, 2018, 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.
- */
-
-/* @test
- * @bug 4313882 4981129 8143610
- * @summary Unit test for datagram-socket-channel adaptors
- * @library .. /test/lib
- * @build jdk.test.lib.Utils TestServers
- * @run main AdaptDatagramSocket
- * @key randomness
- */
-
-import java.net.*;
-import java.nio.channels.*;
-import java.util.*;
-
-
-public class AdaptDatagramSocket {
-
-    static java.io.PrintStream out = System.out;
-    static Random rand = new Random();
-
-    static String toString(DatagramPacket dp) {
-        return ("DatagramPacket[off=" + dp.getOffset()
-                + ", len=" + dp.getLength()
-                + "]");
-    }
-
-    static void test(DatagramSocket ds, InetSocketAddress dst,
-                     boolean shouldTimeout)
-        throws Exception
-    {
-        DatagramPacket op = new DatagramPacket(new byte[100], 13, 42, dst);
-        rand.nextBytes(op.getData());
-        DatagramPacket ip = new DatagramPacket(new byte[100], 19, 100 - 19);
-        out.println("pre  op: " + toString(op) + "  ip: " + toString(ip));
-
-        long start = System.currentTimeMillis();
-        ds.send(op);
-
-        for (;;) {
-            try {
-                ds.receive(ip);
-                if (ip.getLength() == 0) { // ## Not sure why this happens
-                    ip.setLength(100 - 19);
-                    continue;
-                }
-            } catch (SocketTimeoutException x) {
-                if (shouldTimeout) {
-                    out.println("Receive timed out, as expected");
-                    return;
-                }
-                throw x;
-            }
-            break;
-        }
-
-        out.println("rtt: " + (System.currentTimeMillis() - start));
-        out.println("post op: " + toString(op) + "  ip: " + toString(ip));
-
-        for (int i = 0; i < ip.getLength(); i++) {
-            if (ip.getData()[ip.getOffset() + i]
-                != op.getData()[op.getOffset() + i])
-                throw new Exception("Incorrect data received");
-        }
-
-        if (!(ip.getSocketAddress().equals(dst))) {
-            throw new Exception("Incorrect sender address, expected: " + dst
-                + " actual: " + ip.getSocketAddress());
-        }
-    }
-
-    static void test(InetSocketAddress dst,
-                     int timeout, boolean shouldTimeout,
-                     boolean connect)
-        throws Exception
-    {
-        out.println();
-        out.println("dst: " + dst);
-
-        DatagramSocket ds;
-        if (false) {
-            // Original
-            ds = new DatagramSocket();
-        } else {
-            DatagramChannel dc = DatagramChannel.open();
-            ds = dc.socket();
-            ds.bind(new InetSocketAddress(0));
-        }
-
-        out.println("socket: " + ds);
-        if (connect) {
-            ds.connect(dst);
-            out.println("connect: " + ds);
-        }
-        InetSocketAddress src = new InetSocketAddress(ds.getLocalAddress(),
-                                                      ds.getLocalPort());
-        out.println("src: " + src);
-
-        if (timeout > 0)
-            ds.setSoTimeout(timeout);
-        out.println("timeout: " + ds.getSoTimeout());
-
-        for (int i = 0; i < 5; i++) {
-            test(ds, dst, shouldTimeout);
-        }
-
-        // Leave the socket open so that we don't reuse the old src address
-        //ds.close();
-
-    }
-
-    public static void main(String[] args) throws Exception {
-        // need an UDP echo server
-        try (TestServers.UdpEchoServer echoServer
-                = TestServers.UdpEchoServer.startNewServer(100)) {
-            final InetSocketAddress address
-                = new InetSocketAddress(echoServer.getAddress(),
-                                        echoServer.getPort());
-            test(address, 0, false, false);
-            test(address, 0, false, true);
-            test(address, Integer.MAX_VALUE, false, false);
-        }
-        try (TestServers.UdpDiscardServer discardServer
-                = TestServers.UdpDiscardServer.startNewServer()) {
-            final InetSocketAddress address
-                = new InetSocketAddress(discardServer.getAddress(),
-                                        discardServer.getPort());
-            test(address, 10, true, false);
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorBasic.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2001, 2019, 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.
+ */
+
+/* @test
+ * @bug 4313882 4981129 8143610 8232673
+ * @summary Unit test for datagram-socket-channel adaptors
+ * @modules java.base/java.net:+open
+ * @library .. /test/lib
+ * @build jdk.test.lib.Utils TestServers
+ * @run main AdaptorBasic
+ * @key randomness
+ */
+
+import java.net.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.lang.reflect.Field;
+
+
+public class AdaptorBasic {
+
+    static java.io.PrintStream out = System.out;
+    static Random rand = new Random();
+
+    static String toString(DatagramPacket dp) {
+        return ("DatagramPacket[off=" + dp.getOffset()
+                + ", len=" + dp.getLength()
+                + "]");
+    }
+
+    static int getBufLength(DatagramPacket p) throws Exception {
+        Field f = DatagramPacket.class.getDeclaredField("bufLength");
+        f.setAccessible(true);
+        return (int) f.get(p);
+    }
+
+    static void test(DatagramSocket ds, InetSocketAddress dst, boolean shouldTimeout)
+        throws Exception
+    {
+        DatagramPacket op = new DatagramPacket(new byte[100], 13, 42, dst);
+        rand.nextBytes(op.getData());
+        int bufLength = 100 - 19;
+        DatagramPacket ip = new DatagramPacket(new byte[100], 19, bufLength);
+        out.println("pre  op: " + toString(op) + "  ip: " + toString(ip));
+
+        long start = System.currentTimeMillis();
+        ds.send(op);
+
+        for (;;) {
+            try {
+                ds.receive(ip);
+            } catch (SocketTimeoutException x) {
+                if (shouldTimeout) {
+                    out.println("Receive timed out, as expected");
+                    return;
+                }
+                throw x;
+            }
+            break;
+        }
+
+        out.println("rtt: " + (System.currentTimeMillis() - start));
+        out.println("post op: " + toString(op) + "  ip: " + toString(ip));
+
+        for (int i = 0; i < ip.getLength(); i++) {
+            if (ip.getData()[ip.getOffset() + i]
+                != op.getData()[op.getOffset() + i])
+                throw new Exception("Incorrect data received");
+        }
+
+        if (!(ip.getSocketAddress().equals(dst))) {
+            throw new Exception("Incorrect sender address, expected: " + dst
+                + " actual: " + ip.getSocketAddress());
+        }
+
+        if (getBufLength(ip) != bufLength) {
+            throw new Exception("DatagramPacket bufLength changed by receive!!!");
+        }
+    }
+
+    static void test(InetSocketAddress dst,
+                     int timeout, boolean shouldTimeout,
+                     boolean connect)
+        throws Exception
+    {
+        out.println();
+        out.println("dst: " + dst);
+
+        DatagramSocket ds;
+        if (false) {
+            // Original
+            ds = new DatagramSocket();
+        } else {
+            DatagramChannel dc = DatagramChannel.open();
+            ds = dc.socket();
+            ds.bind(new InetSocketAddress(0));
+        }
+
+        out.println("socket: " + ds);
+        if (connect) {
+            ds.connect(dst);
+            out.println("connect: " + ds);
+        }
+        InetSocketAddress src = new InetSocketAddress(ds.getLocalAddress(),
+                                                      ds.getLocalPort());
+        out.println("src: " + src);
+
+        if (timeout > 0)
+            ds.setSoTimeout(timeout);
+        out.println("timeout: " + ds.getSoTimeout());
+
+        for (int i = 0; i < 5; i++) {
+            test(ds, dst, shouldTimeout);
+        }
+
+        // Leave the socket open so that we don't reuse the old src address
+        //ds.close();
+
+    }
+
+    public static void main(String[] args) throws Exception {
+        // need an UDP echo server
+        try (TestServers.UdpEchoServer echoServer
+                = TestServers.UdpEchoServer.startNewServer(100)) {
+            final InetSocketAddress address
+                = new InetSocketAddress(echoServer.getAddress(),
+                                        echoServer.getPort());
+            test(address, 0, false, false);
+            test(address, 0, false, true);
+            test(address, Integer.MAX_VALUE, false, false);
+        }
+        try (TestServers.UdpDiscardServer discardServer
+                = TestServers.UdpDiscardServer.startNewServer()) {
+            final InetSocketAddress address
+                = new InetSocketAddress(discardServer.getAddress(),
+                                        discardServer.getPort());
+            test(address, 10, true, false);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConcurrentIO.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/* @test
+ * @bug 8232673
+ * @summary Test DatagramChannel socket adaptor with concurrent send/receive
+ */
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.channels.DatagramChannel;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class AdaptorConcurrentIO {
+
+    public static void main(String[] args) throws Exception {
+        testConcurrentSendReceive(0);
+        testConcurrentSendReceive(60_000);
+    }
+
+    /**
+     * Starts a task that blocks in the adaptor's receive method, then invokes
+     * the adaptor's send method to send a datagram. If the adaptor were using
+     * the channel's blockingLock then send without be blocked waiting for
+     * the receive to complete.
+     */
+    static void testConcurrentSendReceive(int timeout) throws Exception {
+        try (DatagramChannel dc = DatagramChannel.open()) {
+            InetAddress lb = InetAddress.getLoopbackAddress();
+            dc.bind(new InetSocketAddress(lb, 0));
+            DatagramSocket s = dc.socket();
+            s.setSoTimeout(timeout);
+
+            ExecutorService pool = Executors.newSingleThreadExecutor();
+            try {
+                Future<String> result = pool.submit(() -> {
+                    byte[] data = new byte[100];
+                    DatagramPacket p = new DatagramPacket(data, 0, data.length);
+                    s.receive(p);
+                    return new String(p.getData(), p.getOffset(), p.getLength(), "UTF-8");
+                });
+
+                Thread.sleep(200); // give chance for thread to block
+
+                byte[] data = "hello".getBytes("UTF-8");
+                DatagramPacket p = new DatagramPacket(data, 0, data.length);
+                p.setSocketAddress(s.getLocalSocketAddress());
+                s.send(p);
+
+                String msg = result.get();
+                if (!msg.equals("hello"))
+                    throw new RuntimeException("Unexpected message: " + msg);
+            } finally {
+                pool.shutdown();
+            }
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/* @test
+ * @bug 8232673
+ * @summary Test DatagramChannel socket adaptor connect method with illegal args
+ * @run testng AdaptorConnect
+ */
+
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.DatagramChannel;
+import static java.net.InetAddress.getLoopbackAddress;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class AdaptorConnect {
+
+    /**
+     * Invoke the given socket's connect method with illegal arguments.
+     */
+    private void testConnectWithIllegalArguments(DatagramSocket s) {
+        assertThrows(IllegalArgumentException.class, () -> s.connect(null));
+        assertThrows(IllegalArgumentException.class, () -> s.connect(null, 7000));
+        assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), -1));
+        assertThrows(IllegalArgumentException.class, () -> s.connect(getLoopbackAddress(), 100_000));
+
+        SocketAddress sillyAddress = new SocketAddress() { };
+        assertThrows(IllegalArgumentException.class, () -> s.connect(sillyAddress));
+
+        SocketAddress unresolved = InetSocketAddress.createUnresolved("foo", 7777);
+        assertThrows(SocketException.class, () -> s.connect(unresolved));
+    }
+
+    /**
+     * Test connect method with an open socket.
+     */
+    public void testOpenSocket() throws Exception {
+        try (DatagramChannel dc = DatagramChannel.open()) {
+            DatagramSocket s = dc.socket();
+
+            testConnectWithIllegalArguments(s);
+
+            // should not be bound or connected
+            assertTrue(s.getLocalSocketAddress() == null);
+            assertTrue(s.getRemoteSocketAddress() == null);
+
+            // connect(SocketAddress)
+            var remote1 = new InetSocketAddress(getLoopbackAddress(), 7001);
+            s.connect(remote1);
+            assertEquals(s.getRemoteSocketAddress(), remote1);
+            testConnectWithIllegalArguments(s);
+            assertEquals(s.getRemoteSocketAddress(), remote1);
+
+            // connect(SocketAddress)
+            var remote2 = new InetSocketAddress(getLoopbackAddress(), 7002);
+            s.connect(remote2);
+            assertEquals(s.getRemoteSocketAddress(), remote2);
+            testConnectWithIllegalArguments(s);
+            assertEquals(s.getRemoteSocketAddress(), remote2);
+
+            // connect(InetAddress, int)
+            var remote3 = new InetSocketAddress(getLoopbackAddress(), 7003);
+            s.connect(remote3.getAddress(), remote3.getPort());
+            assertEquals(s.getRemoteSocketAddress(), remote3);
+            testConnectWithIllegalArguments(s);
+            assertEquals(s.getRemoteSocketAddress(), remote3);
+
+            // connect(InetAddress, int)
+            var remote4 = new InetSocketAddress(getLoopbackAddress(), 7004);
+            s.connect(remote4.getAddress(), remote4.getPort());
+            assertEquals(s.getRemoteSocketAddress(), remote4);
+            testConnectWithIllegalArguments(s);
+            assertEquals(s.getRemoteSocketAddress(), remote4);
+        }
+    }
+
+    /**
+     * Test connect method with a closed socket.
+     */
+    public void testClosedSocket() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        dc.close();
+
+        testConnectWithIllegalArguments(s);
+
+        // connect does not throw an exception when closed
+        var remote = new InetSocketAddress(getLoopbackAddress(), 7001);
+        s.connect(remote);
+        s.connect(remote.getAddress(), remote.getPort());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/* @test
+ * @bug 8232673
+ * @summary Test the DatagramChannel socket adaptor getter methods
+ * @run testng AdaptorGetters
+ */
+
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.channels.DatagramChannel;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class AdaptorGetters {
+
+    /**
+     * Test getters on unbound socket, before and after it is closed.
+     */
+    public void testUnboundSocket() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        try {
+
+            // state
+            assertFalse(s.isBound());
+            assertFalse(s.isConnected());
+            assertFalse(s.isClosed());
+
+            // local address
+            assertTrue(s.getLocalAddress().isAnyLocalAddress());
+            assertTrue(s.getLocalPort() == 0);
+            assertTrue(s.getLocalSocketAddress() == null);
+
+            // remote address
+            assertTrue(s.getInetAddress() == null);
+            assertTrue(s.getPort() == -1);
+
+        } finally {
+            dc.close();
+        }
+
+        // state
+        assertFalse(s.isBound());
+        assertFalse(s.isConnected());
+        assertTrue(s.isClosed());
+
+        // local address
+        assertTrue(s.getLocalAddress() == null);
+        assertTrue(s.getLocalPort() == -1);
+        assertTrue(s.getLocalSocketAddress() == null);
+
+        // remote address
+        assertTrue(s.getInetAddress() == null);
+        assertTrue(s.getPort() == -1);
+        assertTrue((s.getRemoteSocketAddress() == null));
+    }
+
+    /**
+     * Test getters on bound socket, before and after it is closed.
+     */
+    public void testBoundSocket() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        try {
+            dc.bind(new InetSocketAddress(0));
+            var localAddress = (InetSocketAddress) dc.getLocalAddress();
+
+            // state
+            assertTrue(s.isBound());
+            assertFalse(s.isConnected());
+            assertFalse(s.isClosed());
+
+            // local address
+            assertEquals(s.getLocalAddress(), localAddress.getAddress());
+            assertTrue(s.getLocalPort() == localAddress.getPort());
+            assertEquals(s.getLocalSocketAddress(), localAddress);
+
+            // remote address
+            assertTrue(s.getInetAddress() == null);
+            assertTrue(s.getPort() == -1);
+            assertTrue((s.getRemoteSocketAddress() == null));
+
+        } finally {
+            dc.close();
+        }
+
+        // state
+        assertTrue(s.isBound());
+        assertFalse(s.isConnected());
+        assertTrue(s.isClosed());
+
+        // local address
+        assertTrue(s.getLocalAddress() == null);
+        assertTrue(s.getLocalPort() == -1);
+        assertTrue(s.getLocalSocketAddress() == null);
+
+        // remote address
+        assertTrue(s.getInetAddress() == null);
+        assertTrue(s.getPort() == -1);
+        assertTrue((s.getRemoteSocketAddress() == null));
+    }
+
+    /**
+     * Test getters on connected socket, before and after it is closed.
+     */
+    public void testConnectedSocket() throws Exception {
+        var loopback = InetAddress.getLoopbackAddress();
+        var remoteAddress = new InetSocketAddress(loopback, 7777);
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        try {
+            dc.connect(remoteAddress);
+            var localAddress = (InetSocketAddress) dc.getLocalAddress();
+
+            // state
+            assertTrue(s.isBound());
+            assertTrue(s.isConnected());
+            assertFalse(s.isClosed());
+
+            // local address
+            assertEquals(s.getLocalAddress(), localAddress.getAddress());
+            assertTrue(s.getLocalPort() == localAddress.getPort());
+            assertEquals(s.getLocalSocketAddress(), localAddress);
+
+            // remote address
+            assertEquals(s.getInetAddress(), remoteAddress.getAddress());
+            assertTrue(s.getPort() == remoteAddress.getPort());
+            assertEquals(s.getRemoteSocketAddress(), remoteAddress);
+
+        } finally {
+            dc.close();
+        }
+
+        // state
+        assertTrue(s.isBound());
+        assertTrue(s.isConnected());
+        assertTrue(s.isClosed());
+
+        // local address
+        assertTrue(s.getLocalAddress() == null);
+        assertTrue(s.getLocalPort() == -1);
+        assertTrue(s.getLocalSocketAddress() == null);
+
+        // remote address
+        assertEquals(s.getInetAddress(), remoteAddress.getAddress());
+        assertTrue(s.getPort() == remoteAddress.getPort());
+        assertEquals(s.getRemoteSocketAddress(), remoteAddress);
+    }
+
+    /**
+     * Test getters on disconnected socket, before and after it is closed.
+     */
+    public void testDisconnectedSocket() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        DatagramSocket s = dc.socket();
+        try {
+            var loopback = InetAddress.getLoopbackAddress();
+            dc.connect(new InetSocketAddress(loopback, 7777));
+            dc.disconnect();
+
+            var localAddress = (InetSocketAddress) dc.getLocalAddress();
+
+            // state
+            assertTrue(s.isBound());
+            assertFalse(s.isConnected());
+            assertFalse(s.isClosed());
+
+            // local address
+            assertEquals(s.getLocalAddress(), localAddress.getAddress());
+            assertTrue(s.getLocalPort() == localAddress.getPort());
+            assertEquals(s.getLocalSocketAddress(), localAddress);
+
+            // remote address
+            assertTrue(s.getInetAddress() == null);
+            assertTrue(s.getPort() == -1);
+            assertTrue((s.getRemoteSocketAddress() == null));
+
+
+        } finally {
+            dc.close();
+        }
+
+        // state
+        assertTrue(s.isBound());
+        assertFalse(s.isConnected());
+        assertTrue(s.isClosed());
+
+        // local address
+        assertTrue(s.getLocalAddress() == null);
+        assertTrue(s.getLocalPort() == -1);
+        assertTrue(s.getLocalSocketAddress() == null);
+
+        // remote address
+        assertTrue(s.getInetAddress() == null);
+        assertTrue(s.getPort() == -1);
+        assertTrue((s.getRemoteSocketAddress() == null));
+    }
+}
--- a/test/jdk/java/nio/channels/etc/AdaptorCloseAndInterrupt.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/nio/channels/etc/AdaptorCloseAndInterrupt.java	Mon Nov 04 11:25:55 2019 +0000
@@ -22,7 +22,7 @@
  */
 
 /* @test
- * @bug 7184932
+ * @bug 7184932 8232673
  * @summary Test asynchronous close and interrupt of timed socket adapter methods
  * @key randomness intermittent
  */
@@ -78,8 +78,10 @@
 
             try (DatagramChannel peer = DatagramChannel.open()) {
                 peer.socket().bind(null);
-                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose();
-                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt();
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(0);
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(30_000);
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(0);
+                new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(30_000);
             }
 
             new AdaptorCloseAndInterrupt().ssAcceptAsyncClose();
@@ -138,11 +140,10 @@
         }
     }
 
-    void dcReceiveAsyncClose() throws IOException {
+    void dcReceiveAsyncClose(int timeout) throws IOException {
         DatagramChannel dc = DatagramChannel.open();
-        dc.connect(new InetSocketAddress(
-            InetAddress.getLoopbackAddress(), port));
-        dc.socket().setSoTimeout(30*1000);
+        dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
+        dc.socket().setSoTimeout(timeout);
 
         doAsyncClose(dc);
 
@@ -150,24 +151,23 @@
             dc.socket().receive(new DatagramPacket(new byte[100], 100));
             System.err.format("close() was invoked: %s%n", isClosed.get());
             throw new RuntimeException("receive should not have completed");
-        } catch (ClosedChannelException expected) {}
+        } catch (SocketException expected) { }
 
         if (!dc.socket().isClosed())
             throw new RuntimeException("socket is not closed");
     }
 
-    void dcReceiveAsyncInterrupt() throws IOException {
+    void dcReceiveAsyncInterrupt(int timeout) throws IOException {
         DatagramChannel dc = DatagramChannel.open();
-        dc.connect(new InetSocketAddress(
-            InetAddress.getLoopbackAddress(), port));
-        dc.socket().setSoTimeout(30*1000);
+        dc.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port));
+        dc.socket().setSoTimeout(timeout);
 
         doAsyncInterrupt();
 
         try {
             dc.socket().receive(new DatagramPacket(new byte[100], 100));
             throw new RuntimeException("receive should not have completed");
-        } catch (ClosedByInterruptException expected) {
+        } catch (SocketException expected) {
             System.out.format("interrupt() was invoked: %s%n",
                 isInterrupted.get());
             System.out.format("dcReceiveAsyncInterrupt was interrupted: %s%n",
--- a/test/jdk/java/util/concurrent/tck/ConcurrentHashMapTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/ConcurrentHashMapTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -55,8 +55,6 @@
         class Implementation implements MapImplementation {
             public Class<?> klazz() { return ConcurrentHashMap.class; }
             public Map emptyMap() { return new ConcurrentHashMap(); }
-            public Object makeKey(int i) { return i; }
-            public Object makeValue(int i) { return i; }
             public boolean isConcurrent() { return true; }
             public boolean permitsNullKeys() { return false; }
             public boolean permitsNullValues() { return false; }
--- a/test/jdk/java/util/concurrent/tck/ConcurrentSkipListMapTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/ConcurrentSkipListMapTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -54,9 +54,8 @@
         class Implementation implements MapImplementation {
             public Class<?> klazz() { return ConcurrentSkipListMap.class; }
             public Map emptyMap() { return new ConcurrentSkipListMap(); }
-            public Object makeKey(int i) { return i; }
-            public Object makeValue(int i) { return i; }
             public boolean isConcurrent() { return true; }
+            public boolean remappingFunctionCalledAtMostOnce() { return false; };
             public boolean permitsNullKeys() { return false; }
             public boolean permitsNullValues() { return false; }
             public boolean supportsSetValue() { return false; }
--- a/test/jdk/java/util/concurrent/tck/HashMapTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/HashMapTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -46,8 +46,6 @@
         class Implementation implements MapImplementation {
             public Class<?> klazz() { return HashMap.class; }
             public Map emptyMap() { return new HashMap(); }
-            public Object makeKey(int i) { return i; }
-            public Object makeValue(int i) { return i; }
             public boolean isConcurrent() { return false; }
             public boolean permitsNullKeys() { return true; }
             public boolean permitsNullValues() { return true; }
--- a/test/jdk/java/util/concurrent/tck/HashtableTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/HashtableTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -44,8 +44,6 @@
         class Implementation implements MapImplementation {
             public Class<?> klazz() { return Hashtable.class; }
             public Map emptyMap() { return new Hashtable(); }
-            public Object makeKey(int i) { return i; }
-            public Object makeValue(int i) { return i; }
             public boolean isConcurrent() { return true; }
             public boolean permitsNullKeys() { return false; }
             public boolean permitsNullValues() { return false; }
--- a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -731,6 +731,13 @@
     /**
      * Returns a random element from given choices.
      */
+    <T> T chooseRandomly(List<T> choices) {
+        return choices.get(ThreadLocalRandom.current().nextInt(choices.size()));
+    }
+
+    /**
+     * Returns a random element from given choices.
+     */
     <T> T chooseRandomly(T... choices) {
         return choices[ThreadLocalRandom.current().nextInt(choices.length)];
     }
@@ -1799,7 +1806,7 @@
 
         public int await() {
             try {
-                return super.await(2 * LONG_DELAY_MS, MILLISECONDS);
+                return super.await(LONGER_DELAY_MS, MILLISECONDS);
             } catch (TimeoutException timedOut) {
                 throw new AssertionError("timed out");
             } catch (Exception fail) {
--- a/test/jdk/java/util/concurrent/tck/LinkedHashMapTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/LinkedHashMapTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -46,8 +46,6 @@
         class Implementation implements MapImplementation {
             public Class<?> klazz() { return LinkedHashMap.class; }
             public Map emptyMap() { return new LinkedHashMap(); }
-            public Object makeKey(int i) { return i; }
-            public Object makeValue(int i) { return i; }
             public boolean isConcurrent() { return false; }
             public boolean permitsNullKeys() { return true; }
             public boolean permitsNullValues() { return true; }
--- a/test/jdk/java/util/concurrent/tck/MapImplementation.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/MapImplementation.java	Mon Nov 04 11:25:55 2019 +0000
@@ -40,9 +40,15 @@
     public Class<?> klazz();
     /** Returns an empty map. */
     public Map emptyMap();
-    public Object makeKey(int i);
-    public Object makeValue(int i);
+
+    // General purpose implementations can use Integers for key and value
+    default Object makeKey(int i) { return i; }
+    default Object makeValue(int i) { return i; }
+    default int keyToInt(Object key) { return (Integer) key; }
+    default int valueToInt(Object value) { return (Integer) value; }
+
     public boolean isConcurrent();
+    default boolean remappingFunctionCalledAtMostOnce() { return true; };
     public boolean permitsNullKeys();
     public boolean permitsNullValues();
     public boolean supportsSetValue();
--- a/test/jdk/java/util/concurrent/tck/MapTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/MapTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -32,13 +32,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-import junit.framework.Test;
-
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiFunction;
+
+import junit.framework.Test;
 
 /**
  * Contains tests applicable to all Map implementations.
@@ -227,6 +231,71 @@
         assertTrue(clone.isEmpty());
     }
 
+    /**
+     * Concurrent access by compute methods behaves as expected
+     */
+    public void testConcurrentAccess() throws Throwable {
+        final Map map = impl.emptyMap();
+        final long testDurationMillis = expensiveTests ? 1000 : 2;
+        final int nTasks = impl.isConcurrent()
+            ? ThreadLocalRandom.current().nextInt(1, 10)
+            : 1;
+        final AtomicBoolean done = new AtomicBoolean(false);
+        final boolean remappingFunctionCalledAtMostOnce
+            = impl.remappingFunctionCalledAtMostOnce();
+        final List<CompletableFuture> futures = new ArrayList<>();
+        final AtomicLong expectedSum = new AtomicLong(0);
+        final Action[] tasks = {
+            // repeatedly increment values using compute()
+            () -> {
+                long[] invocations = new long[2];
+                ThreadLocalRandom rnd = ThreadLocalRandom.current();
+                BiFunction<Object, Object, Object> incValue = (k, v) -> {
+                    invocations[1]++;
+                    int vi = (v == null) ? 1 : impl.valueToInt(v) + 1;
+                    return impl.makeValue(vi);
+                };
+                while (!done.getAcquire()) {
+                    invocations[0]++;
+                    Object key = impl.makeKey(3 * rnd.nextInt(10));
+                    map.compute(key, incValue);
+                }
+                if (remappingFunctionCalledAtMostOnce)
+                    assertEquals(invocations[0], invocations[1]);
+                expectedSum.getAndAdd(invocations[0]);
+            },
+            // repeatedly increment values using computeIfPresent()
+            () -> {
+                long[] invocations = new long[2];
+                ThreadLocalRandom rnd = ThreadLocalRandom.current();
+                BiFunction<Object, Object, Object> incValue = (k, v) -> {
+                    invocations[1]++;
+                    int vi = impl.valueToInt(v) + 1;
+                    return impl.makeValue(vi);
+                };
+                while (!done.getAcquire()) {
+                    Object key = impl.makeKey(3 * rnd.nextInt(10));
+                    if (map.computeIfPresent(key, incValue) != null)
+                        invocations[0]++;
+                }
+                if (remappingFunctionCalledAtMostOnce)
+                    assertEquals(invocations[0], invocations[1]);
+                expectedSum.getAndAdd(invocations[0]);
+            },
+        };
+        for (int i = nTasks; i--> 0; ) {
+            Action task = chooseRandomly(tasks);
+            futures.add(CompletableFuture.runAsync(checkedRunnable(task)));
+        }
+        Thread.sleep(testDurationMillis);
+        done.setRelease(true);
+        for (var future : futures)
+            checkTimedGet(future, null);
+
+        long sum = map.values().stream().mapToLong(x -> (int) x).sum();
+        assertEquals(expectedSum.get(), sum);
+    }
+
 //     public void testFailsIntentionallyForDebugging() {
 //         fail(impl.klazz().getSimpleName());
 //     }
--- a/test/jdk/java/util/concurrent/tck/StampedLockTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/StampedLockTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -42,12 +42,17 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Future;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.StampedLock;
 import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 import java.util.function.Function;
 
 import junit.framework.Test;
@@ -102,56 +107,51 @@
     }
 
     List<Action> lockLockers(Lock lock) {
-        List<Action> lockers = new ArrayList<>();
-        lockers.add(() -> lock.lock());
-        lockers.add(() -> lock.lockInterruptibly());
-        lockers.add(() -> lock.tryLock());
-        lockers.add(() -> lock.tryLock(Long.MIN_VALUE, DAYS));
-        lockers.add(() -> lock.tryLock(0L, DAYS));
-        lockers.add(() -> lock.tryLock(Long.MAX_VALUE, DAYS));
-        return lockers;
+        return List.of(
+            () -> lock.lock(),
+            () -> lock.lockInterruptibly(),
+            () -> lock.tryLock(),
+            () -> lock.tryLock(Long.MIN_VALUE, DAYS),
+            () -> lock.tryLock(0L, DAYS),
+            () -> lock.tryLock(Long.MAX_VALUE, DAYS));
     }
 
     List<Function<StampedLock, Long>> readLockers() {
-        List<Function<StampedLock, Long>> readLockers = new ArrayList<>();
-        readLockers.add(sl -> sl.readLock());
-        readLockers.add(sl -> sl.tryReadLock());
-        readLockers.add(sl -> readLockInterruptiblyUninterrupted(sl));
-        readLockers.add(sl -> tryReadLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
-        readLockers.add(sl -> tryReadLockUninterrupted(sl, 0L, DAYS));
-        readLockers.add(sl -> sl.tryConvertToReadLock(sl.tryOptimisticRead()));
-        return readLockers;
+        return List.of(
+            sl -> sl.readLock(),
+            sl -> sl.tryReadLock(),
+            sl -> readLockInterruptiblyUninterrupted(sl),
+            sl -> tryReadLockUninterrupted(sl, Long.MIN_VALUE, DAYS),
+            sl -> tryReadLockUninterrupted(sl, 0L, DAYS),
+            sl -> sl.tryConvertToReadLock(sl.tryOptimisticRead()));
     }
 
     List<BiConsumer<StampedLock, Long>> readUnlockers() {
-        List<BiConsumer<StampedLock, Long>> readUnlockers = new ArrayList<>();
-        readUnlockers.add((sl, stamp) -> sl.unlockRead(stamp));
-        readUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockRead()));
-        readUnlockers.add((sl, stamp) -> sl.asReadLock().unlock());
-        readUnlockers.add((sl, stamp) -> sl.unlock(stamp));
-        readUnlockers.add((sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp)));
-        return readUnlockers;
+        return List.of(
+            (sl, stamp) -> sl.unlockRead(stamp),
+            (sl, stamp) -> assertTrue(sl.tryUnlockRead()),
+            (sl, stamp) -> sl.asReadLock().unlock(),
+            (sl, stamp) -> sl.unlock(stamp),
+            (sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp)));
     }
 
     List<Function<StampedLock, Long>> writeLockers() {
-        List<Function<StampedLock, Long>> writeLockers = new ArrayList<>();
-        writeLockers.add(sl -> sl.writeLock());
-        writeLockers.add(sl -> sl.tryWriteLock());
-        writeLockers.add(sl -> writeLockInterruptiblyUninterrupted(sl));
-        writeLockers.add(sl -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
-        writeLockers.add(sl -> tryWriteLockUninterrupted(sl, 0L, DAYS));
-        writeLockers.add(sl -> sl.tryConvertToWriteLock(sl.tryOptimisticRead()));
-        return writeLockers;
+        return List.of(
+            sl -> sl.writeLock(),
+            sl -> sl.tryWriteLock(),
+            sl -> writeLockInterruptiblyUninterrupted(sl),
+            sl -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS),
+            sl -> tryWriteLockUninterrupted(sl, 0L, DAYS),
+            sl -> sl.tryConvertToWriteLock(sl.tryOptimisticRead()));
     }
 
     List<BiConsumer<StampedLock, Long>> writeUnlockers() {
-        List<BiConsumer<StampedLock, Long>> writeUnlockers = new ArrayList<>();
-        writeUnlockers.add((sl, stamp) -> sl.unlockWrite(stamp));
-        writeUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockWrite()));
-        writeUnlockers.add((sl, stamp) -> sl.asWriteLock().unlock());
-        writeUnlockers.add((sl, stamp) -> sl.unlock(stamp));
-        writeUnlockers.add((sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp)));
-        return writeUnlockers;
+        return List.of(
+            (sl, stamp) -> sl.unlockWrite(stamp),
+            (sl, stamp) -> assertTrue(sl.tryUnlockWrite()),
+            (sl, stamp) -> sl.asWriteLock().unlock(),
+            (sl, stamp) -> sl.unlock(stamp),
+            (sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp)));
     }
 
     /**
@@ -1413,4 +1413,113 @@
         }
     }
 
+    /**
+     * Multiple threads repeatedly contend for the same lock.
+     */
+    public void testConcurrentAccess() throws Exception {
+        final StampedLock sl = new StampedLock();
+        final Lock wl = sl.asWriteLock();
+        final Lock rl = sl.asReadLock();
+        final long testDurationMillis = expensiveTests ? 1000 : 2;
+        final int nTasks = ThreadLocalRandom.current().nextInt(1, 10);
+        final AtomicBoolean done = new AtomicBoolean(false);
+        final List<CompletableFuture> futures = new ArrayList<>();
+        final List<Callable<Long>> stampedWriteLockers = List.of(
+            () -> sl.writeLock(),
+            () -> writeLockInterruptiblyUninterrupted(sl),
+            () -> tryWriteLockUninterrupted(sl, LONG_DELAY_MS, MILLISECONDS),
+            () -> {
+                long stamp;
+                do { stamp = sl.tryConvertToWriteLock(sl.tryOptimisticRead()); }
+                while (stamp == 0L);
+                return stamp;
+            },
+            () -> {
+              long stamp;
+              do { stamp = sl.tryWriteLock(); } while (stamp == 0L);
+              return stamp;
+            },
+            () -> {
+              long stamp;
+              do { stamp = sl.tryWriteLock(0L, DAYS); } while (stamp == 0L);
+              return stamp;
+            });
+        final List<Callable<Long>> stampedReadLockers = List.of(
+            () -> sl.readLock(),
+            () -> readLockInterruptiblyUninterrupted(sl),
+            () -> tryReadLockUninterrupted(sl, LONG_DELAY_MS, MILLISECONDS),
+            () -> {
+                long stamp;
+                do { stamp = sl.tryConvertToReadLock(sl.tryOptimisticRead()); }
+                while (stamp == 0L);
+                return stamp;
+            },
+            () -> {
+              long stamp;
+              do { stamp = sl.tryReadLock(); } while (stamp == 0L);
+              return stamp;
+            },
+            () -> {
+              long stamp;
+              do { stamp = sl.tryReadLock(0L, DAYS); } while (stamp == 0L);
+              return stamp;
+            });
+        final List<Consumer<Long>> stampedWriteUnlockers = List.of(
+            stamp -> sl.unlockWrite(stamp),
+            stamp -> sl.unlock(stamp),
+            stamp -> assertTrue(sl.tryUnlockWrite()),
+            stamp -> wl.unlock(),
+            stamp -> sl.tryConvertToOptimisticRead(stamp));
+        final List<Consumer<Long>> stampedReadUnlockers = List.of(
+            stamp -> sl.unlockRead(stamp),
+            stamp -> sl.unlock(stamp),
+            stamp -> assertTrue(sl.tryUnlockRead()),
+            stamp -> rl.unlock(),
+            stamp -> sl.tryConvertToOptimisticRead(stamp));
+        final Action writer = () -> {
+            // repeatedly acquires write lock
+            var locker = chooseRandomly(stampedWriteLockers);
+            var unlocker = chooseRandomly(stampedWriteUnlockers);
+            while (!done.getAcquire()) {
+                long stamp = locker.call();
+                try {
+                    assertTrue(isWriteLockStamp(stamp));
+                    assertTrue(sl.isWriteLocked());
+                    assertFalse(isReadLockStamp(stamp));
+                    assertFalse(sl.isReadLocked());
+                    assertEquals(0, sl.getReadLockCount());
+                    assertTrue(sl.validate(stamp));
+                } finally {
+                    unlocker.accept(stamp);
+                }
+            }
+        };
+        final Action reader = () -> {
+            // repeatedly acquires read lock
+            var locker = chooseRandomly(stampedReadLockers);
+            var unlocker = chooseRandomly(stampedReadUnlockers);
+            while (!done.getAcquire()) {
+                long stamp = locker.call();
+                try {
+                    assertFalse(isWriteLockStamp(stamp));
+                    assertFalse(sl.isWriteLocked());
+                    assertTrue(isReadLockStamp(stamp));
+                    assertTrue(sl.isReadLocked());
+                    assertTrue(sl.getReadLockCount() > 0);
+                    assertTrue(sl.validate(stamp));
+                } finally {
+                    unlocker.accept(stamp);
+                }
+            }
+        };
+        for (int i = nTasks; i--> 0; ) {
+            Action task = chooseRandomly(writer, reader);
+            futures.add(CompletableFuture.runAsync(checkedRunnable(task)));
+        }
+        Thread.sleep(testDurationMillis);
+        done.setRelease(true);
+        for (var future : futures)
+            checkTimedGet(future, null);
+    }
+
 }
--- a/test/jdk/java/util/concurrent/tck/TreeMapTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/java/util/concurrent/tck/TreeMapTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -53,8 +53,6 @@
         class Implementation implements MapImplementation {
             public Class<?> klazz() { return TreeMap.class; }
             public Map emptyMap() { return new TreeMap(); }
-            public Object makeKey(int i) { return i; }
-            public Object makeValue(int i) { return i; }
             public boolean isConcurrent() { return false; }
             public boolean permitsNullKeys() { return false; }
             public boolean permitsNullValues() { return true; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/util/zip/ZipFile/ZipFileInputStreamSkipTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8231451
+ * @summary Basic tests for ZipFileInputStream::skip
+ * @modules jdk.zipfs
+ * @run testng/othervm ZipFileInputStreamSkipTest
+ */
+public class ZipFileInputStreamSkipTest {
+
+    // Stored and Deflated Zip File paths used by the tests
+    private final Path STORED_ZIPFILE = Path.of("skipStoredEntries.zip");
+    private final Path DEFLATED_ZIPFILE = Path.of("skipDeflatedEntries.zip");
+
+    // Saved Entries added to the relevant Zip file
+    private final HashMap<String, Entry> STORED_ZIP_ENTRIES = new HashMap<>();
+    private final HashMap<String, Entry> DEFLATED_ZIP_ENTRIES = new HashMap<>();
+
+    /**
+     * Create the Zip Files used by the tests
+     *
+     * @throws IOException If an error occurs creating the Zip Files
+     */
+    @BeforeClass
+    private void createZip() throws IOException {
+        Entry e0 = Entry.of("Entry-0", ZipEntry.STORED, "Tennis Pro");
+        Entry e1 = Entry.of("Entry-1", ZipEntry.STORED,
+                "United States Tennis Association");
+        Entry e2 = Entry.of("Entry-2", ZipEntry.DEFLATED, "Cardio Tennis");
+        Entry e3 = Entry.of("Entry-3", ZipEntry.DEFLATED, "USTA League Championships");
+
+        // Add entries
+        STORED_ZIP_ENTRIES.put(e0.name, e0);
+        STORED_ZIP_ENTRIES.put(e1.name, e1);
+        DEFLATED_ZIP_ENTRIES.put(e2.name, e2);
+        DEFLATED_ZIP_ENTRIES.put(e3.name, e3);
+
+        Files.deleteIfExists(STORED_ZIPFILE);
+        Files.deleteIfExists(DEFLATED_ZIPFILE);
+
+        createZipFile(STORED_ZIPFILE,
+                Map.of("create", "true", "noCompression", "true"),
+                e0, e1);
+
+        createZipFile(DEFLATED_ZIPFILE, Map.of("create", "true"), e2, e3);
+    }
+
+    /**
+     * Delete Zip Files created for the test
+     *
+     * @throws IOException If an error occurs during cleanup
+     */
+    @AfterClass
+    private void cleanUp() throws IOException {
+        Files.deleteIfExists(STORED_ZIPFILE);
+        Files.deleteIfExists(DEFLATED_ZIPFILE);
+    }
+
+    /**
+     * Validate that you can skip forward within a STORED entry
+     * and then read the expected data for the entry
+     *
+     * @throws Exception If an error occurs during the test
+     */
+    @Test
+    private void testStoredSkip() throws Exception {
+
+        try (ZipFile zf = new ZipFile(STORED_ZIPFILE.toFile())) {
+            var entries = zf.entries();
+            while (entries.hasMoreElements()) {
+                var entry = entries.nextElement();
+                var entrySize = entry.getSize();
+                long midpoint = entrySize / 2;
+                Entry expected = STORED_ZIP_ENTRIES.get(entry.getName());
+                assertNotNull(expected);
+                try (InputStream in = zf.getInputStream(entry)) {
+
+                    // Check that if we specify 0, that we return the correct
+                    // skip value value
+                    assertEquals(in.skip(0), 0);
+
+                    // Try to skip past EOF and should return remaining bytes
+                    assertEquals(in.skip(entrySize + 100), entrySize);
+
+                    // Return to BOF and then specify a value which would
+                    // overflow the projected skip value and return the
+                    // number of bytes moved to reach EOF
+                    assertEquals(in.skip(-entrySize), -entrySize);
+                    assertEquals(in.skip(Long.MAX_VALUE), entrySize);
+
+                    // From midpoint, try to skip past EOF and then skip back
+                    // to BOF
+                    assertEquals(in.skip(-entrySize), -entrySize);
+                    assertEquals(in.skip(midpoint), midpoint);
+                    assertEquals(in.skip(1000), entrySize - midpoint);
+                    assertEquals(in.skip(-entrySize), -entrySize);
+
+                    // Read remaining bytes and validate against expected bytes
+                    byte[] bytes = in.readAllBytes();
+                    assertEquals(bytes, expected.bytes);
+                    assertEquals(bytes.length, expected.bytes.length);
+                }
+            }
+        }
+    }
+
+    /**
+     * Validate that you can skip backwards within a STORED entry
+     * and then read the expected data for the entry
+     *
+     * @throws Exception If an error occurs during the test
+     */
+    @Test
+    private void testStoredNegativeSkip() throws Exception {
+
+        try (ZipFile zf = new ZipFile(STORED_ZIPFILE.toFile())) {
+            var entries = zf.entries();
+            while (entries.hasMoreElements()) {
+                var entry = entries.nextElement();
+                var entrySize = entry.getSize();
+                var midpoint = entrySize / 2;
+                Entry expected = STORED_ZIP_ENTRIES.get(entry.getName());
+                assertNotNull(expected);
+                try (InputStream in = zf.getInputStream(entry)) {
+
+                    // Check that if you try to move past BOF
+                    // that we return the correct value
+                    assertEquals(in.skip(-1), 0);
+                    assertEquals(in.skip(-100), 0);
+                    assertEquals(in.skip(Long.MIN_VALUE), 0);
+
+                    // Go to midpoint in file; then specify a value before
+                    // BOF which should result in the number of
+                    // bytes to BOF returned
+                    assertEquals(in.skip(midpoint), midpoint);
+                    assertEquals(in.skip(-(midpoint + 10)), -midpoint);
+
+                    // From midpoint, move back a couple of bytes
+                    assertEquals(in.skip(midpoint), midpoint);
+                    assertEquals(in.skip(-2), -2);
+
+                    // Read the remaining bytes and compare to the expected bytes
+                    byte[] bytes = in.readAllBytes();
+                    assertEquals(bytes, Arrays.copyOfRange(expected.bytes,
+                            (int)midpoint - 2, (int) entrySize));
+                    assertEquals(bytes.length, entrySize - midpoint + 2);
+                }
+            }
+        }
+    }
+
+    /**
+     * Validate that you can skip forward within a DEFLATED entry
+     * and then read the expected data for the entry
+     *
+     * @throws Exception If an error occurs during the test
+     */
+    @Test
+    private void testDeflatedSkip() throws Exception {
+        try (ZipFile zf = new ZipFile(DEFLATED_ZIPFILE.toFile())) {
+            var toSkip = 5; // Bytes to Skip
+            var entries = zf.entries();
+            while (entries.hasMoreElements()) {
+                var entry = entries.nextElement();
+                Entry expected = DEFLATED_ZIP_ENTRIES.get(entry.getName());
+                assertNotNull(expected);
+                try (InputStream in = zf.getInputStream(entry)) {
+                    assertEquals(in.skip(toSkip), toSkip);
+                    byte[] bytes = in.readAllBytes();
+                    var ebytes = Arrays.copyOfRange(expected.bytes,
+                            toSkip, expected.bytes.length);
+                    assertEquals(bytes, ebytes);
+                    assertEquals(bytes.length, expected.bytes.length - toSkip);
+                }
+            }
+        }
+    }
+
+    /**
+     * Validate that an IllegalArgumentException is thrown if you specify
+     * a negative skip value for a DEFLATED entry.
+     *
+     * @throws Exception If an unexpected error occurs during the test
+     */
+    @Test
+    private void testDeflatedIOException() throws Exception {
+        try (ZipFile zf = new ZipFile(DEFLATED_ZIPFILE.toFile())) {
+            var entries = zf.entries();
+            while (entries.hasMoreElements()) {
+                var entry = entries.nextElement();
+                assertNotNull(entry);
+                try (InputStream in = zf.getInputStream(entry)) {
+                    // Cannot specify a negative value
+                    assertThrows(IllegalArgumentException.class, () -> in.skip((-1)));
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a Zip File System using the specified properties and a Zip file
+     * with the specified number of entries
+     *
+     * @param zipFile Path to the Zip File to create
+     * @param env     Properties used for creating the Zip Filesystem
+     * @param entries The entries to add to the Zip File
+     * @throws IOException If an error occurs while creating the Zip file
+     */
+    private void createZipFile(Path zipFile, Map<String, String> env,
+                               Entry... entries) throws IOException {
+        try (FileSystem zipfs =
+                     FileSystems.newFileSystem(zipFile, env)) {
+            for (Entry e : entries) {
+                Files.writeString(zipfs.getPath(e.name), new String(e.bytes));
+            }
+        }
+    }
+
+    /**
+     * Represents an entry in a Zip file. An entry encapsulates a name, a
+     * compression method, and its contents/data.
+     */
+    static class Entry {
+        private final String name;
+        private final int method;
+        private final byte[] bytes;
+
+        Entry(String name, int method, String contents) {
+            this.name = name;
+            this.method = method;
+            this.bytes = contents.getBytes(StandardCharsets.UTF_8);
+        }
+
+        static Entry of(String name, int method, String contents) {
+            return new Entry(name, method, contents);
+        }
+
+        /**
+         * Returns a new Entry with the same name and compression method as this
+         * Entry but with the given content.
+         */
+        Entry content(String contents) {
+            return new Entry(name, method, contents);
+        }
+    }
+
+}
--- a/test/jdk/jdk/jfr/api/consumer/TestReadTwice.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/TestReadTwice.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package jdk.jfr.api.consumer;
 
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.LinkedList;
 import java.util.List;
 
--- a/test/jdk/jdk/jfr/api/consumer/TestRecordingFile.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/TestRecordingFile.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -55,7 +55,7 @@
  * @key jfr
  * @requires vm.hasJFR
  * @library /test/lib
- * @run main/othervm jdk.jfr.api.consumer.TestRecordingFile
+ * @run main/othervm -Xlog:jfr*=info jdk.jfr.api.consumer.TestRecordingFile
  */
 public class TestRecordingFile {
 
@@ -210,12 +210,12 @@
            assertHasEventType(types, "Event2");
            assertMissingEventType(types, "Event3");
        }
-       try (RecordingFile f = new RecordingFile(twoEventTypes)) {
+       try (RecordingFile f = new RecordingFile(threeEventTypes)) {
            List<EventType> types = f.readEventTypes();
            assertUniqueEventTypes(types);
            assertHasEventType(types, "Event1");
            assertHasEventType(types, "Event2");
-           assertMissingEventType(types, "Event3");
+           assertHasEventType(types, "Event3");
        }
 
     }
--- a/test/jdk/jdk/jfr/api/consumer/TestRecordingInternals.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/TestRecordingInternals.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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
@@ -60,6 +60,7 @@
                 Asserts.assertEquals(id.toString(), rt.getThreadGroup().getName(), "Thread group name should match id");
                 Asserts.assertEquals(id, Integer.valueOf(i), "Chunks out of order");
                 i++;
+                System.out.println(i + " OK ");
             }
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/filestream/TestMultipleChunk.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.filestream;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.atomic.AtomicLong;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents from a multichunked file
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.filestream.TestMultipleChunk
+ */
+public class TestMultipleChunk {
+
+    static class SnakeEvent extends Event {
+        int id;
+    }
+
+    public static void main(String... args) throws Exception {
+        Path path = Paths.get("./using-file.jfr");
+        try (Recording r1 = new Recording()) {
+            r1.start();
+            emitSnakeEvent(1);
+            emitSnakeEvent(2);
+            emitSnakeEvent(3);
+            // Force a chunk rotation
+            try (Recording r2 = new Recording()) {
+                r2.start();
+                emitSnakeEvent(4);
+                emitSnakeEvent(5);
+                emitSnakeEvent(6);
+                r2.stop();
+            }
+            r1.stop();
+            r1.dump(path);
+            AtomicLong counter = new AtomicLong();
+            try (EventStream es = EventStream.openFile(path)) {
+                es.onEvent(e -> {
+                    counter.incrementAndGet();
+                });
+                es.start();
+                if (counter.get() != 6) {
+                    throw new Exception("Expected 6 event, but got " + counter.get());
+                }
+            }
+        }
+    }
+
+    static void emitSnakeEvent(int id) {
+        SnakeEvent e = new SnakeEvent();
+        e.id = id;
+        e.commit();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/filestream/TestOrdered.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.filestream;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Test EventStream::setOrdered(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.filestream.TestOrdered
+ */
+public class TestOrdered {
+
+    static class OrderedEvent extends Event {
+    }
+
+    static class Emitter extends Thread {
+        private final CyclicBarrier barrier;
+
+        public Emitter(CyclicBarrier barrier) {
+            this.barrier = barrier;
+        }
+
+        @Override
+        public void run() {
+            OrderedEvent e1 = new OrderedEvent();
+            e1.commit();
+            try {
+                barrier.await();
+            } catch (Exception e) {
+                e.printStackTrace();
+                throw new Error("Unexpected exception in barrier");
+            }
+            OrderedEvent e2 = new OrderedEvent();
+            e2.commit();
+        }
+    }
+
+    private static final int THREAD_COUNT = 4;
+    private static final boolean[] BOOLEAN_STATES = { false, true };
+
+    public static void main(String... args) throws Exception {
+        Path p = makeUnorderedRecording();
+
+        testSetOrderedTrue(p);
+        testSetOrderedFalse(p);
+    }
+
+    private static void testSetOrderedTrue(Path p) throws Exception {
+        for (boolean reuse : BOOLEAN_STATES) {
+            AtomicReference<Instant> timestamp = new AtomicReference<>(Instant.MIN);
+            try (EventStream es = EventStream.openFile(p)) {
+                es.setReuse(reuse);
+                es.setOrdered(true);
+                es.onEvent(e -> {
+                    Instant endTime = e.getEndTime();
+                    if (endTime.isBefore(timestamp.get())) {
+                        throw new Error("Events are not ordered! Reuse = " + reuse);
+                    }
+                    timestamp.set(endTime);
+                });
+                es.start();
+            }
+        }
+    }
+
+    private static void testSetOrderedFalse(Path p) throws Exception {
+        for (boolean reuse : BOOLEAN_STATES) {
+            AtomicReference<Instant> timestamp = new AtomicReference<>(Instant.MIN);
+            AtomicBoolean unoreded = new AtomicBoolean(false);
+            try (EventStream es = EventStream.openFile(p)) {
+                es.setReuse(reuse);
+                es.setOrdered(false);
+                es.onEvent(e -> {
+                    Instant endTime = e.getEndTime();
+                    if (endTime.isBefore(timestamp.get())) {
+                        unoreded.set(true);
+                        es.close();
+                    }
+                    timestamp.set(endTime);
+                });
+                es.start();
+                if (!unoreded.get()) {
+                    throw new Exception("Expected at least some events to be out of order! Reuse = " + reuse);
+                }
+            }
+        }
+    }
+
+    private static Path makeUnorderedRecording() throws Exception {
+        CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
+
+        try (Recording r = new Recording()) {
+            r.start();
+            List<Emitter> emitters = new ArrayList<>();
+            for (int i = 0; i < THREAD_COUNT; i++) {
+                Emitter e = new Emitter(barrier);
+                e.start();
+                emitters.add(e);
+            }
+            // Thread buffers should now have one event each
+            barrier.await();
+            // Add another event to each thread buffer, so
+            // events are bound to come out of order when they
+            // are flushed
+            for (Emitter e : emitters) {
+                e.join();
+            }
+            r.stop();
+            Path p = Files.createTempFile("recording", ".jfr");
+            r.dump(p);
+            return p;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/filestream/TestReuse.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.filestream;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordedEvent;
+
+/**
+ * @test
+ * @summary Test EventStream::setReuse(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.filestream.TestReuse
+ */
+public class TestReuse {
+
+    static class ReuseEvent extends Event {
+    }
+
+    private static final boolean[] BOOLEAN_STATES = { false, true };
+
+    public static void main(String... args) throws Exception {
+        Path p = makeRecording();
+
+        testSetReuseTrue(p);
+        testSetReuseFalse(p);
+    }
+
+    private static void testSetReuseFalse(Path p) throws Exception {
+        for (boolean ordered : BOOLEAN_STATES) {
+            AtomicBoolean fail = new AtomicBoolean(false);
+            Map<RecordedEvent, RecordedEvent> identity = new IdentityHashMap<>();
+            try (EventStream es = EventStream.openFile(p)) {
+                es.setOrdered(ordered);
+                es.setReuse(false);
+                es.onEvent(e -> {
+                    if (identity.containsKey(e)) {
+                        fail.set(true);
+                        es.close();
+                    }
+                    identity.put(e, e);
+                });
+                es.start();
+            }
+            if (fail.get()) {
+                throw new Exception("Unexpected reuse! Ordered = " + ordered);
+            }
+
+        }
+    }
+
+    private static void testSetReuseTrue(Path p) throws Exception {
+        for (boolean ordered : BOOLEAN_STATES) {
+            AtomicBoolean success = new AtomicBoolean(false);
+            Map<RecordedEvent, RecordedEvent> events = new IdentityHashMap<>();
+            try (EventStream es = EventStream.openFile(p)) {
+                es.setOrdered(ordered);
+                es.setReuse(true);
+                es.onEvent(e -> {
+                    if(events.containsKey(e)) {
+                        success.set(true);;
+                        es.close();
+                    }
+                    events.put(e,e);
+                });
+                es.start();
+            }
+            if (!success.get()) {
+                throw new Exception("No reuse! Ordered = " + ordered);
+            }
+        }
+
+    }
+
+    private static Path makeRecording() throws IOException {
+        try (Recording r = new Recording()) {
+            r.start();
+            for (int i = 0; i < 5; i++) {
+                ReuseEvent e = new ReuseEvent();
+                e.commit();
+            }
+            Recording rotation = new Recording();
+            rotation.start();
+            for (int i = 0; i < 5; i++) {
+                ReuseEvent e = new ReuseEvent();
+                e.commit();
+            }
+            r.stop();
+            rotation.close();
+            Path p = Files.createTempFile("recording", ".jfr");
+            r.dump(p);
+            return p;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/EventProducer.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import jdk.jfr.api.consumer.recordingstream.TestStart.StartEvent;
+
+class EventProducer extends Thread {
+    private final Object lock = new Object();
+    private boolean killed = false;
+    public void run() {
+        while (true) {
+            StartEvent s = new StartEvent();
+            s.commit();
+            synchronized (lock) {
+                try {
+                    lock.wait(10);
+                    if (killed) {
+                        return; // end thread
+                    }
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+    public void kill()  {
+        synchronized (lock) {
+            this.killed = true;
+            lock.notifyAll();
+        }
+        try {
+            join();
+        } catch (InterruptedException e) {
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestAwaitTermination.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test RecordingStream::awaitTermination(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestAwaitTermination
+ */
+public class TestAwaitTermination {
+
+    public static void main(String... args) throws Exception {
+        testAwaitClose();
+        testAwaitTimeOut();
+    }
+
+    private static void testAwaitClose() throws InterruptedException, ExecutionException {
+        try (RecordingStream r = new RecordingStream()) {
+            r.startAsync();
+            var c = CompletableFuture.runAsync(() -> {
+                try {
+                    r.awaitTermination();
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            });
+            r.close();
+            c.get();
+        }
+    }
+
+    private static void testAwaitTimeOut() throws InterruptedException, ExecutionException {
+        try (RecordingStream r = new RecordingStream()) {
+            r.startAsync();
+            var c = CompletableFuture.runAsync(() -> {
+                try {
+                    r.awaitTermination(Duration.ofMillis(10));
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            });
+            c.get();
+            r.close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestClose.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicLong;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::close()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestClose
+ */
+public class TestClose {
+
+    private static class CloseEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        testCloseUnstarted();
+        testCloseStarted();
+        testCloseTwice();
+        testCloseStreaming();
+        testCloseMySelf();
+    }
+
+    private static void testCloseMySelf() throws Exception {
+        log("Entering testCloseMySelf()");
+        CountDownLatch l1 = new CountDownLatch(1);
+        CountDownLatch l2 = new CountDownLatch(1);
+        RecordingStream r = new RecordingStream();
+        r.onEvent(e -> {
+            try {
+                l1.await();
+                r.close();
+                l2.countDown();
+            } catch (InterruptedException ie) {
+                throw new Error(ie);
+            }
+        });
+        r.startAsync();
+        CloseEvent c = new CloseEvent();
+        c.commit();
+        l1.countDown();
+        l2.await();
+        log("Leaving testCloseMySelf()");
+    }
+
+    private static void testCloseStreaming() throws Exception {
+        log("Entering testCloseStreaming()");
+        CountDownLatch streaming = new CountDownLatch(1);
+        RecordingStream r = new RecordingStream();
+        AtomicLong count = new AtomicLong();
+        r.onEvent(e -> {
+            if (count.incrementAndGet() > 100) {
+                streaming.countDown();
+            }
+        });
+        r.startAsync();
+        var streamingLoop = CompletableFuture.runAsync(() -> {
+            while (true) {
+                CloseEvent c = new CloseEvent();
+                c.commit();
+            }
+        });
+        streaming.await();
+        r.close();
+        streamingLoop.cancel(true);
+        log("Leaving testCloseStreaming()");
+    }
+
+    private static void testCloseStarted() {
+        log("Entering testCloseStarted()");
+        RecordingStream r = new RecordingStream();
+        r.startAsync();
+        r.close();
+        log("Leaving testCloseStarted()");
+    }
+
+    private static void testCloseUnstarted() {
+        log("Entering testCloseUnstarted()");
+        RecordingStream r = new RecordingStream();
+        r.close();
+        log("Leaving testCloseUnstarted()");
+    }
+
+    private static void testCloseTwice() {
+        log("Entering testCloseTwice()");
+        RecordingStream r = new RecordingStream();
+        r.startAsync();
+        r.close();
+        r.close();
+        log("Leaving testCloseTwice()");
+    }
+
+    private static void log(String msg) {
+        System.out.println(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestConstructor.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Configuration;
+import jdk.jfr.Enabled;
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::RecordingStream()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestConstructor
+ */
+public class TestConstructor {
+
+    public static void main(String... args) throws Exception {
+        testEmpty();
+        testConfiguration();
+    }
+
+    private static void testConfiguration() throws Exception {
+        CountDownLatch jvmInformation = new CountDownLatch(1);
+        Configuration c = Configuration.getConfiguration("default");
+        if (!c.getSettings().containsKey(EventNames.JVMInformation + "#" + Enabled.NAME)) {
+            throw new Exception("Expected default configuration to contain enabled " + EventNames.JVMInformation);
+        }
+        RecordingStream r = new RecordingStream(c);
+        r.onEvent("jdk.JVMInformation", e -> {
+            jvmInformation.countDown();
+        });
+        r.startAsync();
+        jvmInformation.await();
+        r.close();
+    }
+
+    private static void testEmpty() {
+        RecordingStream r = new RecordingStream();
+        r.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestDisable.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::disable(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestDisable
+ */
+public class TestDisable {
+
+    private static class DisabledEvent extends Event {
+    }
+
+    private static class EnabledEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        testDisableWithClass();
+        testDisableWithEventName();
+    }
+
+    private static void testDisableWithEventName() {
+        test(r -> r.disable(DisabledEvent.class.getName()));
+    }
+
+    private static void testDisableWithClass() {
+        test(r -> r.disable(DisabledEvent.class));
+    }
+
+    private static void test(Consumer<RecordingStream> disablement) {
+        CountDownLatch twoEvent = new CountDownLatch(2);
+        AtomicBoolean fail = new AtomicBoolean(false);
+        try(RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                if (e.getEventType().getName().equals(DisabledEvent.class.getName())) {
+                    fail.set(true);
+                }
+                twoEvent.countDown();
+            });
+            disablement.accept(r);
+            r.startAsync();
+            EnabledEvent e1 = new EnabledEvent();
+            e1.commit();
+            DisabledEvent d1 = new DisabledEvent();
+            d1.commit();
+            EnabledEvent e2 = new EnabledEvent();
+            e2.commit();
+            try {
+                twoEvent.await();
+            } catch (InterruptedException ie) {
+                throw new RuntimeException("Unexpexpected interruption of thread", ie);
+            }
+            if (fail.get()) {
+                throw new RuntimeException("Should not receive a disabled event");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestEnable.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
+
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::enable(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestEnable
+ */
+public class TestEnable {
+
+    @Enabled(false)
+    private static class EnabledEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        testEnableWithClass();
+        testEnableWithEventName();
+    }
+
+    private static void testEnableWithEventName() {
+        test(r -> r.enable(EnabledEvent.class.getName()));
+    }
+
+    private static void testEnableWithClass() {
+        test(r -> r.enable(EnabledEvent.class));
+    }
+
+    private static void test(Consumer<RecordingStream> enablement) {
+        CountDownLatch event = new CountDownLatch(1);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                event.countDown();
+            });
+            enablement.accept(r);
+            r.startAsync();
+            EnabledEvent e = new EnabledEvent();
+            e.commit();
+            try {
+                event.await();
+            } catch (InterruptedException ie) {
+                throw new RuntimeException("Unexpected interruption of latch", ie);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestMaxAge.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::setMaxAge(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestMaxAge
+ */
+public class TestMaxAge {
+
+    public static void main(String... args) throws Exception {
+        Duration testDuration = Duration.ofMillis(1234567);
+        try (RecordingStream r = new RecordingStream()) {
+            r.setMaxAge(testDuration);
+            r.enable(EventNames.ActiveRecording);
+            r.onEvent(e -> {
+                System.out.println(e);
+                Duration d = e.getDuration("maxAge");
+                System.out.println(d.toMillis());
+                if (testDuration.equals(d)) {
+                    r.close();
+                    return;
+                }
+                System.out.println("Max age not set, was " + d.toMillis() + " ms , but expected " + testDuration.toMillis() + " ms");
+            });
+            r.start();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnClose.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onClose(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnClose
+ */
+public class TestOnClose {
+
+    private static class CloseEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        testOnCloseNull();
+        testOnClosedUnstarted();
+        testOnClosedStarted();
+    }
+
+    private static void testOnCloseNull() {
+       try (RecordingStream rs = new RecordingStream()) {
+          try {
+              rs.onClose(null);
+              throw new AssertionError("Expected NullPointerException from onClose(null");
+          } catch (NullPointerException npe) {
+              // OK; as expected
+          }
+       }
+    }
+
+    private static void testOnClosedStarted() throws InterruptedException {
+        AtomicBoolean onClose = new AtomicBoolean(false);
+        CountDownLatch event = new CountDownLatch(1);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                event.countDown();
+            });
+            r.onClose(() -> {
+                onClose.set(true);
+            });
+            r.startAsync();
+            CloseEvent c = new CloseEvent();
+            c.commit();
+            event.await();
+        }
+        if (!onClose.get()) {
+            throw new AssertionError("OnClose was not called");
+        }
+    }
+
+    private static void testOnClosedUnstarted() {
+        AtomicBoolean onClose = new AtomicBoolean(false);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onClose(() -> {
+                onClose.set(true);
+            });
+        }
+        if (!onClose.get()) {
+            throw new AssertionError("OnClose was not called");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnErrorAsync.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.api.consumer.recordingstream.TestUtils.TestError;
+import jdk.jfr.api.consumer.recordingstream.TestUtils.TestException;
+import jdk.jfr.api.consumer.security.TestStreamingRemote.TestEvent;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onError(...) when using
+ *          RecordingStream:startAsync
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnErrorAsync
+ */
+public class TestOnErrorAsync {
+    public static void main(String... args) throws Exception {
+        testDefaultError();
+        testCustomError();
+        testDefaultException();
+        testCustomException();
+        testOnFlushSanity();
+        testOnCloseSanity();
+    }
+
+    private static void testDefaultError() throws Exception {
+        AtomicBoolean closed = new AtomicBoolean();
+        CountDownLatch receivedError = new CountDownLatch(1);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                TestError error = new TestError();
+                TestUtils.installUncaughtException(receivedError, error);
+                throw error; // closes stream
+            });
+            r.onClose(() -> {
+                closed.set(true);
+            });
+            r.startAsync();
+            TestEvent e = new TestEvent();
+            e.commit();
+            r.awaitTermination();
+            receivedError.await();
+            if (!closed.get()) {
+                throw new Exception("Expected stream to be closed");
+            }
+        }
+    }
+
+    private static void testCustomError() throws Exception {
+        AtomicBoolean onError = new AtomicBoolean();
+        AtomicBoolean closed = new AtomicBoolean();
+        CountDownLatch receivedError = new CountDownLatch(1);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                TestError error = new TestError();
+                TestUtils.installUncaughtException(receivedError, error);
+                throw error; // closes stream
+            });
+            r.onError(e -> {
+                onError.set(true);
+            });
+            r.onClose(() -> {
+                closed.set(true);
+            });
+            r.startAsync();
+            TestEvent e = new TestEvent();
+            e.commit();
+            r.awaitTermination();
+            receivedError.await();
+            if (onError.get()) {
+                throw new Exception("onError handler should not be invoked on java.lang.Error.");
+            }
+            if (!closed.get()) {
+                throw new Exception("Expected stream to be closed");
+            }
+        }
+    }
+
+    private static void testDefaultException() throws Exception {
+        TestException exception = new TestException();
+        AtomicBoolean closed = new AtomicBoolean();
+        AtomicInteger counter = new AtomicInteger();
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                if (counter.incrementAndGet() == 2) {
+                    r.close();
+                    return;
+                }
+                TestUtils.throwUnchecked(exception);
+            });
+            r.onClose(() -> {
+                closed.set(true);
+            });
+            r.startAsync();
+            TestEvent e1 = new TestEvent();
+            e1.commit();
+            TestEvent e2 = new TestEvent();
+            e2.commit();
+            r.awaitTermination();
+            if (!exception.isPrinted()) {
+                throw new Exception("Expected stack trace from Exception to be printed");
+            }
+            if (!closed.get()) {
+                throw new Exception("Expected stream to be closed");
+            }
+        }
+    }
+
+    private static void testCustomException() throws Exception {
+        TestException exception = new TestException();
+        AtomicBoolean closed = new AtomicBoolean();
+        AtomicBoolean received = new AtomicBoolean();
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                TestUtils.throwUnchecked(exception);
+            });
+            r.onError(t -> {
+                received.set(t == exception);
+                r.close();
+            });
+            r.onClose(() -> {
+                closed.set(true);
+            });
+            r.startAsync();
+            TestEvent event = new TestEvent();
+            event.commit();
+            r.awaitTermination();
+            if (!received.get()) {
+                throw new Exception("Did not receive expected exception in onError(...)");
+            }
+            if (exception.isPrinted()) {
+                throw new Exception("Expected stack trace from Exception NOT to be printed");
+            }
+            if (!closed.get()) {
+                throw new Exception("Expected stream to be closed");
+            }
+        }
+    }
+
+    private static void testOnFlushSanity() throws Exception {
+        TestException exception = new TestException();
+        CountDownLatch received = new CountDownLatch(1);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onFlush(() -> {
+                TestUtils.throwUnchecked(exception);
+            });
+            r.onError(t -> {
+                if (t == exception) {
+                    received.countDown();
+                }
+            });
+            r.startAsync();
+            received.await();
+       }
+    }
+
+    private static void testOnCloseSanity() throws Exception {
+        TestException exception = new TestException();
+        CountDownLatch received = new CountDownLatch(1);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onFlush(() -> {
+                r.close(); // will trigger onClose
+            });
+            r.onClose(() -> {
+                TestUtils.throwUnchecked(exception); // will trigger onError
+            });
+            r.onError(t -> {
+                if (t == exception) {
+                    received.countDown();
+                }
+            });
+            r.startAsync();
+            received.await();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnErrorSync.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.api.consumer.recordingstream.TestUtils.TestError;
+import jdk.jfr.api.consumer.recordingstream.TestUtils.TestException;
+import jdk.jfr.api.consumer.security.TestStreamingRemote.TestEvent;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onError(...) when using RecordingStream:start
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnErrorSync
+ */
+public class TestOnErrorSync {
+    public static void main(String... args) throws Exception {
+        testDefaultError();
+        testCustomError();
+        testDefaultException();
+        testCustomException();
+        testOnFlushSanity();
+        testOnCloseSanity();
+    }
+
+    private static void testDefaultError() throws Exception {
+        TestError error = new TestError();
+        AtomicBoolean closed = new AtomicBoolean();
+        Timer t = newEventEmitter();
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                throw error; // closes stream
+            });
+            r.onClose(() -> {
+                closed.set(true);
+            });
+            try {
+                r.start();
+                throw new Exception("Expected TestError to be thrown");
+            } catch (TestError te) {
+                // as expected
+            }
+            if (!closed.get()) {
+                throw new Exception("Expected stream to be closed");
+            }
+        } finally {
+            t.cancel();
+        }
+    }
+
+    private static void testCustomError() throws Exception {
+        TestError error = new TestError();
+        AtomicBoolean onError = new AtomicBoolean();
+        AtomicBoolean closed = new AtomicBoolean();
+        Timer t = newEventEmitter();
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                throw error; // closes stream
+            });
+            r.onError(e -> {
+                onError.set(true);
+            });
+            r.onClose(() -> {
+                closed.set(true);
+            });
+            try {
+                r.start();
+                throw new Exception("Expected TestError to be thrown");
+            } catch (TestError terror) {
+                // as expected
+            }
+            if (onError.get()) {
+                throw new Exception("Expected onError(...) NOT to be invoked");
+            }
+            if (!closed.get()) {
+                throw new Exception("Expected stream to be closed");
+            }
+        } finally {
+            t.cancel();
+        }
+    }
+
+    private static void testDefaultException() throws Exception {
+        TestException exception = new TestException();
+        AtomicInteger counter = new AtomicInteger();
+        AtomicBoolean closed = new AtomicBoolean();
+        Timer t = newEventEmitter();
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                if (counter.incrementAndGet() == 2) {
+                    // Only close if we get a second event after an exception
+                    r.close();
+                    return;
+                }
+                TestUtils.throwUnchecked(exception);
+            });
+            r.onClose(() -> {
+                closed.set(true);
+            });
+            try {
+                r.start();
+            } catch (Exception e) {
+                throw new Exception("Unexpected exception thrown from start()", e);
+            }
+            if (!exception.isPrinted()) {
+                throw new Exception("Expected stack trace from Exception to be printed");
+            }
+            if (!closed.get()) {
+                throw new Exception("Expected stream to be closed");
+            }
+        } finally {
+            t.cancel();
+        }
+    }
+
+    private static void testCustomException() throws Exception {
+        TestException exception = new TestException();
+        AtomicInteger counter = new AtomicInteger();
+        AtomicBoolean onError = new AtomicBoolean();
+        AtomicBoolean closed = new AtomicBoolean();
+        AtomicBoolean received = new AtomicBoolean();
+        Timer t = newEventEmitter();
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                if (counter.incrementAndGet() == 2) {
+                    // Only close if we get a second event after an exception
+                    r.close();
+                    return;
+                }
+                TestUtils.throwUnchecked(exception);
+            });
+            r.onError(e -> {
+                received.set(e == exception);
+                onError.set(true);
+            });
+            r.onClose(() -> {
+                closed.set(true);
+            });
+            try {
+                r.start();
+            } catch (Exception e) {
+                throw new Exception("Unexpected exception thrown from start()", e);
+            }
+            if (!received.get()) {
+                throw new Exception("Did not receive expected exception in onError(...)");
+            }
+            if (exception.isPrinted()) {
+                throw new Exception("Expected stack trace from Exception NOT to be printed");
+            }
+            if (!onError.get()) {
+                throw new Exception("Expected OnError(...) to be invoked");
+            }
+            if (!closed.get()) {
+                throw new Exception("Expected stream to be closed");
+            }
+        } finally {
+            t.cancel();
+        }
+    }
+
+    private static void testOnFlushSanity() throws Exception {
+        TestException exception = new TestException();
+        AtomicBoolean received = new AtomicBoolean();
+        try (RecordingStream r = new RecordingStream()) {
+            r.onFlush(() -> {
+                TestUtils.throwUnchecked(exception);
+            });
+            r.onError(t -> {
+                received.set(t == exception);
+                r.close();
+            });
+            r.start();
+            if (!received.get()) {
+                throw new Exception("Expected exception in OnFlush to propagate to onError");
+            }
+        }
+    }
+
+    private static void testOnCloseSanity() throws Exception {
+        TestException exception = new TestException();
+        AtomicBoolean received = new AtomicBoolean();
+        try (RecordingStream r = new RecordingStream()) {
+            r.onFlush(() -> {
+                r.close(); // will trigger onClose
+            });
+            r.onClose(() -> {
+                TestUtils.throwUnchecked(exception); // will trigger onError
+            });
+            r.onError(t -> {
+                received.set(t == exception);
+            });
+            r.start();
+            if (!received.get()) {
+                throw new Exception("Expected exception in OnFlush to propagate to onError");
+            }
+        }
+    }
+
+    private static Timer newEventEmitter() {
+        Timer timer = new Timer();
+        TimerTask task = new TimerTask() {
+            @Override
+            public void run() {
+                TestEvent event = new TestEvent();
+                event.commit();
+            }
+        };
+        timer.schedule(task, 0, 100);
+        return timer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnEvent.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onEvent(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnEvent
+ */
+public class TestOnEvent {
+
+    @Name("A")
+    static class EventA extends Event {
+    }
+
+    @Name("A")
+    static class EventAlsoA extends Event {
+    }
+
+    @Name("C")
+    static class EventC extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        testOnEventNull();
+        testOnEvent();
+        testNamedEvent();
+        testTwoEventWithSameName();
+    }
+
+    private static void testOnEventNull() {
+        log("Entering testOnEventNull()");
+        try (RecordingStream rs = new RecordingStream()) {
+           try {
+               rs.onEvent(null);
+               throw new AssertionError("Expected NullPointerException from onEvent(null)");
+           } catch (NullPointerException npe) {
+               // OK; as expected
+           }
+           try {
+               rs.onEvent("A", null);
+               throw new AssertionError("Expected NullPointerException from onEvent(\"A\", null)");
+
+           } catch (NullPointerException npe) {
+               // OK; as expected
+           }
+           try {
+               String s = null;
+               rs.onEvent(s, null);
+               throw new AssertionError("Expected NullPointerException from onEvent(null, null)");
+           } catch (NullPointerException npe) {
+               // OK; as expected
+           }
+        }
+        log("Leaving testOnEventNull()");
+     }
+
+    private static void testTwoEventWithSameName() throws Exception {
+        log("Entering testTwoEventWithSameName()");
+        CountDownLatch eventA = new CountDownLatch(2);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent("A", e -> {
+                System.out.println("testTwoEventWithSameName" +  e);
+                eventA.countDown();
+            });
+            r.startAsync();
+            EventA a1 = new EventA();
+            a1.commit();
+            EventAlsoA a2 = new EventAlsoA();
+            a2.commit();
+            eventA.await();
+        }
+        log("Leaving testTwoEventWithSameName()");
+    }
+
+    private static void testNamedEvent() throws Exception {
+        log("Entering testNamedEvent()");
+        try (RecordingStream r = new RecordingStream()) {
+            CountDownLatch eventA = new CountDownLatch(1);
+            CountDownLatch eventC = new CountDownLatch(1);
+            r.onEvent("A", e -> {
+                System.out.println("TestNamedEvent:" + e);
+                if (e.getEventType().getName().equals("A")) {
+                    eventA.countDown();
+                }
+            });
+            r.onEvent("C", e -> {
+                System.out.println("TestNamedEvent:" + e);
+                if (e.getEventType().getName().equals("C")) {
+                    eventC.countDown();
+                }
+            });
+
+            r.startAsync();
+            EventA a = new EventA();
+            a.commit();
+            EventC c = new EventC();
+            c.commit();
+            eventA.await();
+            eventC.await();
+        }
+        log("Leaving testNamedEvent()");
+    }
+
+    private static void testOnEvent() throws Exception {
+        log("Entering testOnEvent()");
+        try (RecordingStream r = new RecordingStream()) {
+            CountDownLatch event = new CountDownLatch(1);
+            r.onEvent(e -> {
+                event.countDown();
+            });
+            r.startAsync();
+            EventA a = new EventA();
+            a.commit();
+            event.await();
+        }
+        log("Leaving testOnEvent()");
+    }
+
+    private static void log(String msg) {
+        System.out.println(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestOnFlush.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::onFlush(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestOnFlush
+ */
+public class TestOnFlush {
+
+    static class OneEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        testOnFlushNull();
+        testOneEvent();
+        testNoEvent();
+    }
+
+    private static void testOnFlushNull() {
+        log("Entering testOnFlushNull()");
+        try (RecordingStream rs = new RecordingStream()) {
+           try {
+               rs.onFlush(null);
+               throw new AssertionError("Expected NullPointerException from onFlush(null");
+           } catch (NullPointerException npe) {
+               // OK; as expected
+           }
+        }
+        log("Leaving testOnFlushNull()");
+     }
+
+    private static void testNoEvent() throws Exception {
+        log("Entering testNoEvent()");
+        CountDownLatch flush = new CountDownLatch(1);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onFlush(() -> {
+                flush.countDown();
+            });
+            r.startAsync();
+            flush.await();
+        }
+        log("Leaving testNoEvent()");
+    }
+
+    private static void testOneEvent() throws InterruptedException {
+        log("Entering testOneEvent()");
+        CountDownLatch flush = new CountDownLatch(1);
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                // ignore event
+            });
+            r.onFlush(() -> {
+                flush.countDown();
+            });
+            r.startAsync();
+            OneEvent e = new OneEvent();
+            e.commit();
+            flush.await();
+        }
+        log("Leaving testOneEvent()");
+    }
+
+    private static void log(String msg) {
+        System.out.println(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestRecursive.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.Events;
+
+/**
+ * @test
+ * @summary Tests that events are not emitted in handlers
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @build jdk.jfr.api.consumer.recordingstream.EventProducer
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestRecursive
+ */
+public class TestRecursive {
+
+    public static class NotRecorded extends Event {
+    }
+
+    public static class Recorded extends Event {
+    }
+
+    public static class Provoker extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        testSync();
+        testAsync();
+        testStreamInStream();
+    }
+
+    private static void testStreamInStream() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        try (Recording r = new Recording()) {
+            r.start();
+            Recorded r1 = new Recorded(); // 1
+            r1.commit();
+            try (RecordingStream rs = new RecordingStream()) {
+                rs.onEvent(e1 -> {
+                    streamInStream();
+                    latch.countDown();
+                });
+                rs.startAsync();
+                Recorded r2 = new Recorded(); // 2
+                r2.commit();
+                latch.await();
+            }
+            Recorded r3 = new Recorded(); // 2
+            r3.commit();
+            r.stop();
+            List<RecordedEvent> events = Events.fromRecording(r);
+            if (count(events, NotRecorded.class) != 0) {
+                throw new Exception("Expected 0 NotRecorded events");
+            }
+            if (count(events, Recorded.class) != 3) {
+                throw new Exception("Expected 3 Recorded events");
+            }
+        }
+    }
+
+    // No events should be recorded in this method
+    private static void streamInStream() {
+        NotRecorded nr1 = new NotRecorded();
+        nr1.commit();
+        CountDownLatch latch = new CountDownLatch(1);
+        try (RecordingStream rs2 = new RecordingStream()) {
+            rs2.onEvent(e2 -> {
+                NotRecorded nr2 = new NotRecorded();
+                nr2.commit();
+                latch.countDown();
+            });
+            NotRecorded nr3 = new NotRecorded();
+            nr3.commit();
+            rs2.startAsync();
+            // run event in separate thread
+            CompletableFuture.runAsync(() -> {
+                Provoker p = new Provoker();
+                p.commit();
+            });
+            try {
+                latch.await();
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected interruption", e);
+            }
+        }
+        NotRecorded nr2 = new NotRecorded();
+        nr2.commit();
+    }
+
+    private static void testSync() throws Exception {
+        try (Recording r = new Recording()) {
+            r.start();
+            EventProducer p = new EventProducer();
+            try (RecordingStream rs = new RecordingStream()) {
+                Recorded e1 = new Recorded();
+                e1.commit();
+                rs.onEvent(e -> {
+                    System.out.println("Emitting NotRecorded event");
+                    NotRecorded event = new NotRecorded();
+                    event.commit();
+                    System.out.println("Stopping event provoker");
+                    p.kill();
+                    System.out.println("Closing recording stream");
+                    rs.close();
+                    return;
+                });
+                p.start();
+                rs.start();
+                Recorded e2 = new Recorded();
+                e2.commit();
+            }
+            r.stop();
+            List<RecordedEvent> events = Events.fromRecording(r);
+            System.out.println(events);
+            if (count(events, NotRecorded.class) != 0) {
+                throw new Exception("Expected 0 NotRecorded events");
+            }
+            if (count(events, Recorded.class) != 2) {
+                throw new Exception("Expected 2 Recorded events");
+            }
+        }
+    }
+
+    private static int count(List<RecordedEvent> events, Class<?> eventClass) {
+        int count = 0;
+        for (RecordedEvent e : events) {
+            if (e.getEventType().getName().equals(eventClass.getName())) {
+                count++;
+            }
+        }
+        System.out.println(count);
+        return count;
+    }
+
+    private static void testAsync() throws InterruptedException, Exception {
+        CountDownLatch latchOne = new CountDownLatch(1);
+        CountDownLatch latchTwo = new CountDownLatch(2);
+        AtomicBoolean fail = new AtomicBoolean();
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent(e -> {
+                System.out.println(e);
+                NotRecorded event = new NotRecorded();
+                event.commit();
+                if (e.getEventType().getName().equals(Recorded.class.getName())) {
+                    latchOne.countDown();
+                    latchTwo.countDown();
+                }
+                if (e.getEventType().getName().equals(NotRecorded.class.getName())) {
+                    fail.set(true);
+                }
+            });
+            r.startAsync();
+            Recorded e1 = new Recorded();
+            e1.commit();
+            latchOne.await();
+            Recorded e2 = new Recorded();
+            e2.commit();
+            latchTwo.await();
+            if (fail.get()) {
+                throw new Exception("Unexpected event found");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestRemove.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStrream::remove(...)
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestRemove
+ */
+public class TestRemove {
+
+    static class RemoveEvent extends Event {
+
+    }
+
+    public static void main(String... args) throws Exception {
+        testRemoveNull();
+        testRemoveOnFlush();
+        testRemoveOnClose();
+        testRemoveOnEvent();
+    }
+
+    private static void testRemoveNull() {
+        log("Entering testRemoveNull()");
+        try (RecordingStream rs = new RecordingStream()) {
+           try {
+               rs.remove(null);
+               throw new AssertionError("Expected NullPointerException from remove(null");
+           } catch (NullPointerException npe) {
+               // OK; as expected
+           }
+        }
+        log("Leaving testRemoveNull()");
+     }
+
+    private static void testRemoveOnEvent() throws Exception {
+        log("Entering testRemoveOnEvent()");
+        try (RecordingStream rs = new RecordingStream()) {
+            AtomicInteger counter = new AtomicInteger(0);
+            CountDownLatch events = new CountDownLatch(2);
+            Consumer<RecordedEvent> c1 = e -> {
+                counter.incrementAndGet();
+            };
+
+            Consumer<RecordedEvent> c2 = e -> {
+                events.countDown();
+            };
+            rs.onEvent(c1);
+            rs.onEvent(c2);
+
+            rs.remove(c1);
+            rs.startAsync();
+            RemoveEvent r1 = new RemoveEvent();
+            r1.commit();
+            RemoveEvent r2 = new RemoveEvent();
+            r2.commit();
+            events.await();
+            if (counter.get() > 0) {
+                throw new AssertionError("OnEvent handler not removed!");
+            }
+        }
+        log("Leaving testRemoveOnEvent()");
+    }
+
+    private static void testRemoveOnClose() {
+        log("Entering testRemoveOnClose()");
+        try (RecordingStream rs = new RecordingStream()) {
+            AtomicBoolean onClose = new AtomicBoolean(false);
+            Runnable r = () -> {
+                onClose.set(true);
+            };
+            rs.onClose(r);
+            rs.remove(r);
+            rs.close();
+            if (onClose.get()) {
+                throw new AssertionError("onClose handler not removed!");
+            }
+        }
+        log("Leaving testRemoveOnClose()");
+    }
+
+    private static void testRemoveOnFlush() throws Exception {
+        log("Entering testRemoveOnFlush()");
+        try (RecordingStream rs = new RecordingStream()) {
+            AtomicInteger flushCount = new AtomicInteger(2);
+            AtomicBoolean removeExecuted = new AtomicBoolean(false);
+            Runnable onFlush1 = () -> {
+                removeExecuted.set(true);
+            };
+            Runnable onFlush2 = () -> {
+                flushCount.incrementAndGet();
+            };
+
+            rs.onFlush(onFlush1);
+            rs.onFlush(onFlush2);
+            rs.remove(onFlush1);
+            rs.startAsync();
+            while (flushCount.get() < 2) {
+                RemoveEvent r = new RemoveEvent();
+                r.commit();
+                Thread.sleep(100);
+            }
+
+            if (removeExecuted.get()) {
+                throw new AssertionError("onFlush handler not removed!");
+            }
+        }
+        log("Leaving testRemoveOnFlush()");
+    }
+
+    private static void log(String msg) {
+        System.out.println(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetEndTime.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.Recording;
+import jdk.jfr.StackTrace;
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingFile;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests EventStream::setEndTime
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetEndTime
+ */
+public final class TestSetEndTime {
+
+    @Name("Mark")
+    @StackTrace(false)
+    public final static class Mark extends Event {
+        public boolean include;
+        public int id;
+    }
+
+    public static void main(String... args) throws Exception {
+        testEventStream();
+        testRecordingStream();
+    }
+
+    private static void testRecordingStream() throws Exception {
+        while (true) {
+            CountDownLatch closed = new CountDownLatch(1);
+            AtomicInteger count = new AtomicInteger();
+            try (RecordingStream rs = new RecordingStream()) {
+                rs.setFlushInterval(Duration.ofSeconds(1));
+                rs.onEvent(e -> {
+                    count.incrementAndGet();
+                });
+                // when end is reached stream is closed
+                rs.onClose(() -> {
+                    closed.countDown();
+                });
+                Instant endTime = Instant.now().plus(Duration.ofMillis(100));
+                System.out.println("Setting end time: " + endTime);
+                rs.setEndTime(endTime);
+                rs.startAsync();
+                for (int i = 0; i < 50; i++) {
+                    Mark m = new Mark();
+                    m.commit();
+                    Thread.sleep(10);
+                }
+                closed.await();
+                System.out.println("Found events: " + count.get());
+                if (count.get() < 50) {
+                    return;
+                }
+                System.out.println("Found 50 events. Retrying");
+                System.out.println();
+            }
+        }
+    }
+
+    static void testEventStream() throws InterruptedException, IOException, Exception {
+        while (true) {
+            try (Recording r = new Recording()) {
+                r.start();
+
+                Mark event1 = new Mark();
+                event1.id = 1;
+                event1.include = false;
+                event1.commit(); // start time
+
+                nap();
+
+                Mark event2 = new Mark();
+                event2.id = 2;
+                event2.include = true;
+                event2.commit();
+
+                nap();
+
+                Mark event3 = new Mark();
+                event3.id = 3;
+                event3.include = false;
+                event3.commit(); // end time
+
+                Path p = Paths.get("recording.jfr");
+                r.dump(p);
+                Instant start = null;
+                Instant end = null;
+                System.out.println("Find start and end time as instants:");
+                for (RecordedEvent e : RecordingFile.readAllEvents(p)) {
+                    if (e.getInt("id") == 1) {
+                        start = e.getEndTime();
+                        System.out.println("Start  : " + start);
+                    }
+                    if (e.getInt("id") == 2) {
+                        Instant middle = e.getEndTime();
+                        System.out.println("Middle : " + middle);
+                    }
+                    if (e.getInt("id") == 3) {
+                        end = e.getEndTime();
+                        System.out.println("End    : " + end);
+                    }
+                }
+                System.out.println();
+                System.out.println("Opening stream between " + start + " and " + end);
+                AtomicBoolean success = new AtomicBoolean(false);
+                AtomicInteger eventsCount = new AtomicInteger();
+                try (EventStream d = EventStream.openRepository()) {
+                    d.setStartTime(start.plusNanos(1));
+                    // Stream should close when end is reached
+                    d.setEndTime(end.minusNanos(1));
+                    d.onEvent(e -> {
+                        eventsCount.incrementAndGet();
+                        boolean include = e.getBoolean("include");
+                        System.out.println("Event " + e.getEndTime() + " include=" + include);
+                        if (include) {
+                            success.set(true);
+                        }
+                    });
+                    d.start();
+                    if (eventsCount.get() == 1 && success.get()) {
+                        return;
+                    }
+                }
+            }
+        }
+
+    }
+
+    private static void nap() throws InterruptedException {
+        // Ensure we advance at least 1 ns with fast time
+        Thread.sleep(1);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetFlushInterval.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::setFlushInterval
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetFlushInterval
+ */
+public class TestSetFlushInterval {
+
+    public static void main(String... args) throws Exception {
+        Duration expectedDuration = Duration.ofMillis(1001);
+        try (RecordingStream r = new RecordingStream()) {
+            r.setFlushInterval(expectedDuration);
+            r.enable(EventNames.ActiveRecording);
+            r.onEvent(e -> {
+                System.out.println(e);
+                Duration duration = e.getDuration("flushInterval");
+                if (expectedDuration.equals(duration)) {
+                    System.out.println("Closing recording");
+                    r.close();
+                    return;
+                }
+                System.out.println("Flush interval not set, was " + duration +
+                                   ", but expected " + expectedDuration);
+            });
+            r.start();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetMaxAge.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.time.Duration;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests RecordingStrream::setMaxAge
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetMaxAge
+ */
+public class TestSetMaxAge {
+
+    public static void main(String... args) throws Exception {
+        Duration expecteddAge = Duration.ofMillis(123456789);
+        try (RecordingStream r = new RecordingStream()) {
+            r.setMaxAge(expecteddAge);
+            r.enable(EventNames.ActiveRecording);
+            r.onEvent(e -> {
+                System.out.println(e);
+                Duration age = e.getDuration("maxAge");
+                if (expecteddAge.equals(age)) {
+                    r.close();
+                    return;
+                }
+                System.out.println("Max age not set, was " + age + ", but expected " + expecteddAge);
+            });
+            r.start();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetMaxSize.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+* @test
+* @summary Tests RecordingStrream::setMaxSize
+* @key jfr
+* @requires vm.hasJFR
+* @library /test/lib
+* @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetMaxSize
+*/
+public class TestSetMaxSize {
+
+   public static void main(String... args) throws Exception {
+       long testSize = 123456789;
+       try (RecordingStream r = new RecordingStream()) {
+           r.setMaxSize(123456789);
+           r.enable(EventNames.ActiveRecording);
+           r.onEvent(e -> {
+               System.out.println(e);
+               long size= e.getLong("maxSize");
+               if (size == testSize) {
+                   r.close();
+                   return;
+               }
+               System.out.println("Max size not set, was " + size + ", but expected " + testSize);
+           });
+           r.start();
+       }
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetSettings.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::setSettings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+parser jdk.jfr.api.consumer.recordingstream.TestSetSettings
+ */
+public final class TestSetSettings {
+
+    @Name("LateBloomer")
+    @Enabled(false)
+    private final static class LateBloomer extends Event {
+    }
+
+    private static CountDownLatch lateBloomer = new CountDownLatch(1);
+
+    public static void main(String... args) throws Exception {
+        try (RecordingStream r = new RecordingStream()) {
+            r.startAsync();
+            Map<String, String> settings = new HashMap<String, String>();
+            settings.put("LateBloomer#enabled", "true");
+            r.setSettings(settings);
+            r.onEvent("LateBloomer", e -> {
+                lateBloomer.countDown();
+            });
+            LateBloomer event = new LateBloomer();
+            event.commit();
+            lateBloomer.await();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestSetStartTime.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Name;
+import jdk.jfr.Recording;
+import jdk.jfr.StackTrace;
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests EventStream::setStartTime
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestSetStartTime
+ */
+public final class TestSetStartTime {
+
+    private final static int SLEEP_TIME_MS = 100;
+
+    @Name("Mark")
+    @StackTrace(false)
+    public final static class Mark extends Event {
+        public boolean before;
+    }
+
+    public static void main(String... args) throws Exception {
+        testEventStream();
+        testRecordingStream();
+    }
+
+    private static void testRecordingStream() throws InterruptedException {
+        AtomicBoolean exit = new AtomicBoolean();
+        int attempt = 1;
+        while (!exit.get()) {
+            System.out.println("Testing RecordingStream:setStartTime(...). Attempt: " + attempt++);
+            AtomicBoolean firstEvent = new AtomicBoolean(true);
+            try (RecordingStream r2 = new RecordingStream()) {
+                Instant t = Instant.now().plus(Duration.ofMillis(SLEEP_TIME_MS / 2));
+                System.out.println("Setting start time: " + t);
+                r2.setStartTime(t);
+                r2.onEvent(e -> {
+                    if (firstEvent.get()) {
+                        firstEvent.set(false);
+                        if (!e.getBoolean("before")) {
+                            // Skipped first event, let's exit
+                            exit.set(true);
+                        }
+                        r2.close();
+                    }
+                });
+                r2.startAsync();
+                Mark m1 = new Mark();
+                m1.before = true;
+                m1.commit();
+                System.out.println("First event emitted: " + Instant.now());
+                Thread.sleep(SLEEP_TIME_MS);
+                Mark m2 = new Mark();
+                m2.before = false;
+                m2.commit();
+                System.out.println("Second event emitted: " + Instant.now());
+                r2.awaitTermination();
+            }
+            System.out.println();
+        }
+    }
+
+    private static void testEventStream() throws InterruptedException, Exception, IOException {
+        AtomicBoolean exit = new AtomicBoolean();
+        int attempt = 1;
+        while (!exit.get()) {
+            System.out.println("Testing EventStream:setStartTime(...). Attempt: " + attempt++);
+            AtomicBoolean firstEvent = new AtomicBoolean(true);
+            try (Recording r = new Recording()) {
+                r.start();
+                Mark event1 = new Mark();
+                event1.before = true;
+                event1.commit();
+                Instant t = Instant.now();
+                System.out.println("First event emitted: " + t);
+                Thread.sleep(SLEEP_TIME_MS);
+                Mark event2 = new Mark();
+                event2.before = false;
+                event2.commit();
+                System.out.println("Second event emitted: " + Instant.now());
+                try (EventStream es = EventStream.openRepository()) {
+                    Instant startTime = t.plus(Duration.ofMillis(SLEEP_TIME_MS / 2));
+                    es.setStartTime(startTime);
+                    System.out.println("Setting start time: " + startTime);
+                    es.onEvent(e -> {
+                        if (firstEvent.get()) {
+                            firstEvent.set(false);
+                            if (!e.getBoolean("before")) {
+                                // Skipped first event, let's exit
+                                exit.set(true);
+                            }
+                            es.close();
+                        }
+                    });
+                    es.startAsync();
+                    es.awaitTermination();
+                }
+            }
+            System.out.println();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestStart.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::start()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @build jdk.jfr.api.consumer.recordingstream.EventProducer
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestStart
+ */
+public class TestStart {
+    static class StartEvent extends Event {
+    }
+    public static void main(String... args) throws Exception {
+        testStart();
+        testStartOnEvent();
+        testStartTwice();
+        testStartClosed();
+    }
+
+    private static void testStartTwice() throws Exception {
+        log("Entering testStartTwice()");
+        CountDownLatch started = new CountDownLatch(1);
+        try (RecordingStream rs = new RecordingStream()) {
+            EventProducer t = new EventProducer();
+            t.start();
+            Thread thread = new Thread() {
+                public void run() {
+                    rs.start();
+                }
+            };
+            thread.start();
+            rs.onEvent(e -> {
+                if (started.getCount() > 0) {
+                    started.countDown();
+                }
+            });
+            started.await();
+            t.kill();
+            try {
+                rs.start();
+                throw new AssertionError("Expected IllegalStateException if started twice");
+            } catch (IllegalStateException ise) {
+                // OK, as expected
+            }
+        }
+        log("Leaving testStartTwice()");
+    }
+
+    static void testStart() throws Exception {
+        log("Entering testStart()");
+        CountDownLatch started = new CountDownLatch(1);
+        try (RecordingStream rs = new RecordingStream()) {
+            rs.onEvent(e -> {
+                started.countDown();
+            });
+            EventProducer t = new EventProducer();
+            t.start();
+            Thread thread = new Thread() {
+                public void run() {
+                    rs.start();
+                }
+            };
+            thread.start();
+            started.await();
+            t.kill();
+        }
+        log("Leaving testStart()");
+    }
+
+    static void testStartOnEvent() throws Exception {
+        log("Entering testStartOnEvent()");
+        AtomicBoolean ISE = new AtomicBoolean(false);
+        CountDownLatch startedTwice = new CountDownLatch(1);
+        try (RecordingStream rs = new RecordingStream()) {
+            rs.onEvent(e -> {
+                try {
+                    rs.start(); // must not deadlock
+                } catch (IllegalStateException ise) {
+                    if (!ISE.get())  {
+                        ISE.set(true);
+                        startedTwice.countDown();
+                    }
+                }
+            });
+            EventProducer t = new EventProducer();
+            t.start();
+            Thread thread = new Thread() {
+                public void run() {
+                    rs.start();
+                }
+            };
+            thread.start();
+            startedTwice.await();
+            t.kill();
+            if (!ISE.get()) {
+                throw new AssertionError("Expected IllegalStateException");
+            }
+        }
+        log("Leaving testStartOnEvent()");
+    }
+
+    static void testStartClosed() {
+        log("Entering testStartClosed()");
+        RecordingStream rs = new RecordingStream();
+        rs.close();
+        try {
+            rs.start();
+            throw new AssertionError("Expected IllegalStateException");
+        } catch (IllegalStateException ise) {
+            // OK, as expected.
+        }
+        log("Leaving testStartClosed()");
+    }
+
+    private static void log(String msg) {
+        System.out.println(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestStartAsync.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests RecordingStream::startAsync()
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.recordingstream.TestStartAsync
+ */
+public class TestStartAsync {
+    static class StartEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        testStart();
+        testStartTwice();
+        testStartClosed();
+    }
+
+    private static void testStartTwice() throws Exception {
+        try (RecordingStream rs = new RecordingStream()) {
+            rs.startAsync();
+            try {
+                rs.startAsync();
+                throw new AssertionError("Expected IllegalStateException if started twice");
+            } catch (IllegalStateException ise) {
+                // OK, as expected
+            }
+        }
+    }
+
+    static void testStart() throws Exception {
+        CountDownLatch started = new CountDownLatch(1);
+        try (RecordingStream rs = new RecordingStream()) {
+            rs.onEvent(e -> {
+                started.countDown();
+            });
+            rs.startAsync();
+            StartEvent e = new StartEvent();
+            e.commit();
+            started.await();
+        }
+    }
+
+    static void testStartClosed() {
+        RecordingStream rs = new RecordingStream();
+        rs.close();
+        try {
+            rs.startAsync();
+            throw new AssertionError("Expected IllegalStateException");
+        } catch (IllegalStateException ise) {
+            // OK, as expected.
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/recordingstream/TestUtils.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.recordingstream;
+
+import java.util.concurrent.CountDownLatch;
+
+public class TestUtils {
+
+    public static final class TestError extends Error {
+        private static final long serialVersionUID = 1L;
+    }
+
+    public static final class TestException extends Exception {
+        private static final long serialVersionUID = 1L;
+        private volatile boolean printed;
+
+        @Override
+        public void printStackTrace() {
+            super.printStackTrace();
+            printed = true;
+        }
+
+        public boolean isPrinted() {
+            return printed;
+        }
+    }
+
+    // Can throw checked exception as unchecked.
+    @SuppressWarnings("unchecked")
+    public static <T extends Throwable> void throwUnchecked(Throwable e) throws T {
+        throw (T) e;
+    }
+
+    public static void installUncaughtException(CountDownLatch receivedError, Throwable expected) {
+        Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> {
+            if (throwable == expected) {
+                System.out.println("Received uncaught exception " + expected.getClass());
+                receivedError.countDown();
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/DriverRecordingDumper.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.security;
+
+import java.nio.file.Paths;
+
+import jdk.jfr.Recording;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * Driver that dumps a recording with a JVMInformation event
+ *
+ * Usage:
+ *
+ * @run driver jdk.jfr.api.consumer.security.RecordingDumper <filename>
+ */
+public class DriverRecordingDumper {
+    public static void main(String... args) throws Exception {
+        try (Recording r = new Recording()) {
+            r.enable(EventNames.JVMInformation);
+            r.start();
+            r.stop();
+            r.dump(Paths.get(args[0]));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestMissingPermission.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.security;
+
+import java.io.IOException;
+
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests that streaming doesn't work if
+ *          FlightRecordingPermission("accessFlightRecorder") is missing
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy
+ *      jdk.jfr.api.consumer.security.TestMissingPermission
+ */
+public class TestMissingPermission {
+
+    public static void main(String... args) throws Exception {
+        testOpenRepository();
+        testRecordingStream();
+    }
+
+    private static void testRecordingStream() throws IOException {
+        try {
+            try (EventStream es = EventStream.openRepository()) {
+                throw new AssertionError("Should not be able to create EventStream without FlightRecorderPermission");
+            }
+        } catch (SecurityException se) {
+            // OK, as expected
+        }
+    }
+
+    private static void testOpenRepository() throws IOException {
+        try {
+            try (RecordingStream es = new RecordingStream()) {
+                throw new AssertionError("Should not be able to create RecordingStream without FlightRecorderPermission");
+            }
+        } catch (SecurityException se) {
+            // OK, as expected
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestRecordingFile.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.security;
+
+import java.nio.file.Paths;
+
+import jdk.jfr.consumer.RecordingFile;
+
+/**
+ * @test
+ * @summary Test that a recording file can't be opened without permissions
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run driver jdk.jfr.api.consumer.security.DriverRecordingDumper
+ *      test-recording-file.jfr
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy
+ *      jdk.jfr.api.consumer.security.TestRecordingFile
+ *      test-recording-file.jfr
+ */
+public class TestRecordingFile {
+    public static void main(String... args) throws Exception {
+        try {
+            RecordingFile.readAllEvents(Paths.get(args[0]));
+            throw new AssertionError("Expected SecurityException");
+        } catch (SecurityException se) {
+            // OK, as expected
+            return;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestRecordingStream.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.security;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.consumer.RecordingStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests that a RecordingStream works using only
+ *          FlightRecordingPermission("accessFlightRecorder")
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=local-streaming.policy
+ *      jdk.jfr.api.consumer.security.TestStreamingLocal
+ */
+public class TestRecordingStream {
+
+    public static void main(String... args) throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        try (RecordingStream r = new RecordingStream()) {
+            // Enable JVM event, no write permission needed
+            r.enable(EventNames.JVMInformation);
+            r.setStartTime(Instant.EPOCH);
+            r.onEvent(EventNames.JVMInformation, e -> {
+                latch.countDown();
+            });
+            r.startAsync();
+            latch.await();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingFile.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.security;
+
+import java.nio.file.Paths;
+
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Test that an event file stream can't be opened without permissions
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run driver jdk.jfr.api.consumer.security.DriverRecordingDumper
+ *      test-streaming-file.jfr
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy
+ *      jdk.jfr.api.consumer.security.TestStreamingFile
+ *      test-streaming-file.jfr
+ */
+public class TestStreamingFile {
+
+    public static void main(String... args) throws Exception {
+        try (EventStream es = EventStream.openFile(Paths.get(args[0]))) {
+            throw new AssertionError("Expected SecurityException");
+        } catch (SecurityException se) {
+            // OK, as expected
+            return;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingLocal.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.security;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Tests that local streaming works using only
+ *          FlightRecordingPermission("accessFlightRecorder")
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=local-streaming.policy
+ *      jdk.jfr.api.consumer.security.TestStreamingLocal
+ */
+public class TestStreamingLocal {
+
+    public static void main(String... args) throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        try (Recording r = new Recording()) {
+            // Enable JVM event, no write permission needed
+            r.enable(EventNames.JVMInformation);
+            r.start();
+            try (Recording r2 = new Recording()){
+                r2.start();
+                r2.stop();
+            }
+            r.stop();
+            try (EventStream es = EventStream.openRepository()) {
+                es.setStartTime(Instant.EPOCH);
+                es.onEvent("jdk.JVMInformation", e -> {
+                    latch.countDown();
+                });
+                es.startAsync();
+                latch.await();
+            }
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingRemote.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.security;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.time.Instant;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+/**
+ * @test
+ * @summary Test that a stream can be opened against a remote repository using
+ *          only file permission
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm jdk.jfr.api.consumer.security.TestStreamingRemote
+ */
+public class TestStreamingRemote {
+
+    private static final String SUCCESS = "Success!";
+
+    public static class TestEvent extends Event {
+    }
+
+    public static class Test {
+        public static void main(String... args) throws Exception {
+            Path repo = Paths.get(args[0]);
+            System.out.println("Repository: " + repo);
+            try (EventStream es = EventStream.openRepository(repo)) {
+                es.setStartTime(Instant.EPOCH);
+                es.onEvent(e -> {
+                    System.out.println(SUCCESS);
+                    es.close();
+                });
+                es.start();
+            }
+        }
+    }
+
+    public static void main(String... args) throws Exception {
+        try (Recording r = new Recording()) {
+            r.setFlushInterval(Duration.ofSeconds(1));
+            r.start();
+            String repository = System.getProperty("jdk.jfr.repository");
+            Path policy = createPolicyFile(repository);
+            TestEvent e = new TestEvent();
+            e.commit();
+            String[] c = new String[4];
+            c[0] = "-Djava.security.manager";
+            c[1] = "-Djava.security.policy=" + escapeBackslashes(policy.toString());
+            c[2] = Test.class.getName();
+            c[3] = repository;
+            OutputAnalyzer oa = ProcessTools.executeTestJvm(c);
+            oa.shouldContain(SUCCESS);
+        }
+    }
+
+    private static Path createPolicyFile(String repository) throws IOException {
+        Path policy = Paths.get("permission.policy").toAbsolutePath();
+        try (PrintWriter pw = new PrintWriter(policy.toFile())) {
+            pw.println("grant {");
+            // All the files and directories the contained in path
+            String dir = escapeBackslashes(repository);
+            String contents = escapeBackslashes(repository + File.separatorChar + "-");
+            pw.println("  permission java.io.FilePermission \"" + dir + "\", \"read\";");
+            pw.println("  permission java.io.FilePermission \"" + contents + "\", \"read\";");
+            pw.println("};");
+            pw.println();
+        }
+        System.out.println("Permission file: " + policy);
+        for (String line : Files.readAllLines(policy)) {
+            System.out.println(line);
+        }
+        System.out.println();
+        return policy;
+    }
+
+    // Needed for Windows
+    private static String escapeBackslashes(String text) {
+        return text.replace("\\", "\\\\");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/local-streaming.policy	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,4 @@
+// Minimum policy to stream locally
+grant {
+permission jdk.jfr.FlightRecorderPermission "accessFlightRecorder";
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/security/no-permission.policy	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,3 @@
+// No permission
+grant {
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestChunkGap.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Tests that a stream can gracefully handle chunk being removed in the
+ *          middle
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFilledChunks
+ */
+public class TestChunkGap {
+
+    static class StartEvent extends Event {
+    }
+
+    static class TestGapEvent extends Event {
+    }
+
+    static class EndEvent extends Event {
+    }
+
+    private final static AtomicInteger count = new AtomicInteger(0);
+
+    public static void main(String... args) throws Exception {
+
+        CountDownLatch gap = new CountDownLatch(1);
+        CountDownLatch receivedEvent = new CountDownLatch(1);
+
+        try (EventStream s = EventStream.openRepository()) {
+            try (Recording r1 = new Recording()) {
+                s.setStartTime(Instant.EPOCH);
+                s.onEvent(e -> {
+                    System.out.println(e);
+                    receivedEvent.countDown();
+                    try {
+                        gap.await();
+                    } catch (InterruptedException e1) {
+                        e1.printStackTrace();
+                    }
+                    count.incrementAndGet();
+                    if (e.getEventType().getName().equals(EndEvent.class.getName())) {
+                        s.close();
+                    }
+                });
+                s.startAsync();
+                r1.start();
+                StartEvent event1 = new StartEvent();
+                event1.commit();
+                receivedEvent.await();
+                r1.stop();
+
+                // create chunk that is removed
+                try (Recording r2 = new Recording()) {
+                    r2.enable(TestGapEvent.class);
+                    r2.start();
+                    TestGapEvent event2 = new TestGapEvent();
+                    event2.commit();
+                    r2.stop();
+                }
+                gap.countDown();
+                try (Recording r3 = new Recording()) {
+                    r3.enable(EndEvent.class);
+                    r3.start();
+                    EndEvent event3 = new EndEvent();
+                    event3.commit();
+                    r3.stop();
+
+                    s.awaitTermination();
+                    if (count.get() != 2) {
+                        throw new AssertionError("Expected 2 event, but got " + count);
+                    }
+                }
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestEmptyChunks.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test that it is possible to iterate over chunk without normal events
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestEmptyChunks
+ */
+public class TestEmptyChunks {
+    static class EndEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        CountDownLatch end = new CountDownLatch(1);
+        CountDownLatch firstFlush = new CountDownLatch(1);
+        try (RecordingStream es = new RecordingStream()) {
+            es.onEvent(EndEvent.class.getName(), e -> {
+                end.countDown();
+            });
+            es.onFlush(() -> {
+                firstFlush.countDown();
+            });
+            es.startAsync();
+            System.out.println("Invoked startAsync()");
+            // Wait for stream thread to start
+            firstFlush.await();
+            System.out.println("Stream thread active");
+            Recording r1 = new Recording();
+            r1.start();
+            System.out.println("Chunk 1 started");
+            Recording r2 = new Recording();
+            r2.start();
+            System.out.println("Chunk 2 started");
+            Recording r3 = new Recording();
+            r3.start();
+            System.out.println("Chunk 3 started");
+            r2.stop();
+            System.out.println("Chunk 4 started");
+            r3.stop();
+            System.out.println("Chunk 5 started");
+            EndEvent e = new EndEvent();
+            e.commit();
+            end.await();
+            r1.stop();
+            System.out.println("Chunk 5 ended");
+            r1.close();
+            r2.close();
+            r3.close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestEnableEvents.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Enabled;
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents from specified event
+ *          settings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ *
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestEnableEvents
+ */
+public class TestEnableEvents {
+
+    @Enabled(false)
+    static class HorseEvent extends Event {
+    }
+
+    @Enabled(false)
+    static class ElephantEvent extends Event {
+    }
+
+    @Enabled(false)
+    static class TigerEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        CountDownLatch elephantLatch = new CountDownLatch(1);
+        CountDownLatch tigerLatch = new CountDownLatch(1);
+        CountDownLatch horseLatch = new CountDownLatch(1);
+
+        FlightRecorder.addPeriodicEvent(ElephantEvent.class, () -> {
+            HorseEvent ze = new HorseEvent();
+            ze.commit();
+        });
+
+        try (RecordingStream s = new RecordingStream()) {
+            s.enable(HorseEvent.class.getName()).withPeriod(Duration.ofMillis(50));
+            s.enable(TigerEvent.class.getName());
+            s.enable(ElephantEvent.class.getName());
+            s.onEvent(TigerEvent.class.getName(), e -> {
+                System.out.println("Event: " + e.getEventType().getName());
+                System.out.println("Found tiger!");
+                tigerLatch.countDown();
+            });
+            s.onEvent(HorseEvent.class.getName(), e -> {
+                System.out.println("Found horse!");
+                horseLatch.countDown();
+            });
+            s.onEvent(ElephantEvent.class.getName(), e -> {
+                System.out.println("Found elelphant!");
+                elephantLatch.countDown();
+            });
+            s.startAsync();
+            TigerEvent te = new TigerEvent();
+            te.commit();
+            ElephantEvent ee = new ElephantEvent();
+            ee.commit();
+            elephantLatch.await();
+            horseLatch.await();
+            tigerLatch.await();
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestEventRegistration.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Registered;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test that it is possible to register new metadata in a chunk
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestEventRegistration
+ */
+public class TestEventRegistration {
+    @Registered(false)
+    static class StreamEvent1 extends Event {
+    }
+
+    @Registered(false)
+    static class StreamEvent2 extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+
+        CountDownLatch s1Latch = new CountDownLatch(1);
+        CountDownLatch s2Latch = new CountDownLatch(1);
+        try (RecordingStream es = new RecordingStream()) {
+            es.onEvent(StreamEvent1.class.getName(), e -> {
+                s1Latch.countDown();
+            });
+            es.onEvent(StreamEvent2.class.getName(), e -> {
+                s2Latch.countDown();
+            });
+            es.startAsync();
+            System.out.println("Registering " + StreamEvent1.class.getName());
+            FlightRecorder.register(StreamEvent1.class);
+            StreamEvent1 s1 = new StreamEvent1();
+            s1.commit();
+            System.out.println(StreamEvent1.class.getName() + " commited");
+            System.out.println("Awaiting latch for " + StreamEvent1.class.getName());
+            s1Latch.await();
+            System.out.println();
+            System.out.println("Registering " + StreamEvent2.class.getName());
+            FlightRecorder.register(StreamEvent2.class);
+            StreamEvent2 s2 = new StreamEvent2();
+            s2.commit();
+            System.out.println(StreamEvent2.class.getName() + " commited");
+            System.out.println("Awaiting latch for " + StreamEvent2.class.getName());
+            s2Latch.await();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.util.Random;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Test that it is possible to iterate over chunk with normal events
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFilledChunks
+ */
+public class TestFilledChunks {
+
+    static class FillEvent extends Event {
+        String message;
+        int value;
+        int id;
+    }
+
+    static class EndEvent extends Event {
+    }
+
+    // Will generate about 100 MB of data, or 8-9 chunks
+    private static final int EVENT_COUNT = 5_000_000;
+
+    public static void main(String... args) throws Exception {
+        try (RecordingStream rs = new RecordingStream()) {
+            rs.onEvent(FillEvent.class.getName(), e -> {
+                int id = e.getInt("id");
+                // Some events may get lost due to
+                // buffer being full.
+                if (id > EVENT_COUNT / 2) {
+                    rs.close();
+                }
+            });
+            rs.startAsync();
+            long seed = System.currentTimeMillis();
+            System.out.println("Random seed: " + seed);
+            Random r = new Random(seed);
+            for (int i = 1; i < EVENT_COUNT; i++) {
+                FillEvent f = new FillEvent();
+                f.message = i % 2 == 0 ? "hello, hello, hello, hello, hello!" : "hi!";
+                f.value = r.nextInt(10000);
+                f.id = i;
+                f.commit();
+                if (i % 1_000_000 == 0) {
+                    System.out.println("Emitted " + i + " events");
+                }
+            }
+            rs.awaitTermination();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestFiltering.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.jfr.Event;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to filter a stream for an event
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestFiltering
+ */
+public class TestFiltering {
+
+    static class SnakeEvent extends Event {
+        int id;
+    }
+
+    static class EelEvent extends Event {
+        int id;
+    }
+
+    public static void main(String... args) throws Exception {
+        CountDownLatch l = new CountDownLatch(1);
+        String eventName = SnakeEvent.class.getName();
+        AtomicInteger idCounter = new AtomicInteger(-1);
+        try (RecordingStream e = new RecordingStream()) {
+            e.onEvent(eventName, event -> {
+                if (!event.getEventType().getName().equals(eventName)) {
+                    throw new InternalError("Unexpected event " + e);
+                }
+                if (event.getInt("id") != idCounter.incrementAndGet()) {
+                    throw new InternalError("Incorrect id");
+                }
+                if (idCounter.get() == 99) {
+                    l.countDown();
+                }
+            });
+            e.startAsync();
+            for (int i = 0; i < 100; i++) {
+                SnakeEvent se = new SnakeEvent();
+                se.id = i;
+                se.commit();
+
+                EelEvent ee = new EelEvent();
+                ee.id = i;
+                ee.commit();
+            }
+            l.await();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestLatestEvent.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Name;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that EventStream::openRepository() read from the latest flush
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestLatestEvent
+ */
+public class TestLatestEvent {
+
+    @Name("NotLatest")
+    static class NotLatestEvent extends Event {
+
+        public int id;
+    }
+
+    @Name("Latest")
+    static class LatestEvent extends Event {
+    }
+
+    @Name("MakeChunks")
+    static class MakeChunks extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        CountDownLatch notLatestEvent = new CountDownLatch(6);
+        CountDownLatch beginChunks = new CountDownLatch(1);
+
+        try (RecordingStream r = new RecordingStream()) {
+            r.onEvent("MakeChunks", event-> {
+                System.out.println(event);
+                beginChunks.countDown();
+            });
+            r.onEvent("NotLatest", event -> {
+                System.out.println(event);
+                notLatestEvent.countDown();
+            });
+            r.startAsync();
+            MakeChunks e = new MakeChunks();
+            e.commit();
+
+            System.out.println("Waiting for first chunk");
+            beginChunks.await();
+            // Create 5 chunks with events in the repository
+            for (int i = 0; i < 5; i++) {
+                System.out.println("Creating empty chunk");
+                try (Recording r1 = new Recording()) {
+                    r1.start();
+                    NotLatestEvent notLatest = new NotLatestEvent();
+                    notLatest.id = i;
+                    notLatest.commit();
+                    r1.stop();
+                }
+            }
+            System.out.println("All empty chunks created");
+
+            // Create event in new chunk
+            NotLatestEvent notLatest = new NotLatestEvent();
+            notLatest.id = 5;
+            notLatest.commit();
+
+            // This latch ensures thatNotLatest has been
+            // flushed and a new valid position has been written
+            // to the chunk header
+            notLatestEvent.await(80, TimeUnit.SECONDS);
+            if (notLatestEvent.getCount() != 0) {
+               Recording rec =  FlightRecorder.getFlightRecorder().takeSnapshot();
+               Path p = Paths.get("error-not-latest.jfr").toAbsolutePath();
+               rec.dump(p);
+               System.out.println("Dumping repository as a file for inspection at " + p);
+               throw new Exception("Timeout 80 s. Expected 6 event, but got "  + notLatestEvent.getCount());
+            }
+
+            try (EventStream s = EventStream.openRepository()) {
+                System.out.println("EventStream opened");
+                AtomicBoolean foundLatest = new AtomicBoolean();
+                s.onEvent(event -> {
+                    String name = event.getEventType().getName();
+                    System.out.println("Found event " + name);
+                    foundLatest.set(name.equals("Latest"));
+                });
+                s.startAsync();
+                // Must loop here as there is no guarantee
+                // that the parser thread starts before event
+                // is flushed
+                while (!foundLatest.get()) {
+                    LatestEvent latest = new LatestEvent();
+                    latest.commit();
+                    System.out.println("Latest event emitted. Waiting 1 s ...");
+                    Thread.sleep(1000);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRecordingBefore.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to start a stream when there are
+ *          already chunk in the repository
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestRecordingBefore
+ */
+public class TestRecordingBefore {
+
+    static class SnakeEvent extends Event {
+        int id;
+    }
+
+    public static void main(String... args) throws Exception {
+
+        try (Recording r1 = new Recording()) {
+            r1.start();
+            emitSnakeEvent(1);
+            emitSnakeEvent(2);
+            emitSnakeEvent(3);
+            // Force a chunk rotation
+            try (Recording r2 = new Recording()) {
+                r2.start();
+                emitSnakeEvent(4);
+                emitSnakeEvent(5);
+                emitSnakeEvent(6);
+                r2.stop();
+            }
+            r1.stop();
+            // Two chunks should now exist in the repository
+            AtomicBoolean fail = new AtomicBoolean(false);
+            CountDownLatch lastEvent = new CountDownLatch(1);
+            try (RecordingStream rs = new RecordingStream()) {
+                rs.onEvent(e -> {
+                    long id = e.getLong("id");
+                    if (id < 7) {
+                        System.out.println("Found unexpected id " + id);
+                        fail.set(true);
+                    }
+                    if (id == 9) {
+                        lastEvent.countDown();
+                    }
+                });
+                rs.startAsync();
+                emitSnakeEvent(7);
+                emitSnakeEvent(8);
+                emitSnakeEvent(9);
+                lastEvent.await();
+                if (fail.get()) {
+                    throw new Exception("Found events from a previous recording");
+                }
+            }
+        }
+    }
+
+    static void emitSnakeEvent(int id) {
+        SnakeEvent e = new SnakeEvent();
+        e.id = id;
+        e.commit();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRemovedChunks.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Tests that a stream can gracefully handle chunk being removed
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr*=info jdk.jfr.api.consumer.streaming.TestRemovedChunks
+ */
+public class TestRemovedChunks {
+    private final static CountDownLatch parkLatch = new CountDownLatch(1);
+    private final static CountDownLatch removalLatch = new CountDownLatch(1);
+    private final static CountDownLatch IFeelFineLatch = new CountDownLatch(1);
+
+    static class DataEvent extends Event {
+        double double1;
+        double double2;
+        double double3;
+        double double4;
+        double double5;
+    }
+
+    static class ParkStream extends Event {
+    }
+
+    static class IFeelFine extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+
+        try (RecordingStream s = new RecordingStream()) {
+            s.setMaxSize(5_000_000);
+            s.onEvent(ParkStream.class.getName(), e -> {
+                parkLatch.countDown();
+                await(removalLatch);
+
+            });
+            s.onEvent(IFeelFine.class.getName(), e -> {
+                IFeelFineLatch.countDown();
+            });
+            s.startAsync();
+            // Fill first chunk with data
+            emitData(1_000_000);
+            // Park stream
+            ParkStream ps = new ParkStream();
+            ps.commit();
+            await(parkLatch);
+            // Rotate and emit data that exceeds maxSize
+            for (int i = 0; i< 10;i++) {
+                try (Recording r = new Recording()) {
+                    r.start();
+                    emitData(1_000_000);
+                }
+            }
+            // Emit final event
+            IFeelFine i = new IFeelFine();
+            i.commit();
+            // Wake up parked stream
+            removalLatch.countDown();
+            // Await event things gone bad
+            await(IFeelFineLatch);
+        }
+    }
+
+    private static void await(CountDownLatch latch) throws Error {
+        try {
+            latch.await();
+        } catch (InterruptedException e1) {
+            throw new Error("Latch interupted");
+        }
+    }
+
+    private static void emitData(int amount) throws InterruptedException {
+        int count = 0;
+        while (amount > 0) {
+            DataEvent de = new DataEvent();
+            // 5 doubles are 40 bytes bytes
+            // and event size, event type, thread,
+            // start time, duration and stack trace about 15 bytes
+            de.double1 = 0.0;
+            de.double2 = 1.0;
+            de.double3 = 2.0;
+            de.double4 = 3.0;
+            de.double5 = 4.0;
+            de.commit();
+            amount -= 55;
+            count++;
+            //
+            if (count % 100_000 == 0) {
+                Thread.sleep(10);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRepositoryMigration.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.jfr.jcmd.JcmdHelper;
+
+/**
+ * @test
+ * @summary Verifies that is possible to stream from a repository that is being
+ *          moved.
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestRepositoryMigration
+ */
+public class TestRepositoryMigration {
+    static class MigrationEvent extends Event {
+        int id;
+    }
+
+    public static void main(String... args) throws Exception {
+        Path newRepository = Paths.get("new-repository");
+        CountDownLatch event1 = new CountDownLatch(1);
+        CountDownLatch event2 = new CountDownLatch(1);
+
+        try (EventStream es = EventStream.openRepository()) {
+            es.setStartTime(Instant.EPOCH);
+            es.onEvent(e -> {
+                System.out.println(e);
+                if (e.getInt("id") == 1) {
+                    event1.countDown();
+                }
+                if (e.getInt("id") == 2) {
+                    event2.countDown();
+                }
+            });
+            es.startAsync();
+            System.out.println("Started es.startAsync()");
+
+            try (Recording r = new Recording()) {
+                r.setFlushInterval(Duration.ofSeconds(1));
+                r.start();
+                // Chunk in default repository
+                MigrationEvent e1 = new MigrationEvent();
+                e1.id = 1;
+                e1.commit();
+                event1.await();
+                System.out.println("Passed the event1.await()");
+                JcmdHelper.jcmd("JFR.configure", "repositorypath=" + newRepository.toAbsolutePath());
+                // Chunk in new repository
+                MigrationEvent e2 = new MigrationEvent();
+                e2.id = 2;
+                e2.commit();
+                r.stop();
+                event2.await();
+                System.out.println("Passed the event2.await()");
+                // Verify that it happened in new repository
+                if (!Files.exists(newRepository)) {
+                    throw new AssertionError("Could not find repository " + newRepository);
+                }
+                System.out.println("Listing contents in new repository:");
+                boolean empty = true;
+                for (Path p : Files.newDirectoryStream(newRepository)) {
+                    System.out.println(p.toAbsolutePath());
+                    empty = false;
+                }
+                System.out.println();
+                if (empty) {
+                    throw new AssertionError("Could not find contents in new repository location " + newRepository);
+                }
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRepositoryProperty.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Properties;
+
+import com.sun.tools.attach.AttachNotSupportedException;
+import com.sun.tools.attach.VirtualMachine;
+
+import jdk.jfr.Recording;
+import jdk.test.lib.dcmd.CommandExecutor;
+import jdk.test.lib.dcmd.PidJcmdExecutor;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to access JFR repository from a system
+ *          property
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @modules jdk.attach
+ *          jdk.jfr
+ * @run main/othervm -Djdk.attach.allowAttachSelf=true jdk.jfr.api.consumer.streaming.TestRepositoryProperty
+ */
+public class TestRepositoryProperty {
+
+    private final static String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
+
+    public static void main(String... args) throws Exception {
+        testBeforeInitialization();
+        testAfterInitialization();
+        testFromAgent();
+        testAfterChange();
+    }
+
+    private static void testFromAgent() throws AttachNotSupportedException, IOException {
+        String pidText = String.valueOf(ProcessHandle.current().pid());
+        VirtualMachine vm = VirtualMachine.attach(pidText);
+        Properties p = vm.getSystemProperties();
+        String location = (String) p.get(JFR_REPOSITORY_LOCATION_PROPERTY);
+        if (location == null) {
+            throw new AssertionError("Could not find repository path in agent properties");
+        }
+        Path path = Path.of(location);
+        if (!Files.isDirectory(path)) {
+            throw new AssertionError("Repository path doesn't point to directory");
+        }
+    }
+
+    private static void testAfterChange() {
+        Path newRepository = Path.of(".").toAbsolutePath();
+
+        String cmd = "JFR.configure repository=" +  newRepository.toString();
+        CommandExecutor executor = new PidJcmdExecutor();
+        executor.execute(cmd);
+        String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
+        if (newRepository.toString().equals(location)) {
+            throw new AssertionError("Repository path not updated after it has been changed");
+        }
+    }
+
+    private static void testAfterInitialization() {
+        try (Recording r = new Recording()) {
+            r.start();
+            String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
+            if (location == null) {
+                throw new AssertionError("Repository path should exist after JFR is initialized");
+            }
+            System.out.println("repository=" + location);
+            Path p = Path.of(location);
+            if (!Files.isDirectory(p)) {
+                throw new AssertionError("Repository path doesn't point to directory");
+            }
+        }
+
+    }
+
+    private static void testBeforeInitialization() {
+        String location = System.getProperty(JFR_REPOSITORY_LOCATION_PROPERTY);
+        if (location != null) {
+            throw new AssertionError("Repository path should not exist before JFR is initialized");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Name;
+import jdk.jfr.Period;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents of ongoing
+ *          recordings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+streaming=trace
+ *      jdk.jfr.api.consumer.streaming.TestStartMultiChunk
+ */
+public class TestStartMultiChunk {
+
+    @Period("10 s")
+    @Name("Zebra")
+    static class ZebraEvent extends Event {
+    }
+
+    @Name("Cat")
+    static class CatEvent extends Event {
+    }
+
+    @Name("Dog")
+    static class DogEvent extends Event {
+    }
+
+    @Name("Mouse")
+    static class MouseEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        CountDownLatch dogLatch = new CountDownLatch(1);
+        CountDownLatch catLatch = new CountDownLatch(1);
+        CountDownLatch mouseLatch = new CountDownLatch(1);
+        CountDownLatch zebraLatch = new CountDownLatch(3);
+
+        FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> {
+            ZebraEvent ze = new ZebraEvent();
+            ze.commit();
+            System.out.println("Zebra emitted");
+        });
+
+        try (RecordingStream s = new RecordingStream()) {
+            s.onEvent("Cat", e -> {
+                System.out.println("Found cat!");
+                catLatch.countDown();
+            });
+            s.onEvent("Dog", e -> {
+                System.out.println("Found dog!");
+                dogLatch.countDown();
+            });
+            s.onEvent("Zebra", e -> {
+                System.out.println("Found zebra!");
+                zebraLatch.countDown();
+            });
+            s.onEvent("Mouse", e -> {
+                System.out.println("Found mouse!");
+                mouseLatch.countDown();
+            });
+            s.startAsync();
+            System.out.println("Stream recoding started");
+
+            try (Recording r1 = new Recording()) {
+                r1.start();
+                System.out.println("r1.start()");
+                MouseEvent me = new MouseEvent();
+                me.commit();
+                System.out.println("Mouse emitted");
+                mouseLatch.await();
+                try (Recording r2 = new Recording()) { // force chunk rotation
+                                                       // in stream
+                    r2.start();
+                    System.out.println("r2.start()");
+                    DogEvent de = new DogEvent();
+                    de.commit();
+                    System.out.println("Dog emitted");
+                    dogLatch.await();
+                    CatEvent ce = new CatEvent();
+                    ce.commit();
+                    System.out.println("Cat emitted");
+                    catLatch.await();
+                    zebraLatch.await();
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartSingleChunk.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Period;
+import jdk.jfr.consumer.RecordingStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to stream contents of ongoing
+ *          recordings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+streaming=trace
+ *      jdk.jfr.api.consumer.streaming.TestStartSingleChunk
+ */
+public class TestStartSingleChunk {
+
+    @Period("500 ms")
+    static class ElkEvent extends Event {
+    }
+
+    static class FrogEvent extends Event {
+    }
+
+    static class LionEvent extends Event {
+    }
+
+    public static void main(String... args) throws Exception {
+        CountDownLatch frogLatch = new CountDownLatch(1);
+        CountDownLatch lionLatch = new CountDownLatch(1);
+        CountDownLatch elkLatch = new CountDownLatch(3);
+
+        FlightRecorder.addPeriodicEvent(ElkEvent.class, () -> {
+            ElkEvent ee = new ElkEvent();
+            ee.commit();
+        });
+        try (RecordingStream s = new RecordingStream()) {
+            s.onEvent(ElkEvent.class.getName(), e -> {
+                System.out.println("Found elk!");
+                elkLatch.countDown();
+            });
+            s.onEvent(LionEvent.class.getName(), e -> {
+                System.out.println("Found lion!");
+                lionLatch.countDown();
+            });
+            s.onEvent(FrogEvent.class.getName(), e -> {
+                System.out.println("Found frog!");
+                frogLatch.countDown();
+            });
+            s.startAsync();
+            FrogEvent fe = new FrogEvent();
+            fe.commit();
+
+            LionEvent le = new LionEvent();
+            le.commit();
+
+            frogLatch.await();
+            lionLatch.await();
+            elkLatch.await();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestUnstarted.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.consumer.streaming;
+
+import jdk.jfr.consumer.EventStream;
+
+/**
+ * @test
+ * @summary Verifies that it is possible to open a stream when a repository doesn't
+ *          exists
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.consumer.streaming.TestUnstarted
+ */
+public class TestUnstarted {
+    public static void main(String... args) throws Exception {
+        try (EventStream es = EventStream.openRepository()) {
+            es.onEvent(e -> {
+                // ignore
+            });
+            es.startAsync();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/event/TestEventDuration.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.event;
+
+import java.util.List;
+
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.test.lib.jfr.Events;
+import jdk.test.lib.jfr.SimpleEvent;
+
+/**
+ * @test
+ * @summary Tests that a duration is recorded.
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.event.TestEventDuration
+ */
+public class TestEventDuration {
+
+    public static int counter;
+
+    public static void main(String[] args) throws Exception {
+
+        try(Recording r = new Recording()) {
+            r.start();
+            SimpleEvent e = new SimpleEvent();
+            e.begin();
+            for (int i = 0; i < 10_000;i++) {
+                counter+=i;
+            }
+            e.end();
+            e.commit();
+
+            r.stop();
+            List<RecordedEvent> events = Events.fromRecording(r);
+            if (events.get(0).getDuration().toNanos() < 1) {
+                throw new AssertionError("Expected a duration");
+            }
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/api/recording/time/TestSetFlushInterval.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.api.recording.time;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.EventStream;
+import jdk.test.lib.Asserts;
+
+/**
+ * @test
+ * @key jfr
+ * @summary Test Recording::SetFlushInterval(...) and
+ *          Recording::getFlushInterval()
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm jdk.jfr.api.recording.time.TestSetFlushInterval
+ */
+
+public class TestSetFlushInterval {
+
+    public static void main(String[] args) throws Throwable {
+        testSetGet();
+        testSetNull();
+        testFlush();
+    }
+
+    static void testFlush() throws Exception {
+        CountDownLatch flush = new CountDownLatch(1);
+        try (EventStream es = EventStream.openRepository()) {
+            es.onFlush(() -> {
+                flush.countDown();
+            });
+            es.startAsync();
+            try (Recording r = new Recording()) {
+                r.setFlushInterval(Duration.ofSeconds(1));
+                r.start();
+                flush.await();
+            }
+        }
+    }
+
+    static void testSetNull() {
+        try (Recording r = new Recording()) {
+            r.setFlushInterval(null);
+            Asserts.fail("Expected NullPointerException");
+        } catch (NullPointerException npe) {
+            // as expected
+        }
+    }
+
+    static void testSetGet() {
+        try (Recording r = new Recording()) {
+            Duration a = Duration.ofNanos(21378461289374646L);
+            r.setFlushInterval(a);
+            Duration b = r.getFlushInterval();
+            Asserts.assertEQ(a, b);
+        }
+    }
+
+}
--- a/test/jdk/jdk/jfr/event/compiler/TestCompilerStats.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerStats.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -59,8 +59,8 @@
             Events.assertField(event, "standardCompileCount").atLeast(0);
             Events.assertField(event, "osrBytesCompiled").atLeast(0L);
             Events.assertField(event, "standardBytesCompiled").atLeast(0L);
-            Events.assertField(event, "nmetodsSize").atLeast(0L);
-            Events.assertField(event, "nmetodCodeSize").atLeast(0L);
+            Events.assertField(event, "nmethodsSize").atLeast(0L);
+            Events.assertField(event, "nmethodCodeSize").atLeast(0L);
             Events.assertField(event, "peakTimeSpent").atLeast(0L);
             Events.assertField(event, "totalTimeSpent").atLeast(0L);
         }
--- a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java	Mon Nov 04 11:25:55 2019 +0000
@@ -59,7 +59,8 @@
  * @library /test/lib /test/jdk
  * @build sun.hotspot.WhiteBox
  * @run main ClassFileInstaller sun.hotspot.WhiteBox
- * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:MaxTenuringThreshold=1 -Xms20M -Xmx20M
+ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+AlwaysTenure
+ *      -Xms20M -Xmx20M -Xlog:gc=debug,gc+heap*=debug,gc+ergo*=debug,gc+start=debug
  *      -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0 -XX:G1HeapRegionSize=1m
  *      -XX:+UseG1GC -XX:+UseStringDeduplication
  *      -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
@@ -75,7 +76,7 @@
         recording.start();
 
         // create more weak garbage than can fit in this heap (-Xmx20m), will force collection of weak references
-        weakRefs = range(1, 100)
+        weakRefs = range(1, 30)
             .mapToObj(n -> new WeakReference<>(new byte[1_000_000]))
             .collect(toList()); // force evaluation of lazy stream (all weak refs must be created)
 
@@ -150,8 +151,7 @@
             toUnreachable.add(new byte[arraySize]);
         });
 
-        // Do two young collections, MaxTenuringThreshold=1 will force promotion.
-        getWhiteBox().youngGC();
+        // Do one young collection, AlwaysTenure will force promotion.
         getWhiteBox().youngGC();
 
         // Check it is promoted & keep alive
@@ -167,11 +167,10 @@
 
     /**
     * The necessary condition for guaranteed mixed GC is running in VM with the following flags:
-    * -XX:+UnlockExperimentalVMOptions -XX:MaxTenuringThreshold=1 -Xms{HEAP_SIZE}M
+    * -XX:+UnlockExperimentalVMOptions -XX:+AlwaysTenure -Xms{HEAP_SIZE}M
     * -Xmx{HEAP_SIZE}M -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0
     * -XX:G1HeapRegionSize={REGION_SIZE}m
     *
-    * @param provokeSize The size to allocate to provoke the start of a mixed gc (half heap size?)
     * @param g1HeapRegionSize The size of your regions in bytes
     */
     public static void provokeMixedGC(int g1HeapRegionSize) {
--- a/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -81,6 +81,13 @@
         Arrays.asList("DumpReason")
     );
 
+    // Experimental events
+    private static final Set<String> experimentalEvents = new HashSet<>(
+      Arrays.asList(
+                    "Flush", "FlushStorage", "FlushStacktrace",
+                    "FlushStringPool", "FlushMetadata", "FlushTypeSet")
+    );
+
 
     public static void main(String[] args) throws Exception {
         for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) {
@@ -138,6 +145,10 @@
             }
         }
 
+        // remove experimental events from eventsFromEventNamesClass since jfrEventTypes
+        // excludes experimental events
+        eventsFromEventNamesClass.removeAll(experimentalEvents);
+
         if (!jfrEventTypes.equals(eventsFromEventNamesClass)) {
             String exceptionMsg = "Events declared in jdk.test.lib.jfr.EventNames differ " +
                          "from events returned by FlightRecorder.getEventTypes()";
--- a/test/jdk/jdk/jfr/event/oldobject/TestLargeRootSet.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/jdk/jfr/event/oldobject/TestLargeRootSet.java	Mon Nov 04 11:25:55 2019 +0000
@@ -26,15 +26,19 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Random;
+import java.util.Vector;
 import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CyclicBarrier;
 
 import jdk.jfr.Recording;
 import jdk.jfr.consumer.RecordedClass;
 import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordedFrame;
+import jdk.jfr.consumer.RecordedMethod;
 import jdk.jfr.consumer.RecordedObject;
+import jdk.jfr.consumer.RecordedStackTrace;
 import jdk.jfr.internal.test.WhiteBox;
-import jdk.test.lib.Asserts;
 import jdk.test.lib.jfr.EventNames;
 import jdk.test.lib.jfr.Events;
 
@@ -50,13 +54,13 @@
 public class TestLargeRootSet {
 
     private static final int THREAD_COUNT = 50;
+    private static final Random RANDOM = new Random(4711);
+    public static Vector<StackObject[]> temporaries = new Vector<>(OldObjects.MIN_SIZE);
 
     private static class RootThread extends Thread {
         private final CyclicBarrier barrier;
         private int maxDepth = OldObjects.MIN_SIZE / THREAD_COUNT;
 
-        public List<StackObject[]> temporaries = new ArrayList<>(maxDepth);
-
         RootThread(CyclicBarrier cb) {
             this.barrier = cb;
         }
@@ -67,8 +71,9 @@
 
         private void buildRootObjects() {
             if (maxDepth-- > 0) {
-                // Allocate array to trigger sampling code path for interpreter / c1
-                StackObject[] stackObject = new StackObject[0];
+                // Allocate array to trigger sampling code path for interpreter
+                // / c1
+                StackObject[] stackObject = new StackObject[RANDOM.nextInt(7)];
                 temporaries.add(stackObject); // make sure object escapes
                 buildRootObjects();
             } else {
@@ -91,37 +96,73 @@
 
     public static void main(String[] args) throws Exception {
         WhiteBox.setWriteAllObjectSamples(true);
-
-        List<RootThread> threads = new ArrayList<>();
-        try (Recording r = new Recording()) {
-            r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity");
-            r.start();
-            CyclicBarrier cb = new CyclicBarrier(THREAD_COUNT + 1);
-            for (int i = 0; i < THREAD_COUNT; i++) {
-                RootThread t = new RootThread(cb);
-                t.start();
-                if (i % 10 == 0) {
-                    // Give threads some breathing room before starting next batch
-                    Thread.sleep(100);
+        int attempt = 1;
+        while (true) {
+            System.out.println();
+            System.out.println();
+            System.out.println("ATTEMPT: " + attempt);
+            System.out.println("====================================");
+            List<RootThread> threads = new ArrayList<>();
+            try (Recording r = new Recording()) {
+                r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity");
+                r.start();
+                CyclicBarrier cb = new CyclicBarrier(THREAD_COUNT + 1);
+                for (int i = 0; i < THREAD_COUNT; i++) {
+                    RootThread t = new RootThread(cb);
+                    t.start();
+                    if (i % 10 == 0) {
+                        // Give threads some breathing room before starting next
+                        // batch
+                        Thread.sleep(100);
+                    }
+                    threads.add(t);
                 }
-                threads.add(t);
-            }
-            cb.await();
-            System.gc();
-            r.stop();
-            cb.await();
-            List<RecordedEvent> events = Events.fromRecording(r);
-            Events.hasEvents(events);
-            for (RecordedEvent e : events) {
-                RecordedObject ro = e.getValue("object");
-                RecordedClass rc = ro.getValue("type");
-                System.out.println(rc.getName());
-                if (rc.getName().equals(StackObject[].class.getName())) {
-                    return; // ok
+                cb.await();
+                System.gc();
+                r.stop();
+                cb.await();
+                List<RecordedEvent> events = Events.fromRecording(r);
+                Events.hasEvents(events);
+                int sample = 0;
+                for (RecordedEvent e : events) {
+                    RecordedObject ro = e.getValue("object");
+                    RecordedClass rc = ro.getValue("type");
+                    System.out.println("Sample: " + sample);
+                    System.out.println(" - allocationTime: " + e.getInstant("allocationTime"));
+                    System.out.println(" - type: " + rc.getName());
+                    RecordedObject root = e.getValue("root");
+                    if (root != null) {
+                        System.out.println(" - root:");
+                        System.out.println("   - description: " + root.getValue("description"));
+                        System.out.println("   - system: " + root.getValue("system"));
+                        System.out.println("   - type: " + root.getValue("type"));
+                    } else {
+                        System.out.println(" - root: N/A");
+                    }
+                    RecordedStackTrace stack = e.getStackTrace();
+                    if (stack != null) {
+                        System.out.println(" - stack:");
+                        int frameCount = 0;
+                        for (RecordedFrame frame : stack.getFrames()) {
+                            RecordedMethod m = frame.getMethod();
+                            System.out.println("      " + m.getType().getName() + "." + m.getName() + "(...)");
+                            frameCount++;
+                            if (frameCount == 10) {
+                                break;
+                            }
+                        }
+                    } else {
+                        System.out.println(" - stack: N/A");
+                    }
+                    System.out.println();
+                    if (rc.getName().equals(StackObject[].class.getName())) {
+                        return; // ok
+                    }
+                    sample++;
                 }
             }
-            Asserts.fail("Could not find root object");
+            attempt++;
         }
     }
+
 }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/runtime/TestFlush.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.event.runtime;
+
+import java.util.concurrent.CountDownLatch;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jdk.jfr.Event;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Period;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordingStream;
+import jdk.jfr.consumer.RecordedEvent;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.jfr.EventNames;
+
+/**
+ * @test
+ * @summary Verifies at the metalevel that stream contents are written to ongoing recordings
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @run main/othervm -Xlog:jfr+system+streaming=trace jdk.jfr.event.runtime.TestFlush
+ */
+public class TestFlush {
+    private static boolean flushEventAck = false;
+
+    @Period("2 s")
+    static class ZebraEvent extends Event {
+    }
+    static class CatEvent extends Event {
+    }
+    static class DogEvent extends Event {
+    }
+    static class MouseEvent extends Event {
+    }
+
+    public static void main(String... args) throws InterruptedException {
+        CountDownLatch dogLatch = new CountDownLatch(1);
+        CountDownLatch catLatch = new CountDownLatch(1);
+        CountDownLatch mouseLatch = new CountDownLatch(1);
+        CountDownLatch zebraLatch = new CountDownLatch(3);
+
+        FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> {
+            ZebraEvent ze = new ZebraEvent();
+            ze.commit();
+        });
+
+        try (RecordingStream rs = new RecordingStream()) {
+            rs.enable(EventNames.Flush);
+            rs.enable(EventNames.FlushStorage);
+            rs.enable(EventNames.FlushStacktrace);
+            rs.enable(EventNames.FlushStringPool);
+            rs.enable(EventNames.FlushMetadata);
+            rs.enable(EventNames.FlushTypeSet);
+            rs.onEvent(e -> {
+                switch (e.getEventType().getName()) {
+                    case EventNames.Flush:
+                        flushEventAck = true;
+                    case EventNames.FlushStorage:
+                    case EventNames.FlushStacktrace:
+                    case EventNames.FlushStringPool:
+                    case EventNames.FlushMetadata:
+                    case EventNames.FlushTypeSet:
+                        validateFlushEvent(e);
+                        return;
+                }
+                if (e.getEventType().getName().equals(CatEvent.class.getName())) {
+                    System.out.println("Found cat!");
+                    catLatch.countDown();
+                    return;
+                }
+                if (e.getEventType().getName().equals(DogEvent.class.getName())) {
+                    System.out.println("Found dog!");
+                    dogLatch.countDown();
+                    return;
+                }
+                if (e.getEventType().getName().equals(ZebraEvent.class.getName())) {
+                    System.out.println("Found zebra!");
+                    zebraLatch.countDown();
+                    return;
+                }
+                if (e.getEventType().getName().equals(MouseEvent.class.getName())) {
+                    System.out.println("Found mouse!");
+                    mouseLatch.countDown();
+                    return;
+                }
+                System.out.println("Unexpected event: " + e.getEventType().getName());
+            });
+
+            rs.startAsync();
+
+            try (Recording r1 = new Recording()) {
+                r1.start();
+                MouseEvent me = new MouseEvent();
+                me.commit();
+                System.out.println("Mouse emitted");
+                mouseLatch.await();
+                try (Recording r2 = new Recording()) { // force chunk rotation in stream
+                    r2.start();
+                    DogEvent de = new DogEvent();
+                    de.commit();
+                    System.out.println("Dog emitted");
+                    dogLatch.await();
+                    CatEvent ce = new CatEvent();
+                    ce.commit();
+                    System.out.println("Cat emitted");
+                    catLatch.await();
+                    zebraLatch.await();
+                    acknowledgeFlushEvent();
+                }
+            }
+        }
+    }
+
+    private static void printEvent(RecordedEvent re) {
+        System.out.println(re.getEventType().getName());
+        System.out.println(re.getStartTime().toEpochMilli());
+        System.out.println(re.getEndTime().toEpochMilli());
+    }
+
+    private static void printFlushEvent(RecordedEvent re) {
+        printEvent(re);
+        System.out.println("flushID: " + (long) re.getValue("flushId"));
+        System.out.println("elements: " + (long) re.getValue("elements"));
+        System.out.println("size: " + (long) re.getValue("size"));
+    }
+
+    private static void validateFlushEvent(RecordedEvent re) {
+        printFlushEvent(re);
+        Asserts.assertTrue(re.getEventType().getName().contains("Flush"), "invalid Event type");
+        Asserts.assertGT((long) re.getValue("flushId"), 0L, "Invalid flush ID");
+        Asserts.assertGT((long) re.getValue("elements"), 0L, "No elements");
+        Asserts.assertGT((long) re.getValue("size"), 0L, "Empty size");
+    }
+
+    private static void acknowledgeFlushEvent() {
+        Asserts.assertTrue(flushEventAck, "No Flush event");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/jcmd/TestJcmdStartFlushInterval.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.jcmd;
+
+import java.time.Duration;
+
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Recording;
+
+/**
+ * @test
+ * @summary Start a recording with a flush interval
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm jdk.jfr.jcmd.TestJcmdStartReadOnlyFile
+ */
+public class TestJcmdStartFlushInterval {
+
+    public static void main(String[] args) throws Exception {
+        JcmdHelper.jcmd("JFR.start","flush-interval=1s");
+        for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) {
+            Duration d = r.getFlushInterval();
+            if (d.equals(Duration.ofSeconds(1))) {
+                return; //OK
+            } else {
+                throw new Exception("Unexpected flush-interval=" + d);
+            }
+        }
+        throw new Exception("No recording found");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/jvm/TestThreadExclusion.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.jvm;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.internal.JVM;
+import jdk.jfr.Recording;
+
+import jdk.test.lib.jfr.EventNames;
+import jdk.test.lib.jfr.Events;
+
+import static jdk.test.lib.Asserts.assertTrue;
+
+/**
+ * @test
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib
+ * @modules jdk.jfr/jdk.jfr.internal
+ * @run main/othervm jdk.jfr.jvm.TestThreadExclusion
+ */
+
+/**
+ * Starts and stops a number of threads in order.
+ * Verifies that events are in the same order.
+ */
+public class TestThreadExclusion {
+    private final static String EVENT_NAME_THREAD_START = EventNames.ThreadStart;
+    private final static String EVENT_NAME_THREAD_END = EventNames.ThreadEnd;
+    private static final String THREAD_NAME_PREFIX = "TestThread-";
+    private static JVM jvm;
+
+    public static void main(String[] args) throws Throwable {
+        // Test Java Thread Start event
+        Recording recording = new Recording();
+        recording.enable(EVENT_NAME_THREAD_START).withThreshold(Duration.ofMillis(0));
+        recording.enable(EVENT_NAME_THREAD_END).withThreshold(Duration.ofMillis(0));
+        recording.start();
+        LatchedThread[] threads = startThreads();
+        long[] javaThreadIds = getJavaThreadIds(threads);
+        stopThreads(threads);
+        recording.stop();
+        List<RecordedEvent> events = Events.fromRecording(recording);
+        verifyThreadExclusion(events, javaThreadIds);
+    }
+
+    private static void verifyThreadExclusion(List<RecordedEvent> events, long[] javaThreadIds) throws Exception {
+        for (RecordedEvent event : events) {
+            System.out.println("Event:" + event);
+            final long eventJavaThreadId = event.getThread().getJavaThreadId();
+            for (int i = 0; i < javaThreadIds.length; ++i) {
+                if (eventJavaThreadId == javaThreadIds[i]) {
+                    throw new Exception("Event " + event.getEventType().getName() + " has a thread id " + eventJavaThreadId + " that should have been excluded");
+                }
+            }
+        }
+    }
+
+    private static LatchedThread[] startThreads() {
+        LatchedThread threads[] = new LatchedThread[10];
+        ThreadGroup threadGroup = new ThreadGroup("TestThreadGroup");
+        jvm = JVM.getJVM();
+        for (int i = 0; i < threads.length; i++) {
+            threads[i] = new LatchedThread(threadGroup, THREAD_NAME_PREFIX + i);
+            jvm.exclude(threads[i]);
+            threads[i].startThread();
+            System.out.println("Started thread id=" + threads[i].getId());
+        }
+        return threads;
+    }
+
+    private static long[] getJavaThreadIds(LatchedThread[] threads) {
+        long[] javaThreadIds = new long[threads.length];
+        for (int i = 0; i < threads.length; ++i) {
+            javaThreadIds[i] = threads[i].getId();
+        }
+        return javaThreadIds;
+    }
+
+    private static void stopThreads(LatchedThread[] threads) {
+        for (LatchedThread thread : threads) {
+            assertTrue(jvm.isExcluded(thread), "Thread " + thread + "should be excluded");
+            thread.stopThread();
+            while (thread.isAlive()) {
+                try {
+                    Thread.sleep(5);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private static class LatchedThread extends Thread {
+        private final CountDownLatch start = new CountDownLatch(1);
+        private final CountDownLatch stop = new CountDownLatch(1);
+
+        public LatchedThread(ThreadGroup threadGroup, String name) {
+            super(threadGroup, name);
+        }
+
+        public void run() {
+            start.countDown();
+            try {
+                stop.await();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+
+        public void startThread() {
+            this.start();
+            try {
+                start.await();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+
+        public void stopThread() {
+            stop.countDown();
+        }
+    }
+}
--- a/test/jdk/jdk/jfr/jvm/TestUnsupportedVM.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/jdk/jfr/jvm/TestUnsupportedVM.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -30,8 +30,10 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import jdk.jfr.AnnotationElement;
 import jdk.jfr.Configuration;
@@ -48,6 +50,7 @@
 import jdk.jfr.RecordingState;
 import jdk.jfr.SettingControl;
 import jdk.jfr.ValueDescriptor;
+import jdk.jfr.consumer.EventStream;
 import jdk.jfr.consumer.RecordedClass;
 import jdk.jfr.consumer.RecordedEvent;
 import jdk.jfr.consumer.RecordedFrame;
@@ -57,6 +60,7 @@
 import jdk.jfr.consumer.RecordedThread;
 import jdk.jfr.consumer.RecordedThreadGroup;
 import jdk.jfr.consumer.RecordingFile;
+import jdk.jfr.consumer.RecordingStream;
 import jdk.management.jfr.ConfigurationInfo;
 import jdk.management.jfr.EventTypeInfo;
 import jdk.management.jfr.FlightRecorderMXBean;
@@ -106,9 +110,11 @@
             RecordingState.class,
             SettingControl.class,
             SettingDescriptorInfo.class,
-            ValueDescriptor.class
+            ValueDescriptor.class,
+            EventStream.class,
+            RecordingStream.class
        };
-    // * @run main/othervm -Dprepare-recording=true jdk.jfr.jvm.TestUnsupportedVM
+
     @Label("My Event")
     @Description("My fine event")
     static class MyEvent extends Event {
@@ -125,7 +131,7 @@
             return;
         }
 
-        System.out.println("jdk.jfr.unsupportedvm=" + System.getProperty("jdk.jfr.unsupportedvm"));
+        System.out.println("jfr.unsupported.vm=" + System.getProperty("jfr.unsupported.vm"));
         // Class FlightRecorder
         if (FlightRecorder.isAvailable()) {
             throw new AssertionError("JFR should not be available on an unsupported VM");
@@ -136,6 +142,7 @@
         }
 
         assertIllegalStateException(() -> FlightRecorder.getFlightRecorder());
+        assertIllegalStateException(() -> new RecordingStream());
         assertSwallow(() -> FlightRecorder.addListener(new FlightRecorderListener() {}));
         assertSwallow(() -> FlightRecorder.removeListener(new FlightRecorderListener() {}));
         assertSwallow(() -> FlightRecorder.register(MyEvent.class));
@@ -174,8 +181,39 @@
         // Only run this part of tests if we are on VM
         // that can produce a recording file
         if (Files.exists(RECORDING_FILE)) {
+            boolean firstFileEvent = true;
             for(RecordedEvent re : RecordingFile.readAllEvents(RECORDING_FILE)) {
-                System.out.println(re);
+                // Print one event
+                if (firstFileEvent) {
+                    System.out.println(re);
+                    firstFileEvent = false;
+                }
+            }
+            AtomicBoolean firstStreamEvent = new AtomicBoolean(true);
+            try (EventStream es = EventStream.openFile(RECORDING_FILE)) {
+                es.onEvent(e -> {
+                    // Print one event
+                    if (firstStreamEvent.get()) {
+                        try {
+                            System.out.println(e);
+                            firstStreamEvent.set(false);
+                        } catch (Throwable t) {
+                            t.printStackTrace();
+                        }
+                    }
+                });
+                es.start();
+                if (firstStreamEvent.get()) {
+                    throw new AssertionError("Didn't print streaming event");
+                }
+            }
+
+            try (EventStream es = EventStream.openRepository()) {
+                es.onEvent(e -> {
+                    System.out.println(e);
+                });
+                es.startAsync();
+                es.awaitTermination(Duration.ofMillis(10));
             }
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/startupargs/TestFlushInterval.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.jfr.startupargs;
+
+import java.time.Duration;
+
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.Recording;
+
+/**
+ * @test
+ * @summary Start a recording with a flush interval
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main/othervm -XX:StartFlightRecording=flush-interval=1s jdk.jfr.startupargs.TestFlushInterval
+ */
+public class TestFlushInterval {
+
+    public static void main(String[] args) throws Exception {
+        for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) {
+            Duration d = r.getFlushInterval();
+            if (d.equals(Duration.ofSeconds(1))) {
+                return; //OK
+            } else {
+                throw new Exception("Unexpected flush-interval " + d);
+            }
+        }
+        throw new Exception("No recording found");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/nio/zipfs/CopyMoveTests.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.*;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import static java.lang.String.format;
+import static java.util.stream.Collectors.joining;
+import static org.testng.Assert.*;
+
+/**
+ * @test
+ * @bug 8223771
+ * @summary Test Files::copy and Files::move with Zip FS
+ * @modules jdk.zipfs
+ * @run testng/othervm CopyMoveTests
+ */
+public class CopyMoveTests {
+    // Enable debugging output
+    private static final boolean DEBUG = false;
+    // Path to current directory
+    private static final Path HERE = Path.of(".");
+    // Value to use when creating Zip Entries
+    private static final String ZIP_FILE_VALUE = "US Open 2019";
+    // Value used to create the OS file to be copied into/from a Zip File
+    private static final String OS_FILE_VALUE = "Hello World!";
+    private static final SecureRandom random = new SecureRandom();
+
+    /*
+     * DataProvider used to verify that a FileAlreadyExistsException is
+     * thrown with copying a file without the REPLACE_EXISTING option
+     */
+    @DataProvider(name = "zipfsMap")
+    private Object[][] zipfsMap() {
+        return new Object[][]{
+                {Map.of("create", "true"), ZipEntry.DEFLATED},
+                {Map.of("create", "true", "noCompression", "true"),
+                        ZipEntry.STORED},
+                {Map.of("create", "true", "noCompression", "false"),
+                        ZipEntry.DEFLATED}
+        };
+    }
+
+    /*
+     * DataProvider used to verify that an entry may be copied or moved within
+     * a Zip file system with the correct compression method
+     */
+    @DataProvider(name = "copyMoveMap")
+    private Object[][] copyMoveMap() {
+        return new Object[][]{
+                {Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED},
+                {Map.of("create", "true", "noCompression", "true"),
+                        ZipEntry.STORED, ZipEntry.DEFLATED},
+                {Map.of("create", "true", "noCompression", "false"),
+                        ZipEntry.DEFLATED, ZipEntry.STORED}
+        };
+    }
+
+    /**
+     * Validate that an entry that is copied within a Zip file is copied with
+     * the correct compression
+     *
+     * @param createMap           Zip FS properties to use when creating the Zip File
+     * @param compression         The compression used when writing the initial entries
+     * @param expectedCompression The compression to be used when copying the entry
+     * @throws Exception If an error occurs
+     */
+    @Test(dataProvider = "copyMoveMap", enabled = true)
+    public void copyTest(Map<String, String> createMap, int compression,
+                         int expectedCompression) throws Exception {
+        Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
+        Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
+        Entry e00 = Entry.of("Entry-00", expectedCompression, ZIP_FILE_VALUE);
+
+        Path zipFile = generatePath(HERE, "test", ".zip");
+        Files.deleteIfExists(zipFile);
+
+        // Create the Zip File with the initial entries
+        createZipFile(zipFile, createMap, e0, e1);
+        try (FileSystem zipfs = FileSystems.newFileSystem(zipFile,
+                Map.of("noCompression", expectedCompression == ZipEntry.STORED))) {
+            Files.copy(zipfs.getPath(e0.name), zipfs.getPath(e00.name));
+        }
+        // Verify entries e0, e1 and e00 exist
+        verify(zipFile, e0, e1, e00);
+        Files.deleteIfExists(zipFile);
+
+    }
+
+    /**
+     * Validate that an entry that is copied from one Zip file to another,
+     * is copied with the correct compression
+     *
+     * @param createMap           Zip FS properties to use when creating the Zip File
+     * @param compression         The compression used when writing the initial entries
+     * @param expectedCompression The compression to be used when copying the entry
+     * @throws Exception If an error occurs
+     */
+    @Test(dataProvider = "copyMoveMap", enabled = true)
+    public void copyZipToZipTest(Map<String, String> createMap, int compression,
+                            int expectedCompression) throws Exception {
+        Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
+        Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
+        Entry e00 = Entry.of("Entry-00", expectedCompression, ZIP_FILE_VALUE);
+
+        Path zipFile = generatePath(HERE, "test", ".zip");
+        Path zipFile2 = generatePath(HERE, "test", ".zip");
+        Files.deleteIfExists(zipFile);
+        Files.deleteIfExists(zipFile2);
+
+        createZipFile(zipFile, createMap, e0, e1);
+        try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, createMap);
+             FileSystem zipfsTarget = FileSystems.newFileSystem(zipFile2,
+                     Map.of("create", "true", "noCompression",
+                             expectedCompression == ZipEntry.STORED))) {
+            Files.copy(zipfs.getPath(e0.name), zipfsTarget.getPath(e00.name));
+        }
+        // Only 1 entry copied to the secondary Zip file
+        verify(zipFile2, e00);
+        // Verify  entries e0 and e1 remain in the original Zip file
+        verify(zipFile, e0, e1);
+        Files.deleteIfExists(zipFile);
+        Files.deleteIfExists(zipFile2);
+    }
+
+    /**
+     * Validate that an external file copied to a Zip file is copied with
+     * the correct compression
+     *
+     * @param createMap           Zip FS properties to use when creating the Zip File
+     * @param compression         The compression used when writing the initial entries
+     * @param expectedCompression The compression to be used when copying the entry
+     * @throws Exception If an error occurs
+     */
+    @Test(dataProvider = "copyMoveMap", enabled = true)
+    public void copyFromOsTest(Map<String, String> createMap, int compression,
+                           int expectedCompression) throws Exception {
+
+        Path osFile = generatePath(HERE, "test", ".txt");
+        Files.deleteIfExists(osFile);
+        Files.writeString(osFile, OS_FILE_VALUE);
+        Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
+        Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
+        Entry e00 = Entry.of("Entry-00", expectedCompression, OS_FILE_VALUE);
+
+        Path zipFile = generatePath(HERE, "test", ".zip");
+        Files.deleteIfExists(zipFile);
+
+        createZipFile(zipFile, createMap, e0, e1);
+        try (FileSystem zipfs = FileSystems.newFileSystem(zipFile,
+                Map.of("noCompression", expectedCompression == ZipEntry.STORED))) {
+            Files.copy(osFile, zipfs.getPath(e00.name));
+        }
+        verify(zipFile, e0, e1, e00);
+        Files.deleteIfExists(osFile);
+        Files.deleteIfExists(zipFile);
+    }
+
+    /**
+     * Validate that an entry that is copied from a Zip file to an OS file contains
+     * the correct bytes and the file remains in the Zip file
+     *
+     * @param createMap           Zip FS properties to use when creating the Zip File
+     * @param compression         The compression used when writing the initial entries
+     * @param expectedCompression The compression to be used when moving the entry
+     * @throws Exception If an error occurs
+     */
+    @Test(dataProvider = "copyMoveMap", enabled = true)
+    public void CopyFromZipTest(Map<String, String> createMap, int compression,
+                                int expectedCompression) throws Exception {
+
+        Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
+        Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
+
+        Path zipFile = generatePath(HERE, "test", ".zip");
+        Path osFile = generatePath(HERE, "test", ".txt");
+        Files.deleteIfExists(zipFile);
+        Files.deleteIfExists(osFile);
+
+        createZipFile(zipFile, createMap, e0, e1);
+        try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, Map.of())) {
+            Files.copy(zipfs.getPath(e0.name), osFile);
+        }
+
+        // Entries e0 and e1 should exist
+        verify(zipFile, e0, e1);
+        // Check to see if the file exists and the bytes match
+        assertTrue(Files.isRegularFile(osFile));
+        assertEquals(Files.readAllBytes(osFile), e0.bytes);
+        Files.deleteIfExists(zipFile);
+        Files.deleteIfExists(osFile);
+    }
+
+    /**
+     * Validate that an entry that is moved within a Zip file is moved with
+     * the correct compression
+     *
+     * @param createMap           Zip FS properties to use when creating the Zip File
+     * @param compression         The compression used when writing the initial entries
+     * @param expectedCompression The compression to be used when moving the entry
+     * @throws Exception If an error occurs
+     */
+    @Test(dataProvider = "copyMoveMap", enabled = true)
+    public void moveTest(Map<String, String> createMap, int compression,
+                         int expectedCompression) throws Exception {
+
+        Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
+        Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
+        Entry e00 = Entry.of("Entry-00", expectedCompression, ZIP_FILE_VALUE);
+
+        Path zipFile = generatePath(HERE, "test", ".zip");
+        Files.deleteIfExists(zipFile);
+
+        createZipFile(zipFile, createMap, e0, e1);
+        try (FileSystem zipfs = FileSystems.newFileSystem(zipFile,
+                Map.of("noCompression", expectedCompression == ZipEntry.STORED))) {
+            Files.move(zipfs.getPath(e0.name), zipfs.getPath(e00.name));
+        }
+        // Entry e0 should not exist but Entry e00 should
+        verify(zipFile, e1, e00);
+        Files.deleteIfExists(zipFile);
+    }
+
+    /**
+     * Validate that an entry that is moved one Zip file to another is moved with
+     * the correct compression
+     *
+     * @param createMap           Zip FS properties to use when creating the Zip File
+     * @param compression         The compression used when writing the initial entries
+     * @param expectedCompression The compression to be used when moving the entry
+     * @throws Exception If an error occurs
+     */
+    @Test(dataProvider = "copyMoveMap", enabled = true)
+    public void moveZipToZipTest(Map<String, String> createMap, int compression,
+                            int expectedCompression) throws Exception {
+
+        Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
+        Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
+        Entry e00 = Entry.of("Entry-00", expectedCompression, ZIP_FILE_VALUE);
+
+        Path zipFile = generatePath(HERE, "test", ".zip");
+        Path zipFile2 = generatePath(HERE, "test", ".zip");
+        Files.deleteIfExists(zipFile);
+        Files.deleteIfExists(zipFile2);
+
+        createZipFile(zipFile, createMap, e0, e1);
+        try (FileSystem zipfs = FileSystems.newFileSystem(zipFile,
+                Map.of("noCompression", expectedCompression == ZipEntry.STORED));
+             FileSystem zipfsTarget = FileSystems.newFileSystem(zipFile2,
+                     Map.of("create", "true", "noCompression",
+                             expectedCompression == ZipEntry.STORED))) {
+            Files.move(zipfs.getPath(e0.name), zipfsTarget.getPath(e00.name));
+        }
+        // Only Entry e00 should exist
+        verify(zipFile2, e00);
+        // Only Entry e1 should exist
+        verify(zipFile, e1);
+        Files.deleteIfExists(zipFile);
+        Files.deleteIfExists(zipFile2);
+    }
+
+    /**
+     * Validate that an entry that is moved from a Zip file to an OS file contains
+     * the correct bytes and is removed from the Zip file
+     *
+     * @param createMap           Zip FS properties to use when creating the Zip File
+     * @param compression         The compression used when writing the initial entries
+     * @param expectedCompression The compression to be used when moving the entry
+     * @throws Exception If an error occurs
+     */
+    @Test(dataProvider = "copyMoveMap", enabled = true)
+    public void moveFromZipTest(Map<String, String> createMap, int compression,
+                            int expectedCompression) throws Exception {
+
+        Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
+        Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
+
+        Path zipFile = generatePath(HERE, "test", ".zip");
+        Path osFile = generatePath(HERE, "test", ".txt");
+        Files.deleteIfExists(zipFile);
+        Files.deleteIfExists(osFile);
+
+        createZipFile(zipFile, createMap, e0, e1);
+        try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, Map.of())) {
+            Files.move(zipfs.getPath(e0.name), osFile);
+        }
+
+        // Only Entry e1 should exist
+        verify(zipFile, e1);
+        // Check to see if the file exists and the bytes match
+        assertTrue(Files.isRegularFile(osFile));
+        assertEquals(Files.readAllBytes(osFile), e0.bytes);
+        Files.deleteIfExists(zipFile);
+        Files.deleteIfExists(osFile);
+    }
+
+    /**
+     * Validate that a FileAlreadyExistsException is thrown when copying a
+     * file and not specifying the REPLACE_EXISTING option.
+     *
+     * @param createMap Properties used for creating the ZIP Filesystem
+     * @throws Exception if an error occurs
+     */
+    @Test(dataProvider = "zipfsMap", enabled = true)
+    public void testFAEWithCopy(Map<String, String> createMap,
+                                int compression) throws Exception {
+        if (DEBUG) {
+            System.out.printf("ZIP FS Map = %s%n ", formatMap(createMap));
+        }
+        Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
+        Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
+
+        Path zipFile = generatePath(HERE, "test", ".zip");
+        Files.deleteIfExists(zipFile);
+
+        createZipFile(zipFile, createMap, e0, e1);
+        try (FileSystem zipfs =
+                     FileSystems.newFileSystem(zipFile, createMap)) {
+            assertThrows(FileAlreadyExistsException.class, () ->
+                    Files.copy(zipfs.getPath(e0.name),
+                            zipfs.getPath(e1.name)));
+        }
+        Files.deleteIfExists(zipFile);
+    }
+
+    /**
+     * Generate a temporary file Path
+     *
+     * @param dir    Directory used to create the path
+     * @param prefix The prefix string used to create the path
+     * @param suffix The suffix string used to create the path
+     * @return Path that was generated
+     */
+    private static Path generatePath(Path dir, String prefix, String suffix) {
+        long n = random.nextLong();
+        String s = prefix + Long.toUnsignedString(n) + suffix;
+        Path name = dir.getFileSystem().getPath(s);
+        // the generated name should be a simple file name
+        if (name.getParent() != null)
+            throw new IllegalArgumentException("Invalid prefix or suffix");
+        return dir.resolve(name);
+    }
+
+    /**
+     * Utility method to return a formatted String of the key:value entries for
+     * a Map
+     *
+     * @param env Map to format
+     * @return Formatted string of the Map entries
+     */
+    private static String formatMap(Map<String, String> env) {
+        return env.entrySet().stream()
+                .map(e -> format("(%s:%s)", e.getKey(), e.getValue()))
+                .collect(joining(", "));
+    }
+
+    /**
+     * Create a Zip file using the Zip File System with the specified
+     * Zip File System properties
+     *
+     * @param zipFile Path to the Zip File to create
+     * @param env     Properties used for creating the Zip Filesystem
+     * @param entries The entries to add to the Zip File
+     * @throws IOException If an error occurs while creating the Zip file
+     */
+    private void createZipFile(Path zipFile, Map<String, String> env,
+                               Entry... entries) throws IOException {
+        if (DEBUG) {
+            System.out.printf("Creating Zip file: %s with the Properties: %s%n",
+                    zipFile, formatMap(env));
+        }
+        try (FileSystem zipfs =
+                     FileSystems.newFileSystem(zipFile, env)) {
+            for (Entry e : entries) {
+                Files.writeString(zipfs.getPath(e.name), new String(e.bytes));
+            }
+        }
+    }
+
+    /**
+     * Represents an entry in a Zip file. An entry encapsulates a name, a
+     * compression method, and its contents/data.
+     */
+    static class Entry {
+        private final String name;
+        private final int method;
+        private final byte[] bytes;
+
+        Entry(String name, int method, String contents) {
+            this.name = name;
+            this.method = method;
+            this.bytes = contents.getBytes(StandardCharsets.UTF_8);
+        }
+
+        static Entry of(String name, int method, String contents) {
+            return new Entry(name, method, contents);
+        }
+
+        /**
+         * Returns a new Entry with the same name and compression method as this
+         * Entry but with the given content.
+         */
+        Entry content(String contents) {
+            return new Entry(name, method, contents);
+        }
+    }
+
+    /**
+     * Verify that the given path is a Zip file containing exactly the
+     * given entries.
+     */
+    private static void verify(Path zipfile, Entry... entries) throws IOException {
+        // check entries with zip API
+        try (ZipFile zf = new ZipFile(zipfile.toFile())) {
+            // check entry count
+            assertEquals(entries.length, zf.size());
+
+            // check compression method and content of each entry
+            for (Entry e : entries) {
+                ZipEntry ze = zf.getEntry(e.name);
+                //System.out.printf("Name: %s, method: %s, Expected Method: %s%n", e.name, ze.getMethod(), e.method);
+                assertNotNull(ze);
+                assertEquals(e.method, ze.getMethod());
+                try (InputStream in = zf.getInputStream(ze)) {
+                    byte[] bytes = in.readAllBytes();
+                    assertTrue(Arrays.equals(bytes, e.bytes));
+                }
+            }
+        }
+
+        // check entries with FileSystem API
+        try (FileSystem fs = FileSystems.newFileSystem(zipfile)) {
+            // check entry count
+            Path top = fs.getPath("/");
+            long count = Files.find(top, Integer.MAX_VALUE,
+                    (path, attrs) -> attrs.isRegularFile()).count();
+            assertEquals(entries.length, count);
+
+            // check content of each entry
+            for (Entry e : entries) {
+                Path file = fs.getPath(e.name);
+                byte[] bytes = Files.readAllBytes(file);
+                assertTrue(Arrays.equals(bytes, e.bytes));
+            }
+        }
+    }
+}
--- a/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -33,6 +33,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Arrays;
+import java.util.Map;
 import java.util.spi.ToolProvider;
 import java.util.zip.CRC32;
 import java.util.zip.ZipEntry;
@@ -116,14 +117,15 @@
 
         // Create JAR file with a STORED(non-compressed) entry
         Files.writeString(Path.of(storedFileName), "foobar");
-        int rc = JAR_TOOL.run(System.out, System.err,
+        JAR_TOOL.run(System.out, System.err,
                 "cM0vf", jarFileName, storedFileName);
 
-        // Replace the STORED entry
+        // Replace the STORED entry using the default(DEFLATED) compression
+        // method.
         try (FileSystem fs = FileSystems.newFileSystem(zipFile)) {
             Files.writeString(fs.getPath(storedFileName), replacedValue);
         }
-        Entry e1 = Entry.of(storedFileName, ZipEntry.STORED, replacedValue);
+        Entry e1 = Entry.of(storedFileName, ZipEntry.DEFLATED, replacedValue);
         verify(zipFile, e1);
     }
 
@@ -159,8 +161,12 @@
 
         String newContents = "hi";
 
+        // Set the required compression method
+        Map<String, Boolean> map = Map.of("noCompression",
+                e1.method != ZipEntry.DEFLATED);
+
         // replace contents of e1
-        try (FileSystem fs = FileSystems.newFileSystem(zipfile)) {
+        try (FileSystem fs = FileSystems.newFileSystem(zipfile, map)) {
             Path foo = fs.getPath(e1.name);
             Files.writeString(foo, newContents);
         }
--- a/test/jdk/sun/net/www/ftptest/FtpCommandHandler.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/sun/net/www/ftptest/FtpCommandHandler.java	Mon Nov 04 11:25:55 2019 +0000
@@ -466,6 +466,10 @@
             try {
                 str = in.readLine();
                 System.out.println("line: " + str);
+                if (str == null) {
+                    System.out.println("EOF read from input");
+                    break;
+                }
                 buf = new StringBuffer(str);
                 res = parseCmd(buf);
                 switch (res) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/tools/keytool/ExtOptionCamelCase.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8231950
+ * @modules java.base/sun.security.tools.keytool
+ *          java.base/sun.security.util
+ *          java.base/sun.security.x509
+ * @compile -XDignore.symbol.file ExtOptionCamelCase.java
+ * @summary keytool -ext camel-case shorthand not working
+ */
+
+import sun.security.tools.keytool.Main;
+import sun.security.util.DerValue;
+import sun.security.x509.BasicConstraintsExtension;
+import sun.security.x509.CertificateExtensions;
+import sun.security.x509.Extension;
+import sun.security.x509.KeyUsageExtension;
+
+import java.io.ByteArrayOutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.util.List;
+
+public class ExtOptionCamelCase {
+    static Method createV3Extensions;
+    static Constructor<Main> ctor;
+    static PublicKey pk;
+    static Method oneOf;
+
+    public static void main(String[] args) throws Exception {
+
+        prepare();
+
+        // Unseen ext name
+        testCreateFail("abc");
+
+        // camelCase match, both cases work
+        testCreate("bc", BasicConstraintsExtension.class);
+        testCreate("BC", BasicConstraintsExtension.class);
+
+        // Prefix match
+        testCreate("BasicC", BasicConstraintsExtension.class);
+
+        // Ambiguous, digitalSignature or dataEncipherment?
+        testCreateFail("ku=d");
+
+        // prefix match
+        testCreate("ku:c=dig", KeyUsageExtension.class,
+                x -> x.get(KeyUsageExtension.DIGITAL_SIGNATURE));
+
+        // camelCase match
+        testCreate("ku=kE", KeyUsageExtension.class,
+                x -> x.get(KeyUsageExtension.KEY_ENCIPHERMENT));
+
+        // camelCase match must be only 1st+CAPITALs
+        testCreateFail("ku=KeUs");
+
+        // camelCase match, must be only 1st + all CAPITALs
+        testCreate("ku=kCS", KeyUsageExtension.class,
+                x -> x.get(KeyUsageExtension.KEY_CERTSIGN));
+
+        // ... not all CAPITALs
+        testCreateFail("ku=kC");
+
+        // ... has lowercase letters
+        testCreateFail("ku=keCeSi");
+
+        // Ambiguous, keyAgreement or keyCertSign
+        testCreateFail("ku:c=ke");
+
+        // camelCase natch
+        testCreate("ku:c=dE", KeyUsageExtension.class,
+                x -> x.get(KeyUsageExtension.DATA_ENCIPHERMENT));
+        // prefix match
+        testCreate("ku:c=de", KeyUsageExtension.class,
+                x -> x.get(KeyUsageExtension.DECIPHER_ONLY));
+
+        // camelCase match
+        testCreate("ku:c=kA", KeyUsageExtension.class,
+                x -> x.get(KeyUsageExtension.KEY_AGREEMENT));
+
+        // camelCase match, fallback
+        testCreate("ku:c=ka", KeyUsageExtension.class,
+                x -> x.get(KeyUsageExtension.KEY_AGREEMENT));
+
+        // Testing oneOf() directly
+        testOneOf("a", -1, "b", "c"); // -1 means not found
+        testOneOf("a", -2, "ab", "ac"); // -2 means ambiguous
+
+        testOneOf("a", 0, "a", "ac"); //exact match
+        testOneOf("a", 0, "a", "b");
+        testOneOf("ac", 1, "a", "ac");
+
+        testOneOf("a", 0, "abc", "bcd");
+        testOneOf("ab", 0, "abc", "ABC");
+        testOneOf("ab", 0, "abc", "aBC");
+        testOneOf("ab", 0, "abc", "Abc");
+        testOneOf("AB", 1, "abc", "ABC");
+        testOneOf("aB", 0, "abcBcd", "abcDef");
+        testOneOf("ab", -2, "abcBcd", "abcDef");
+        testOneOf("aB", -2, "abcBcdEfg", "abcDef");
+
+        testOneOf("ab", 0, "abcDef", "axyBuv");
+        testOneOf("aB", 1, "abcDef", "axyBuv");
+        testOneOf("a", -2, "abcDef", "axyBuv");
+
+        testOneOf("aBC", -1, "a12BxyCuvDmn"); // 12 is not removed
+        testOneOf("a12BCD", 0, "a12BxyCuvDmn");
+        testOneOf("a12BC", -1, "a12BxyCuvDmn"); // must be full
+
+        // Fallback
+        testOneOf("bc", 0, "BasicConstraints");
+        testOneOf("BC", 0, "BasicConstraints");
+        testOneOf("BasicConstraints", 0, "BasicConstraints");
+        testOneOf("basicconstraints", 0, "BasicConstraints");
+        testOneOf("Basic", 0, "BasicConstraints");
+        testOneOf("basic", 0, "BasicConstraints");
+
+        testOneOf("BaCo", -1, "BasicConstraints");
+    }
+
+    // Expose some private methods
+    static void prepare() throws Exception {
+        createV3Extensions = Main.class.getDeclaredMethod(
+                "createV3Extensions",
+                CertificateExtensions.class,
+                CertificateExtensions.class,
+                List.class,
+                PublicKey.class,
+                PublicKey.class);
+        createV3Extensions.setAccessible(true);
+        ctor = Main.class.getDeclaredConstructor();
+        ctor.setAccessible(true);
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
+        pk = kpg.generateKeyPair().getPublic();
+
+        oneOf = Main.class.getDeclaredMethod(
+                "oneOf", String.class, String[].class);
+        oneOf.setAccessible(true);
+    }
+
+    /**
+     * Ensures the given type of extension is created with the option
+     */
+    static <T extends Extension> void testCreate(String option, Class<T> clazz)
+            throws Exception {
+        testCreate(option, clazz, null);
+    }
+
+    /**
+     * Ensures an option is invalid and will be rejected
+     */
+    static <T extends Extension> void testCreateFail(String option)
+            throws Exception {
+        testCreate(option, null, null);
+    }
+
+    /**
+     * Ensures the given type of extension is created and match the rule
+     * with the option.
+     *
+     * @param option the -ext option provided to keytool
+     * @param clazz the expected extension to create, null means none
+     * @param rule a predicate to check if the extension created is acceptable
+     * @param <T> the extected extension type
+     * @throws Exception if test result is unexpected
+     */
+    static <T extends Extension> void testCreate(String option, Class<T> clazz,
+            PredicateWithException<T> rule) throws Exception {
+        try {
+            CertificateExtensions exts = (CertificateExtensions)
+                    createV3Extensions.invoke(ctor.newInstance(),
+                            null, null, List.of(option), pk, null);
+
+            // ATTENTION: the extensions created above might contain raw
+            // extensions (not of a subtype) and we need to store and reload
+            // it to resolve them to subtypes.
+            ByteArrayOutputStream bout = new ByteArrayOutputStream();
+            exts.encode(bout);
+            exts = new CertificateExtensions(new DerValue(bout.toByteArray()).data);
+
+            if (clazz == null) {
+                throw new Exception("Should fail");
+            } else {
+                for (Extension e : exts.getAllExtensions()) {
+                    if (e.getClass() == clazz) {
+                        if (rule == null || rule.test((T) e)) {
+                            return;
+                        }
+                    }
+                }
+                throw new Exception("Unexpected result: " + exts);
+            }
+        } catch (InvocationTargetException e) {
+            if (clazz == null) {
+                return;
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    @FunctionalInterface
+    interface PredicateWithException<T> {
+        boolean test(T t) throws Exception;
+    }
+
+    /**
+     * Ensures oneOf returns the expected result.
+     *
+     * @param s input
+     * @param expected expected value, -2 if ambiguous, -1 if no match
+     * @param items existing strings to match
+     * @throws Exception if test result is unexpected
+     */
+    static void testOneOf(String s, int expected, String... items)
+            throws Exception {
+        try {
+            int res = (int)oneOf.invoke(null, s, items);
+            if (expected == -2) {
+                throw new Exception("Should fail");
+            } else {
+                if (expected != res) {
+                    throw new Exception(
+                            "Expected " + expected + ", actually " + res);
+                }
+            }
+        } catch (InvocationTargetException e) {
+            if (expected == -2) {
+                return;
+            } else {
+                throw e;
+            }
+        }
+    }
+}
--- a/test/jdk/sun/security/tools/keytool/KeyToolTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/sun/security/tools/keytool/KeyToolTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @author weijun.wang
+ * @bug 6251120 8231950
  * @summary Testing keytool
  *
  * Run through autotest.sh and manualtest.sh
@@ -1319,11 +1319,13 @@
         // cRLSign cannot be cs
         testFail("", pre + "ku6 -ext KU=cs");
         testOK("", pre + "ku11 -ext KU=nr");
-        // ke also means keyAgreement
+        // ke means keyAgreement and keyCertSign...
         testFail("", pre + "ku12 -ext KU=ke");
         testOK("", pre + "ku12 -ext KU=keyE");
+        testOK("", pre + "ku12a -ext KU=kE"); // kE is only keyEncipherment
         // de also means decipherOnly
-        testFail("", pre + "ku13 -ext KU=de");
+        testOK("", pre + "ku13a -ext KU=de"); // de is decipherOnly
+        testOK("", pre + "ku13b -ext KU=dE"); // dE is dataEncipherment
         testOK("", pre + "ku13 -ext KU=dataE");
         testOK("", pre + "ku14 -ext KU=ka");
         testOK("", pre + "ku15 -ext KU=kcs");
--- a/test/jdk/tools/jlink/JLinkReproducibleTest.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/jdk/tools/jlink/JLinkReproducibleTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -43,7 +43,7 @@
         res.shouldHaveExitValue(0);
     }
 
-    private static void jlink(Path image) throws Exception {
+    private static void jlink(Path image, boolean with_default_trace_file) throws Exception {
         var cmd = new ArrayList<String>();
         cmd.add(JDKToolFinder.getJDKTool("jlink"));
         cmd.addAll(List.of(
@@ -52,6 +52,9 @@
             "--compress=2",
             "--output", image.toString()
         ));
+        if (!with_default_trace_file) {
+            cmd.add("--generate-jli-classes=@file-not-exists");
+        }
         run(cmd);
     }
 
@@ -98,17 +101,31 @@
 
         // Link the first image
         var firstImage = Path.of("image-first");
-        jlink(firstImage);
+        jlink(firstImage, true);
         var firstModulesFile = firstImage.resolve("lib")
                                          .resolve("modules");
 
         // Link the second image
         var secondImage = Path.of("image-second");
-        jlink(secondImage);
+        jlink(secondImage, true);
         var secondModulesFile = secondImage.resolve("lib")
                                            .resolve("modules");
 
         // Ensure module files are identical
         assertEquals(-1L, Files.mismatch(firstModulesFile, secondModulesFile));
+
+        // Link the third image
+        var thirdImage = Path.of("image-third");
+        jlink(thirdImage, false);
+        var thirdModulesFile = thirdImage.resolve("lib")
+                                         .resolve("modules");
+        // Link the fourth image
+        var fourthImage = Path.of("image-fourth");
+        jlink(fourthImage, false);
+        var fourthModulesFile = fourthImage.resolve("lib")
+                                           .resolve("modules");
+
+        // Ensure module files are identical
+        assertEquals(-1L, Files.mismatch(thirdModulesFile, fourthModulesFile));
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jlink/plugins/AddOptionsPluginTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import jdk.test.lib.process.*;
+
+import tests.Helper;
+
+/* @test
+ * @bug 8232080
+ * @summary Test the --add-options plugin
+ * @library ../../lib
+ * @library /test/lib
+ * @modules java.base/jdk.internal.jimage
+ *          jdk.jdeps/com.sun.tools.classfile
+ *          jdk.jlink/jdk.tools.jlink.internal
+ *          jdk.jlink/jdk.tools.jmod
+ *          jdk.jlink/jdk.tools.jimage
+ *          jdk.compiler
+ * @build tests.*
+ * @run main AddOptionsPluginTest
+ */
+
+public class AddOptionsPluginTest {
+
+    private static final String PROP = "add.options.plugin.test";
+    private static final String VALUE = "xyzzy";
+    private static final String OPTS = "-D" + PROP + "=" + VALUE;
+
+    public static void main(String[] args) throws Throwable {
+
+        Helper helper = Helper.newHelper();
+        if (helper == null) {
+            System.err.println("Test not run");
+            return;
+        }
+
+        var module = "addoptions";
+        helper.generateDefaultJModule(module);
+        var image = helper.generateDefaultImage(new String[] { "--add-options", OPTS },
+                                                module)
+            .assertSuccess();
+        helper.checkImage(image, module, null, null);
+
+        var launcher = image.resolve("bin/java"
+                                     + (System.getProperty("os.name").startsWith("Windows")
+                                        ? ".exe" : ""));
+        var oa = ProcessTools.executeProcess(launcher.toString(),
+                                             "-XshowSettings:properties", "--version");
+        oa.stderrShouldMatch("^ +" + PROP + " = " + VALUE + "$");
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jlink/plugins/VendorInfoPluginsTest.java	Mon Nov 04 11:25:55 2019 +0000
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+import jdk.test.lib.process.*;
+
+import tests.Helper;
+
+/* @test
+ * @bug 8232080
+ * @summary Test the --vendor-version --vendor-url-bug plugins
+ * @library ../../lib
+ * @library /test/lib
+ * @modules java.base/jdk.internal.jimage
+ *          jdk.jdeps/com.sun.tools.classfile
+ *          jdk.jlink/jdk.tools.jlink.internal
+ *          jdk.jlink/jdk.tools.jmod
+ *          jdk.jlink/jdk.tools.jimage
+ *          jdk.compiler
+ * @build tests.*
+ * @run main VendorInfoPluginsTest
+ */
+
+public class VendorInfoPluginsTest {
+
+    public static class Crasher {
+
+        public static void main(String ... args) throws Exception {
+            var uc = Class.forName("sun.misc.Unsafe");
+            var f = uc.getDeclaredField("theUnsafe");
+            f.setAccessible(true);
+            var u = (sun.misc.Unsafe)f.get(null);
+            for (long a = 0; a < Long.MAX_VALUE; a += 8)
+                u.putLong(a, -1L);
+        }
+
+    }
+
+    private static final String VERSION = "XyzzyVM 3.14.15";
+    private static final String BUG_URL = "https://bugs.xyzzy.com/";
+    private static final String VM_BUG_URL = "https://bugs.xyzzy.com/crash/";
+
+    public static void main(String[] args) throws Throwable {
+
+        Helper helper = Helper.newHelper();
+        if (helper == null) {
+            System.err.println("Test not run");
+            return;
+        }
+
+        var module = "vendorinfo";
+        helper.generateDefaultJModule(module);
+        var image = helper.generateDefaultImage(new String[] {
+                "--add-modules", "jdk.unsupported",
+                "--vendor-version", VERSION,
+                "--vendor-bug-url", BUG_URL,
+                "--vendor-vm-bug-url", VM_BUG_URL },
+            module).assertSuccess();
+        helper.checkImage(image, module, null, null);
+
+        // Properties and --version
+        var launcher
+            = image.resolve("bin/java"
+                            + (System.getProperty("os.name").startsWith("Windows")
+                               ? ".exe" : "")).toString();
+        var oa = ProcessTools.executeProcess(launcher,
+                                             "-XshowSettings:properties",
+                                             "--version");
+        oa.stderrShouldMatch("^ +java.vendor.url.bug = " + BUG_URL + "$");
+        oa.stderrShouldMatch("^ +java.vendor.version = " + VERSION + "$");
+        oa.stdoutShouldMatch("^.*Runtime Environment " + VERSION + " \\(build.*$");
+        oa.stdoutShouldMatch("^.*Server VM " + VERSION + " \\(build.*$");
+
+        // VM error log
+        oa = ProcessTools.executeProcess(launcher,
+                                         "--class-path",
+                                         System.getProperty("test.classes"),
+                                         "VendorInfoPluginsTest$Crasher");
+        oa.stdoutShouldMatch("^# +" + VM_BUG_URL + "$");
+        oa.stdoutShouldMatch("^.*Runtime Environment " + VERSION + " \\(.*$");
+        oa.stdoutShouldMatch("^.*Server VM " + VERSION + " \\(.*$");
+
+    }
+
+}
--- a/test/langtools/tools/javac/api/TestGetScopeResult.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/langtools/tools/javac/api/TestGetScopeResult.java	Mon Nov 04 11:25:55 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8205418 8207229 8207230
+ * @bug 8205418 8207229 8207230 8230847
  * @summary Test the outcomes from Trees.getScope
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.comp
@@ -42,11 +42,19 @@
 import javax.tools.StandardJavaFileManager;
 import javax.tools.ToolProvider;
 
+import com.sun.source.tree.BlockTree;
 import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ConditionalExpressionTree;
+import com.sun.source.tree.IdentifierTree;
 import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
 import com.sun.source.tree.Scope;
+import com.sun.source.tree.Tree;
 import com.sun.source.tree.VariableTree;
 import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
 import com.sun.source.util.TreePath;
 import com.sun.source.util.TreePathScanner;
 import com.sun.source.util.Trees;
@@ -65,6 +73,11 @@
     public static void main(String... args) throws IOException {
         new TestGetScopeResult().run();
         new TestGetScopeResult().testAnalyzerDisabled();
+        new TestGetScopeResult().testVariablesInSwitch();
+        new TestGetScopeResult().testMemberRefs();
+        new TestGetScopeResult().testAnnotations();
+        new TestGetScopeResult().testAnnotationsLazy();
+        new TestGetScopeResult().testCircular();
     }
 
     public void run() throws IOException {
@@ -259,5 +272,225 @@
             super.analyze(statement, env);
         }
     }
+
+    void testVariablesInSwitch() throws IOException {
+        JavacTool c = JavacTool.create();
+        try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) {
+            class MyFileObject extends SimpleJavaFileObject {
+                MyFileObject() {
+                    super(URI.create("myfo:///Test.java"), SOURCE);
+                }
+                @Override
+                public String getCharContent(boolean ignoreEncodingErrors) {
+                    return "class Test {" +
+                           "    void test() {\n" +
+                           "        E e = E.A;\n" +
+                           "        Object o = E.A;\n" +
+                           "        switch (e) {\n" +
+                           "            case A:\n" +
+                           "                return;\n" +
+                           "            case B:\n" +
+                           "                test();\n" +
+                           "                E ee = null;\n" +
+                           "                break;\n" +
+                           "        }\n" +
+                           "    }\n" +
+                           "    enum E {A, B}\n" +
+                           "}";
+                }
+            }
+            Context ctx = new Context();
+            TestAnalyzer.preRegister(ctx);
+            JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null,
+                                                List.of(new MyFileObject()), ctx);
+            CompilationUnitTree cut = t.parse().iterator().next();
+            t.analyze();
+
+            new TreePathScanner<Void, Void>() {
+                @Override
+                public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
+                    Trees.instance(t).getScope(getCurrentPath());
+                    return super.visitMethodInvocation(node, p);
+                }
+            }.scan(cut, null);
+        }
+    }
+
+    void testMemberRefs() throws IOException {
+        JavacTool c = JavacTool.create();
+        try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) {
+            class MyFileObject extends SimpleJavaFileObject {
+                MyFileObject() {
+                    super(URI.create("myfo:///Test.java"), SOURCE);
+                }
+                @Override
+                public String getCharContent(boolean ignoreEncodingErrors) {
+                    return "class Test {" +
+                           "    void test() {\n" +
+                           "        Test t = this;\n" +
+                           "        Runnable r1 = t::test;\n" +
+                           "        Runnable r2 = true ? t::test : t::test;\n" +
+                           "        c(t::test);\n" +
+                           "        c(true ? t::test : t::test);\n" +
+                           "    }\n" +
+                           "    void c(Runnable r) {}\n" +
+                           "}";
+                }
+            }
+            Context ctx = new Context();
+            TestAnalyzer.preRegister(ctx);
+            JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null,
+                                                List.of(new MyFileObject()), ctx);
+            CompilationUnitTree cut = t.parse().iterator().next();
+            t.analyze();
+
+            new TreePathScanner<Void, Void>() {
+                @Override
+                public Void visitConditionalExpression(ConditionalExpressionTree node, Void p) {
+                    Trees.instance(t).getScope(new TreePath(getCurrentPath(), node.getCondition()));
+                    return super.visitConditionalExpression(node, p);
+                }
+
+                @Override
+                public Void visitBlock(BlockTree node, Void p) {
+                    Trees.instance(t).getScope(getCurrentPath());
+                    return super.visitBlock(node, p);
+                }
+            }.scan(cut, null);
+        }
+    }
+
+    void testAnnotations() throws IOException {
+        JavacTool c = JavacTool.create();
+        try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) {
+            class MyFileObject extends SimpleJavaFileObject {
+                MyFileObject() {
+                    super(URI.create("myfo:///Test.java"), SOURCE);
+                }
+                @Override
+                public String getCharContent(boolean ignoreEncodingErrors) {
+                    return "class Test {" +
+                           "    void test() {\n" +
+                           "        new Object() {\n" +
+                           "            @A\n" +
+                           "            public String t() { return null; }\n" +
+                           "        };\n" +
+                           "    }\n" +
+                           "    @interface A {}\n" +
+                           "}";
+                }
+            }
+            Context ctx = new Context();
+            TestAnalyzer.preRegister(ctx);
+            JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null,
+                                                List.of(new MyFileObject()), ctx);
+            CompilationUnitTree cut = t.parse().iterator().next();
+            t.analyze();
+
+            new TreePathScanner<Void, Void>() {
+                @Override
+                public Void visitIdentifier(IdentifierTree node, Void p) {
+                    if (node.getName().contentEquals("A")) {
+                        Trees.instance(t).getScope(getCurrentPath());
+                    }
+                    return super.visitIdentifier(node, p);
+                }
+
+                @Override
+                public Void visitMethod(MethodTree node, Void p) {
+                    super.visitMethod(node, p);
+                    if (node.getReturnType() != null) {
+                        Trees.instance(t).getScope(new TreePath(getCurrentPath(), node.getReturnType()));
+                    }
+                    return null;
+                }
+            }.scan(cut, null);
+        }
+    }
+
+    void testAnnotationsLazy() throws IOException {
+        JavacTool c = JavacTool.create();
+        try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) {
+            class MyFileObject extends SimpleJavaFileObject {
+                MyFileObject() {
+                    super(URI.create("myfo:///Test.java"), SOURCE);
+                }
+                @Override
+                public String getCharContent(boolean ignoreEncodingErrors) {
+                    return "import java.lang.annotation.*;\n" +
+                           "\n" +
+                           "class ClassA {\n" +
+                           "    Object o = ClassB.lcv;\n" +
+                           "}\n" +
+                           "\n" +
+                           "class ClassB {\n" +
+                           "    static final String[] lcv = new @TA String[0];\n" +
+                           "}\n" +
+                           "\n" +
+                           "class ClassC {\n" +
+                           "    static final Object o = (@TA Object) null;\n" +
+                           "}\n" +
+                           "\n" +
+                           "@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})\n" +
+                           "@interface TA {}\n";
+                }
+            }
+            Context ctx = new Context();
+            TestAnalyzer.preRegister(ctx);
+            JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null,
+                                                List.of(new MyFileObject()), ctx);
+            t.addTaskListener(new TaskListener() {
+                @Override
+                public void finished(TaskEvent e) {
+                    if (e.getKind() == TaskEvent.Kind.ANALYZE) {
+                        new TreePathScanner<Void, Void>() {
+                            @Override
+                            public Void scan(Tree tree, Void p) {
+                                if (tree != null) {
+                                    Trees.instance(t).getScope(new TreePath(getCurrentPath(), tree));
+                                }
+                                return super.scan(tree, p);
+                            }
+                        }.scan(Trees.instance(t).getPath(e.getTypeElement()), null);
+                    }
+                }
+            });
+
+            t.call();
+        }
+    }
+
+    void testCircular() throws IOException {
+        JavacTool c = JavacTool.create();
+        try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) {
+            class MyFileObject extends SimpleJavaFileObject {
+                MyFileObject() {
+                    super(URI.create("myfo:///Test.java"), SOURCE);
+                }
+                @Override
+                public String getCharContent(boolean ignoreEncodingErrors) {
+                    return "class Test extends Test {" +
+                           "    {\n" +
+                           "        int i;\n" +
+                           "    }\n" +
+                           "}";
+                }
+            }
+            Context ctx = new Context();
+            TestAnalyzer.preRegister(ctx);
+            JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null,
+                                                List.of(new MyFileObject()), ctx);
+            CompilationUnitTree cut = t.parse().iterator().next();
+            t.analyze();
+
+            new TreePathScanner<Void, Void>() {
+                @Override
+                public Void visitBlock(BlockTree node, Void p) {
+                    Trees.instance(t).getScope(getCurrentPath());
+                    return super.visitBlock(node, p);
+                }
+            }.scan(cut, null);
+        }
+    }
+
 }
-
--- a/test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java	Mon Nov 04 11:25:55 2019 +0000
@@ -26,8 +26,11 @@
  * @bug 8003280
  * @summary Add lambda tests
  *   Test bridge methods for certain SAM conversions
+ *   Tests that jdk.internal.lambda.disableEagerInitialization=true creates a
+ *   get$Lambda method for non-capturing lambdas
  * @compile LambdaTest6.java
  * @run main LambdaTest6
+ * @run main/othervm -Djdk.internal.lambda.disableEagerInitialization=true LambdaTest6
  */
 
 import java.lang.reflect.Method;
@@ -60,18 +63,37 @@
         return s;
     }
 
+    private static Set<String> allowedMethods() {
+        Set<String> s = new HashSet<>();
+        s.add("m");
+        if (Boolean.getBoolean("jdk.internal.lambda.disableEagerInitialization")) {
+            s.add("get$Lambda");
+        }
+        return s;
+    }
+
+    private static boolean matchingMethodNames(Method[] methods) {
+        Set<String> methodNames = new HashSet<>();
+        for (Method m : methods) {
+            methodNames.add(m.getName());
+        }
+        return methodNames.equals(allowedMethods());
+    }
+
     private void test1()
     {
         L la = s -> { };
         la.m("hi");
         Class<? extends L> c1 = la.getClass();
         Method[] methods = c1.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         Set<String> types = setOfStringObject();
         for(Method m : methods) {
-            assertTrue(m.getName().equals("m"));
-            Class[] parameterTypes = m.getParameterTypes();
-            assertTrue(parameterTypes.length == 1);
-            assertTrue(types.remove(parameterTypes[0].getName()));
+            if ("m".equals(m.getName())) {
+                Class[] parameterTypes = m.getParameterTypes();
+                assertTrue(parameterTypes.length == 1);
+                assertTrue(types.remove(parameterTypes[0].getName()));
+            }
         }
         assertTrue(types.isEmpty() || (types.size() == 1 && types.contains("java.lang.String")));
     }
@@ -82,12 +104,14 @@
         //km.m("hi");
         Class<? extends KM> c2 = km.getClass();
         Method[] methods = c2.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         Set<String> types = setOfStringObject();
         for(Method m : methods) {
-            assertTrue(m.getName().equals("m"));
-            Class[] parameterTypes = m.getParameterTypes();
-            assertTrue(parameterTypes.length == 1);
-            assertTrue(types.remove(parameterTypes[0].getName()));
+            if ("m".equals(m.getName())) {
+                Class[] parameterTypes = m.getParameterTypes();
+                assertTrue(parameterTypes.length == 1);
+                assertTrue(types.remove(parameterTypes[0].getName()));
+            }
         }
         assertTrue(types.isEmpty());
     }
@@ -99,11 +123,13 @@
         assertTrue( ((H)na).m().equals("hi") );
         Class<? extends N> c3 = na.getClass();
         Method[] methods = c3.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         Set<String> types = setOfStringObject();
         for(Method m : methods) {
-            assertTrue(m.getName().equals("m"));
-            Class returnType = m.getReturnType();
-            assertTrue(types.remove(returnType.getName()));
+            if ("m".equals(m.getName())) {
+                Class returnType = m.getReturnType();
+                assertTrue(types.remove(returnType.getName()));
+            }
         }
         assertTrue(types.size() == 1); //there's a bridge
     }
--- a/test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java	Mon Nov 04 11:25:55 2019 +0000
@@ -26,11 +26,15 @@
  * @bug 8003280
  * @summary Add lambda tests
  *   Test bridge methods in certain SAM conversion
+ *   Tests that jdk.internal.lambda.disableEagerInitialization=true creates a
+ *   get$Lambda method for non-capturing lambdas
  * @compile BridgeMethod.java
  * @run main BridgeMethod
+ * @run main/othervm -Djdk.internal.lambda.disableEagerInitialization=true BridgeMethod
  */
 
 import java.lang.reflect.Method;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -68,19 +72,38 @@
         return s;
     }
 
+    private static Set<String> allowedMethods() {
+        Set<String> s = new HashSet<>();
+        s.add("m");
+        if (Boolean.getBoolean("jdk.internal.lambda.disableEagerInitialization")) {
+            s.add("get$Lambda");
+        }
+        return s;
+    }
+
+    private static boolean matchingMethodNames(Method[] methods) {
+        Set<String> methodNames = new HashSet<>();
+        for (Method m : methods) {
+            methodNames.add(m.getName());
+        }
+        return methodNames.equals(allowedMethods());
+    }
+
     public static void main(String[] args) {
         L la = BridgeMethod::bar; //static reference
         la.m("hi");
         Class<? extends L> c1 = la.getClass();
         Method[] methods = c1.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         Set<String> types = setOfStringObject();
         System.out.println("methods in SAM conversion of L:");
         for(Method m : methods) {
-            System.out.println(m.toGenericString());
-            assertTrue(m.getName().equals("m"));
-            Class[] parameterTypes = m.getParameterTypes();
-            assertTrue(parameterTypes.length == 1);
-            assertTrue(types.remove(parameterTypes[0].getName()));
+            if (m.getName().equals("m")) {
+                System.out.println(m.toGenericString());
+                Class[] parameterTypes = m.getParameterTypes();
+                assertTrue(parameterTypes.length == 1);
+                assertTrue(types.remove(parameterTypes[0].getName()));
+            }
         }
         assertTrue(types.isEmpty() || (types.size() == 1 && types.contains("java.lang.String")));
 
@@ -88,14 +111,16 @@
         //km.m("hi"); //will be uncommented when CR7028808 fixed
         Class<? extends KM> c2 = km.getClass();
         methods = c2.getDeclaredMethods();
+        assertTrue(matchingMethodNames(methods));
         types = setOfStringObject();
         System.out.println("methods in SAM conversion of KM:");
         for(Method m : methods) {
-            System.out.println(m.toGenericString());
-            assertTrue(m.getName().equals("m"));
-            Class<?>[] parameterTypes = m.getParameterTypes();
-            assertTrue(parameterTypes.length == 1);
-            assertTrue(types.remove(parameterTypes[0].getName()));
+            if (m.getName().equals("m")) {
+                System.out.println(m.toGenericString());
+                Class<?>[] parameterTypes = m.getParameterTypes();
+                assertTrue(parameterTypes.length == 1);
+                assertTrue(types.remove(parameterTypes[0].getName()));
+            }
         }
         assertTrue(types.isEmpty());
 
--- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java	Mon Nov 04 11:25:55 2019 +0000
@@ -309,7 +309,7 @@
     // exceptions match. Pass null if you wish not to re-throw any exception.
     public static void checkCommonExecExceptions(OutputAnalyzer output, Exception e)
         throws Exception {
-        if (output.getStdout().contains("http://bugreport.java.com/bugreport/crash.jsp")) {
+        if (output.getStdout().contains("https://bugreport.java.com/bugreport/crash.jsp")) {
             throw new RuntimeException("Hotspot crashed");
         }
         if (output.getStdout().contains("TEST FAILED")) {
--- a/test/lib/jdk/test/lib/jfr/EventNames.java	Wed Oct 30 12:25:22 2019 +0000
+++ b/test/lib/jdk/test/lib/jfr/EventNames.java	Mon Nov 04 11:25:55 2019 +0000
@@ -189,6 +189,12 @@
     public final static String CPUTimeStampCounter = PREFIX + "CPUTimeStampCounter";
     public final static String ActiveRecording = PREFIX + "ActiveRecording";
     public final static String ActiveSetting = PREFIX + "ActiveSetting";
+    public static final String Flush = PREFIX + "Flush";
+    public static final String FlushStringPool = PREFIX + "FlushStringPool";
+    public static final String FlushStacktrace = PREFIX + "FlushStacktrace";
+    public static final String FlushStorage = PREFIX + "FlushStorage";
+    public static final String FlushMetadata = PREFIX + "FlushMetadata";
+    public static final String FlushTypeSet = PREFIX + "FlushTypeSet";
 
     public static boolean isGcEvent(EventType et) {
         return et.getCategoryNames().contains(GC_CATEGORY);