changeset 60002:735f9fd6dbe0

Merge
author jwilhelm
date Tue, 30 Jun 2020 20:09:03 +0200
parents 664eff33990a 991eb7e944fc
children a4585ab8c15f
files src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp src/hotspot/share/jfr/recorder/jfrRecorder.cpp src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/LauncherData.java src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WinMsiBundler.java test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java
diffstat 30 files changed, 721 insertions(+), 274 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp	Tue Jun 30 20:09:03 2020 +0200
@@ -1083,11 +1083,12 @@
     Node* barrier = state->enqueue_barrier(i);
     Node* ctrl = phase->get_ctrl(barrier);
     IdealLoopTree* loop = phase->get_loop(ctrl);
-    if (loop->_head->is_OuterStripMinedLoop()) {
+    Node* head = loop->head();
+    if (head->is_OuterStripMinedLoop()) {
       // Expanding a barrier here will break loop strip mining
       // verification. Transform the loop so the loop nest doesn't
       // appear as strip mined.
-      OuterStripMinedLoopNode* outer = loop->_head->as_OuterStripMinedLoop();
+      OuterStripMinedLoopNode* outer = head->as_OuterStripMinedLoop();
       hide_strip_mined_loop(outer, outer->unique_ctrl_out()->as_CountedLoop(), phase);
     }
   }
@@ -1289,11 +1290,12 @@
     ShenandoahLoadReferenceBarrierNode* lrb = state->load_reference_barrier(i);
     Node* ctrl = phase->get_ctrl(lrb);
     IdealLoopTree* loop = phase->get_loop(ctrl);
-    if (loop->_head->is_OuterStripMinedLoop()) {
+    Node* head = loop->head();
+    if (head->is_OuterStripMinedLoop()) {
       // Expanding a barrier here will break loop strip mining
       // verification. Transform the loop so the loop nest doesn't
       // appear as strip mined.
-      OuterStripMinedLoopNode* outer = loop->_head->as_OuterStripMinedLoop();
+      OuterStripMinedLoopNode* outer = head->as_OuterStripMinedLoop();
       hide_strip_mined_loop(outer, outer->unique_ctrl_out()->as_CountedLoop(), phase);
     }
   }
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp	Tue Jun 30 20:09:03 2020 +0200
@@ -35,6 +35,7 @@
 #include "jfr/recorder/jfrRecorder.hpp"
 #include "jfr/recorder/repository/jfrChunkWriter.hpp"
 #include "jfr/recorder/service/jfrOptionSet.hpp"
+#include "jfr/recorder/storage/jfrEpochStorage.inline.hpp"
 #include "jfr/recorder/storage/jfrMemorySpace.inline.hpp"
 #include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
 #include "jfr/support/jfrKlassUnloading.hpp"
@@ -91,52 +92,46 @@
 }
 
 JfrCheckpointManager::JfrCheckpointManager(JfrChunkWriter& cw) :
-  _mspace(NULL),
+  _global_mspace(NULL),
+  _thread_local_mspace(NULL),
   _chunkwriter(cw) {}
 
 JfrCheckpointManager::~JfrCheckpointManager() {
   JfrTraceIdLoadBarrier::destroy();
   JfrTypeManager::destroy();
-  delete _mspace;
+  delete _global_mspace;
+  delete _thread_local_mspace;
 }
 
-static const size_t buffer_count = 2;
-static const size_t buffer_size = 512 * K;
+static const size_t global_buffer_prealloc_count = 2;
+static const size_t global_buffer_size = 512 * K;
 
-static JfrCheckpointMspace* allocate_mspace(size_t min_elem_size,
-                                            size_t free_list_cache_count_limit,
-                                            size_t cache_prealloc_count,
-                                            bool prealloc_to_free_list,
-                                            JfrCheckpointManager* mgr) {
-  return create_mspace<JfrCheckpointMspace, JfrCheckpointManager>(min_elem_size,
-                                                                  free_list_cache_count_limit,
-                                                                  cache_prealloc_count,
-                                                                  prealloc_to_free_list,
-                                                                  mgr);
-}
+static const size_t thread_local_buffer_prealloc_count = 16;
+static const size_t thread_local_buffer_size = 128;
 
 bool JfrCheckpointManager::initialize() {
-  assert(_mspace == NULL, "invariant");
-  _mspace = allocate_mspace(buffer_size, 0, 0, false, this); // post-pone preallocation
-  if (_mspace == NULL) {
+  assert(_global_mspace == NULL, "invariant");
+  _global_mspace =  create_mspace<JfrCheckpointMspace, JfrCheckpointManager>(global_buffer_size, 0, 0, false, this); // post-pone preallocation
+  if (_global_mspace == NULL) {
     return false;
   }
   // preallocate buffer count to each of the epoch live lists
-  for (size_t i = 0; i < buffer_count * 2; ++i) {
-    Buffer* const buffer = mspace_allocate(buffer_size, _mspace);
-    _mspace->add_to_live_list(buffer, i % 2 == 0);
+  for (size_t i = 0; i < global_buffer_prealloc_count * 2; ++i) {
+    Buffer* const buffer = mspace_allocate(global_buffer_size, _global_mspace);
+    _global_mspace->add_to_live_list(buffer, i % 2 == 0);
   }
-  assert(_mspace->free_list_is_empty(), "invariant");
+  assert(_global_mspace->free_list_is_empty(), "invariant");
+
+  assert(_thread_local_mspace == NULL, "invariant");
+  _thread_local_mspace = new JfrThreadLocalCheckpointMspace();
+  if (_thread_local_mspace == NULL || !_thread_local_mspace->initialize(thread_local_buffer_size,
+                                                                        JFR_MSPACE_UNLIMITED_CACHE_SIZE,
+                                                                        thread_local_buffer_prealloc_count)) {
+    return false;
+  }
   return JfrTypeManager::initialize() && JfrTraceIdLoadBarrier::initialize();
 }
 
-void JfrCheckpointManager::register_full(BufferPtr buffer, Thread* thread) {
-  // nothing here at the moment
-  assert(buffer != NULL, "invariant");
-  assert(buffer->acquired_by(thread), "invariant");
-  assert(buffer->retired(), "invariant");
-}
-
 #ifdef ASSERT
 static void assert_lease(const BufferPtr buffer) {
   assert(buffer != NULL, "invariant");
@@ -149,15 +144,28 @@
   assert(buffer->lease(), "invariant");
   assert(buffer->acquired_by_self(), "invariant");
 }
+
+static void assert_retired(const BufferPtr buffer, Thread* thread) {
+  assert(buffer != NULL, "invariant");
+  assert(buffer->acquired_by(thread), "invariant");
+  assert(buffer->retired(), "invariant");
+}
 #endif // ASSERT
 
-static BufferPtr lease(size_t size, JfrCheckpointMspace* mspace, size_t retry_count, Thread* thread, bool previous_epoch) {
+void JfrCheckpointManager::register_full(BufferPtr buffer, Thread* thread) {
+  DEBUG_ONLY(assert_retired(buffer, thread);)
+  // nothing here at the moment
+}
+
+BufferPtr JfrCheckpointManager::lease(Thread* thread, bool previous_epoch /* false */, size_t size /* 0 */) {
+  JfrCheckpointMspace* const mspace = instance()._global_mspace;
   assert(mspace != NULL, "invariant");
   static const size_t max_elem_size = mspace->min_element_size(); // min is max
   BufferPtr buffer;
   if (size <= max_elem_size) {
-    buffer = mspace_acquire_lease_with_retry(size, mspace, retry_count, thread, previous_epoch);
+    buffer = mspace_acquire_live(size, mspace, thread, previous_epoch);
     if (buffer != NULL) {
+      buffer->set_lease();
       DEBUG_ONLY(assert_lease(buffer);)
       return buffer;
     }
@@ -167,54 +175,70 @@
   return buffer;
 }
 
-static const size_t lease_retry = 100;
+const u1 thread_local_context = 1;
 
-BufferPtr JfrCheckpointManager::lease(Thread* thread, bool previous_epoch /* false */, size_t size /* 0 */) {
-  return ::lease(size, instance()._mspace, lease_retry, thread, previous_epoch);
+static bool is_thread_local(JfrBuffer* buffer) {
+  assert(buffer != NULL, "invariant");
+  return buffer->context() == thread_local_context;
 }
 
-bool JfrCheckpointManager::lookup(BufferPtr old) const {
-  assert(old != NULL, "invariant");
-  return !_mspace->in_current_epoch_list(old);
-}
-
-BufferPtr JfrCheckpointManager::lease(BufferPtr old, Thread* thread, size_t size /* 0 */) {
-  assert(old != NULL, "invariant");
-  return ::lease(size, instance()._mspace, lease_retry, thread, instance().lookup(old));
+static void retire(JfrBuffer* buffer) {
+  DEBUG_ONLY(assert_release(buffer);)
+  buffer->clear_lease();
+  buffer->set_retired();
 }
 
 /*
- * 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 buffer, Thread* thread) {
+static void release(JfrBuffer* buffer) {
   DEBUG_ONLY(assert_release(buffer);)
-  buffer->clear_lease();
-  if (buffer->transient()) {
-    buffer->set_retired();
+  if (is_thread_local(buffer)) {
+    retire(buffer);
   } else {
+    buffer->clear_lease();
     buffer->release();
   }
 }
+BufferPtr JfrCheckpointManager::acquire_thread_local(size_t size, Thread* thread) {
+  assert(thread != NULL, "invariant");
+  JfrBuffer* const buffer = instance()._thread_local_mspace->acquire(size, thread);
+  assert(buffer != NULL, "invariant");
+  assert(buffer->free_size() >= size, "invariant");
+  buffer->set_context(thread_local_context);
+  assert(is_thread_local(buffer), "invariant");
+  buffer->set_lease();
+  return buffer;
+}
+
+BufferPtr JfrCheckpointManager::lease_thread_local(Thread* thread, size_t size /* 0 */) {
+  JfrBuffer* const buffer = acquire_thread_local(size, thread);
+  DEBUG_ONLY(assert_lease(buffer);)
+  return buffer;
+}
+
+BufferPtr JfrCheckpointManager::lease(BufferPtr old, Thread* thread, size_t size) {
+  assert(old != NULL, "invariant");
+  return is_thread_local(old) ? acquire_thread_local(size, thread) :
+                                lease(thread, instance()._global_mspace->in_previous_epoch_list(old), size);
+}
 
 BufferPtr JfrCheckpointManager::flush(BufferPtr old, size_t used, size_t requested, Thread* thread) {
   assert(old != NULL, "invariant");
   assert(old->lease(), "invariant");
   if (0 == requested) {
     // indicates a lease is being returned
-    release(old, thread);
+    release(old);
+    // signal completion of a new checkpoint
     set_constant_pending();
     return NULL;
   }
-  // migration of in-flight information
-  BufferPtr const new_buffer = lease(old, thread, used + requested);
-  if (new_buffer != NULL) {
-    migrate_outstanding_writes(old, new_buffer, used, requested);
-  }
-  release(old, thread);
-  return new_buffer; // might be NULL
+  BufferPtr new_buffer = lease(old, thread, used + requested);
+  assert(new_buffer != NULL, "invariant");
+  migrate_outstanding_writes(old, new_buffer, used, requested);
+  retire(old);
+  return new_buffer;
 }
 
 // offsets into the JfrCheckpointEntry
@@ -311,7 +335,7 @@
 
 typedef CheckpointWriteOp<JfrCheckpointManager::Buffer> WriteOperation;
 typedef MutexedWriteOp<WriteOperation> MutexedWriteOperation;
-typedef ReleaseOpWithExcision<JfrCheckpointMspace, JfrCheckpointMspace::LiveList> ReleaseOperation;
+typedef ReleaseWithExcisionOp<JfrCheckpointMspace, JfrCheckpointMspace::LiveList> ReleaseOperation;
 typedef CompositeOperation<MutexedWriteOperation, ReleaseOperation> WriteReleaseOperation;
 
 void JfrCheckpointManager::begin_epoch_shift() {
@@ -328,12 +352,13 @@
 
 size_t JfrCheckpointManager::write() {
   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(Thread::current()));
-  assert(_mspace->free_list_is_empty(), "invariant");
   WriteOperation wo(_chunkwriter);
   MutexedWriteOperation mwo(wo);
-  ReleaseOperation ro(_mspace, _mspace->live_list(true));
+  _thread_local_mspace->iterate(mwo, true); // previous epoch list
+  assert(_global_mspace->free_list_is_empty(), "invariant");
+  ReleaseOperation ro(_global_mspace, _global_mspace->live_list(true));
   WriteReleaseOperation wro(&mwo, &ro);
-  process_live_list(wro, _mspace, true);
+  process_live_list(wro, _global_mspace, true); // previous epoch list
   return wo.processed();
 }
 
@@ -344,10 +369,11 @@
   JfrTraceIdLoadBarrier::clear();
   clear_type_set();
   DiscardOperation discard_operation(mutexed); // mutexed discard mode
-  ReleaseOperation ro(_mspace, _mspace->live_list(true));
+  _thread_local_mspace->iterate(discard_operation, true); // previous epoch list
+  ReleaseOperation ro(_global_mspace, _global_mspace->live_list(true));
   DiscardReleaseOperation discard_op(&discard_operation, &ro);
-  assert(_mspace->free_list_is_empty(), "invariant");
-  process_live_list(discard_op, _mspace, true); // previous epoch list
+  assert(_global_mspace->free_list_is_empty(), "invariant");
+  process_live_list(discard_op, _global_mspace, true); // previous epoch list
   return discard_operation.elements();
 }
 
@@ -451,8 +477,9 @@
   if (is_constant_pending()) {
     WriteOperation wo(_chunkwriter);
     MutexedWriteOperation mwo(wo);
-    assert(_mspace->live_list_is_nonempty(), "invariant");
-    process_live_list(mwo, _mspace);
+    _thread_local_mspace->iterate(mwo); // current epoch list
+    assert(_global_mspace->live_list_is_nonempty(), "invariant");
+    process_live_list(mwo, _global_mspace); // current epoch list
   }
   return elements;
 }
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.hpp	Tue Jun 30 20:09:03 2020 +0200
@@ -26,14 +26,13 @@
 #define SHARE_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTMANAGER_HPP
 
 #include "jfr/recorder/storage/jfrBuffer.hpp"
+#include "jfr/recorder/storage/jfrEpochStorage.hpp"
 #include "jfr/recorder/storage/jfrMemorySpace.hpp"
 #include "jfr/recorder/storage/jfrMemorySpaceRetrieval.hpp"
 #include "jfr/utilities/jfrLinkedList.hpp"
 
 class JfrCheckpointManager;
 class JfrChunkWriter;
-class JfrSerializer;
-class JfrTypeManager;
 class Thread;
 
 struct JfrCheckpointEntry {
@@ -45,6 +44,7 @@
 };
 
 typedef JfrMemorySpace<JfrCheckpointManager, JfrMspaceRetrieval, JfrLinkedList<JfrBuffer>, JfrLinkedList<JfrBuffer>, true > JfrCheckpointMspace;
+typedef JfrEpochStorageHost<JfrBuffer, JfrMspaceRemoveRetrieval, true /* reclaim buffers eagerly*/ > JfrThreadLocalCheckpointMspace;
 
 //
 // Responsible for maintaining checkpoints and by implication types.
@@ -56,7 +56,8 @@
   typedef JfrCheckpointMspace::Node Buffer;
   typedef JfrCheckpointMspace::NodePtr BufferPtr;
  private:
-  JfrCheckpointMspace* _mspace;
+  JfrCheckpointMspace* _global_mspace;
+  JfrThreadLocalCheckpointMspace* _thread_local_mspace;
   JfrChunkWriter& _chunkwriter;
 
   JfrCheckpointManager(JfrChunkWriter& cw);
@@ -66,14 +67,16 @@
   bool initialize();
   static void destroy();
 
-  bool lookup(Buffer* old) const;
   static BufferPtr lease(Thread* thread, bool previous_epoch = false, size_t size = 0);
-  static BufferPtr lease(BufferPtr old, Thread* thread, size_t size = 0);
+  static BufferPtr lease(BufferPtr old, Thread* thread, size_t size);
+
+  static BufferPtr acquire_thread_local(size_t size, Thread* thread);
+  static BufferPtr lease_thread_local(Thread* thread, size_t size = 0);
+
   static BufferPtr flush(BufferPtr old, size_t used, size_t requested, Thread* thread);
 
   size_t clear();
   size_t write();
-  size_t flush();
   void notify_threads();
 
   size_t write_static_type_set(Thread* thread);
@@ -102,7 +105,6 @@
   friend class JfrCheckpointFlush;
   friend class JfrCheckpointWriter;
   friend class JfrSerializer;
-  friend class JfrStackTraceRepository;
   template <typename, template <typename> class, typename, typename, bool>
   friend class JfrMemorySpace;
 };
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp	Tue Jun 30 20:09:03 2020 +0200
@@ -45,8 +45,8 @@
   }
 }
 
-JfrCheckpointWriter::JfrCheckpointWriter(Thread* thread, bool header /* true */, JfrCheckpointType type /* GENERIC */) :
-  JfrCheckpointWriterBase(JfrCheckpointManager::lease(thread), thread),
+JfrCheckpointWriter::JfrCheckpointWriter(Thread* thread, bool header /* true */, JfrCheckpointType type /* GENERIC */, bool global_lease /* true */) :
+  JfrCheckpointWriterBase(global_lease ? JfrCheckpointManager::lease(thread) : JfrCheckpointManager::lease_thread_local(thread), thread),
   _time(JfrTicks::now()),
   _offset(0),
   _count(0),
--- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp	Tue Jun 30 20:09:03 2020 +0200
@@ -72,7 +72,7 @@
   JfrCheckpointWriter(bool previous_epoch, Thread* thread, JfrCheckpointType type = GENERIC);
  public:
   JfrCheckpointWriter(JfrCheckpointType type = GENERIC);
-  JfrCheckpointWriter(Thread* thread, bool header = true, JfrCheckpointType mode = GENERIC);
+  JfrCheckpointWriter(Thread* thread, bool header = true, JfrCheckpointType mode = GENERIC, bool global_lease = true);
   ~JfrCheckpointWriter();
   void write_type(JfrTypeId type_id);
   void write_count(u4 nof_entries);
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp	Tue Jun 30 20:09:03 2020 +0200
@@ -28,6 +28,7 @@
 #include "jfr/recorder/checkpoint/types/jfrType.hpp"
 #include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp"
 #include "jfr/recorder/jfrRecorder.hpp"
+#include "jfr/support/jfrThreadLocal.hpp"
 #include "jfr/utilities/jfrIterator.hpp"
 #include "jfr/utilities/jfrLinkedList.inline.hpp"
 #include "memory/resourceArea.hpp"
@@ -105,7 +106,7 @@
   ResourceMark rm(t);
   HandleMark hm(t);
   JfrThreadConstant type_thread(t);
-  JfrCheckpointWriter writer(t, true, THREADS);
+  JfrCheckpointWriter writer(t, true, THREADS, false);
   writer.write_type(TYPE_THREAD);
   type_thread.serialize(writer);
   // create and install a checkpoint blob
@@ -115,12 +116,11 @@
 
 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);
+  if (!t->jfr_thread_local()->has_thread_blob()) {
+    create_thread_blob(t);
+  }
+  JfrCheckpointWriter writer(t, false, THREADS, false);
+  t->jfr_thread_local()->thread_blob()->write(writer);
 }
 
 class SerializerRegistrationGuard : public StackObj {
--- a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp	Tue Jun 30 20:09:03 2020 +0200
@@ -173,7 +173,7 @@
 
 static bool is_cds_dump_requested() {
   // we will not be able to launch recordings on startup if a cds dump is being requested
-  if (Arguments::is_dumping_archive()) {
+  if (Arguments::is_dumping_archive() && JfrOptionSet::start_flight_recording_options() != NULL) {
     warning("JFR will be disabled during CDS dumping");
     teardown_startup_support();
     return true;
--- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp	Tue Jun 30 20:09:03 2020 +0200
@@ -32,9 +32,10 @@
                          _identity(NULL),
                          _pos(NULL),
                          _top(NULL),
-                         _flags(0),
+                         _size(0),
                          _header_size(0),
-                         _size(0) {}
+                         _flags(0),
+                         _context(0) {}
 
 bool JfrBuffer::initialize(size_t header_size, size_t size) {
   assert(_next == NULL, "invariant");
@@ -52,7 +53,6 @@
 
 void JfrBuffer::reinitialize(bool exclusion /* false */) {
   acquire_critical_section_top();
-  assert(!lease(), "invariant");
   if (exclusion != excluded()) {
     // update
     if (exclusion) {
@@ -185,25 +185,25 @@
   EXCLUDED = 8
 };
 
-inline u2 load(const volatile u2* flags) {
-  assert(flags != NULL, "invariant");
-  return Atomic::load_acquire(flags);
+inline u1 load(const volatile u1* dest) {
+  assert(dest != NULL, "invariant");
+  return Atomic::load_acquire(dest);
 }
 
-inline void set(u2* flags, FLAG flag) {
-  assert(flags != NULL, "invariant");
+inline void set(u1* dest, u1 data) {
+  assert(dest != NULL, "invariant");
   OrderAccess::storestore();
-  *flags |= (u1)flag;
+  *dest |= data;
 }
 
-inline void clear(u2* flags, FLAG flag) {
-  assert(flags != NULL, "invariant");
+inline void clear(u1* dest, u1 data) {
+  assert(dest != NULL, "invariant");
   OrderAccess::storestore();
-  *flags ^= (u1)flag;
+  *dest ^= data;
 }
 
-inline bool test(const u2* flags, FLAG flag) {
-  return (u1)flag == (load(flags) & (u1)flag);
+inline bool test(const u1* dest, u1 data) {
+  return data == (load(dest) & data);
 }
 
 bool JfrBuffer::transient() const {
@@ -273,3 +273,15 @@
     clear(&_flags, RETIRED);
   }
 }
+
+u1 JfrBuffer::context() const {
+  return load(&_context);
+}
+
+void JfrBuffer::set_context(u1 context) {
+  set(&_context, context);
+}
+
+void JfrBuffer::clear_context() {
+  set(&_context, 0);
+}
--- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp	Tue Jun 30 20:09:03 2020 +0200
@@ -44,6 +44,10 @@
 // e.g. the delta must always be fully parsable.
 // _top can move concurrently by other threads but is always <= _pos.
 //
+// The _flags field holds generic tags applicable to all subsystems.
+//
+// The _context field can be used to set subsystem specific tags onto a buffer.
+//
 // Memory ordering:
 //
 //  Method                 Owner thread             Other threads
@@ -66,9 +70,10 @@
   const void* _identity;
   u1* _pos;
   mutable const u1* _top;
-  u2 _flags;
+  u4 _size;
   u2 _header_size;
-  u4 _size;
+  u1 _flags;
+  u1 _context;
 
   const u1* stable_top() const;
 
@@ -168,6 +173,10 @@
   bool excluded() const;
   void set_excluded();
   void clear_excluded();
+
+  u1 context() const;
+  void set_context(u1 context);
+  void clear_context();
 };
 
 #endif // SHARE_JFR_RECORDER_STORAGE_JFRBUFFER_HPP
--- a/src/hotspot/share/jfr/recorder/storage/jfrEpochStorage.hpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrEpochStorage.hpp	Tue Jun 30 20:09:03 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
  * 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,9 +33,19 @@
 
 /*
  * Provides storage as a function of an epoch, with iteration capabilities for the current and previous epoch.
- * Iteration over the current epoch is incremental while iteration over the previous epoch is complete,
- * including storage reclamation. The design caters to use cases having multiple incremental iterations
- * over the current epoch, and a single, complete, iteration over the previous epoch.
+ *
+ * When iterating the previous epoch, where exclusive access to buffers is assumed,
+ * all buffers will be reinitialized post-callback, with retired buffers reclaimed
+ * and moved onto the free list and non-retired buffers left in-place.
+ *
+ * When iterating the current epoch, where concurrent access to buffers is assumed,
+ * there exist two modes, controlled by the EagerReclaim parameter.
+ * By default, EagerReclaim is false, meaning no retired buffers are reclaimed during the current epoch.
+ * Setting EagerReclaim to true, retired buffers will be reclaimed post-callback, by reinitialization
+ * and by moving them onto the free list, just like is done when iterating the previous epoch.
+ *
+ * The design caters to use cases having multiple incremental iterations over the current epoch,
+ * and a single iteration over the previous epoch.
  *
  * The JfrEpochStorage can be specialized by the following policies:
  *
@@ -43,10 +53,12 @@
  *
  * RetrievalPolicy   see jfrMemorySpace.hpp for a description.
  *
+ * EagerReclaim      should retired buffers be reclaimed also during the current epoch (i.e. eagerly)
+ *
  */
-template <typename NodeType, template <typename> class RetrievalPolicy>
+template <typename NodeType, template <typename> class RetrievalPolicy, bool EagerReclaim = false>
 class JfrEpochStorageHost : public JfrCHeapObj {
-  typedef JfrMemorySpace<JfrEpochStorageHost<NodeType, RetrievalPolicy>,
+  typedef JfrMemorySpace<JfrEpochStorageHost<NodeType, RetrievalPolicy, EagerReclaim>,
                          RetrievalPolicy,
                          JfrConcurrentQueue<NodeType>,
                          JfrLinkedList<NodeType>,
--- a/src/hotspot/share/jfr/recorder/storage/jfrEpochStorage.inline.hpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrEpochStorage.inline.hpp	Tue Jun 30 20:09:03 2020 +0200
@@ -32,23 +32,23 @@
 #include "jfr/utilities/jfrLinkedList.inline.hpp"
 #include "logging/log.hpp"
 
-template <typename NodeType, template <typename> class RetrievalPolicy>
-JfrEpochStorageHost<NodeType, RetrievalPolicy>::JfrEpochStorageHost() : _mspace(NULL) {}
+template <typename NodeType, template <typename> class RetrievalPolicy, bool EagerReclaim>
+JfrEpochStorageHost<NodeType, RetrievalPolicy, EagerReclaim>::JfrEpochStorageHost() : _mspace(NULL) {}
 
-template <typename NodeType, template <typename> class RetrievalPolicy>
-JfrEpochStorageHost<NodeType, RetrievalPolicy>::~JfrEpochStorageHost() {
+template <typename NodeType, template <typename> class RetrievalPolicy, bool EagerReclaim>
+JfrEpochStorageHost<NodeType, RetrievalPolicy, EagerReclaim>::~JfrEpochStorageHost() {
   delete _mspace;
 }
 
-template <typename NodeType, template <typename> class RetrievalPolicy>
-bool JfrEpochStorageHost<NodeType, RetrievalPolicy>::initialize(size_t min_elem_size, size_t free_list_cache_count_limit, size_t cache_prealloc_count) {
+template <typename NodeType, template <typename> class RetrievalPolicy, bool EagerReclaim>
+bool JfrEpochStorageHost<NodeType, RetrievalPolicy, EagerReclaim>::initialize(size_t min_elem_size, size_t free_list_cache_count_limit, size_t cache_prealloc_count) {
   assert(_mspace == NULL, "invariant");
   _mspace = new EpochMspace(min_elem_size, free_list_cache_count_limit, this);
   return _mspace != NULL && _mspace->initialize(cache_prealloc_count);
 }
 
-template <typename NodeType, template <typename> class RetrievalPolicy>
-inline NodeType* JfrEpochStorageHost<NodeType, RetrievalPolicy>::acquire(size_t size, Thread* thread) {
+template <typename NodeType, template <typename> class RetrievalPolicy, bool EagerReclaim>
+inline NodeType* JfrEpochStorageHost<NodeType, RetrievalPolicy, EagerReclaim>::acquire(size_t size, Thread* thread) {
   BufferPtr buffer = mspace_acquire_to_live_list(size, _mspace, thread);
   if (buffer == NULL) {
     log_warning(jfr)("Unable to allocate " SIZE_FORMAT " bytes of %s.", _mspace->min_element_size(), "epoch storage");
@@ -58,29 +58,37 @@
   return buffer;
 }
 
-template <typename NodeType, template <typename> class RetrievalPolicy>
-void JfrEpochStorageHost<NodeType, RetrievalPolicy>::release(NodeType* buffer) {
+template <typename NodeType, template <typename> class RetrievalPolicy, bool EagerReclaim>
+void JfrEpochStorageHost<NodeType, RetrievalPolicy, EagerReclaim>::release(NodeType* buffer) {
   assert(buffer != NULL, "invariant");
   buffer->set_retired();
 }
 
-template <typename NodeType, template <typename> class RetrievalPolicy>
-void JfrEpochStorageHost<NodeType, RetrievalPolicy>::register_full(NodeType* buffer, Thread* thread) {
+template <typename NodeType, template <typename> class RetrievalPolicy, bool EagerReclaim>
+void JfrEpochStorageHost<NodeType, RetrievalPolicy, EagerReclaim>::register_full(NodeType* buffer, Thread* thread) {
   // nothing here at the moment
 }
 
-template <typename NodeType, template <typename> class RetrievalPolicy>
+template <typename NodeType, template <typename> class RetrievalPolicy, bool EagerReclaim>
 template <typename Functor>
-void JfrEpochStorageHost<NodeType, RetrievalPolicy>::iterate(Functor& functor, bool previous_epoch) {
-  typedef ReleaseRetiredToFreeListOp<EpochMspace, typename EpochMspace::LiveList> ReleaseStorage;
-  typedef CompositeOperation<Functor, ReleaseStorage> PreviousEpochOperation;
+void JfrEpochStorageHost<NodeType, RetrievalPolicy, EagerReclaim>::iterate(Functor& functor, bool previous_epoch) {
+  typedef ReinitializeAllReleaseRetiredOp<EpochMspace, typename EpochMspace::LiveList> PreviousEpochReleaseOperation;
+  typedef CompositeOperation<Functor, PreviousEpochReleaseOperation> PreviousEpochOperation;
+  typedef ReleaseRetiredOp<EpochMspace, typename EpochMspace::LiveList> CurrentEpochReleaseOperation;
+  typedef CompositeOperation<Functor, CurrentEpochReleaseOperation> CurrentEpochOperation;
   if (previous_epoch) {
-    ReleaseStorage rs(_mspace, _mspace->live_list(true));
-    PreviousEpochOperation peo(&functor, &rs);
-    process_live_list(peo, _mspace, true);
+    PreviousEpochReleaseOperation pero(_mspace, _mspace->live_list(true));
+    PreviousEpochOperation peo(&functor, &pero);
+    process_live_list(peo, _mspace, true); // previous epoch list
     return;
   }
-  process_live_list(functor, _mspace, false);
+  if (EagerReclaim) {
+    CurrentEpochReleaseOperation cero(_mspace, _mspace->live_list());
+    CurrentEpochOperation ceo(&functor, &cero);
+    process_live_list(ceo, _mspace, false); // current epoch list
+    return;
+  }
+  process_live_list(functor, _mspace, false); // current epoch list
 }
 
 #ifdef ASSERT
@@ -100,8 +108,8 @@
   }
 };
 
-template <typename NodeType, template <typename> class RetrievalPolicy>
-void JfrEpochStorageHost<NodeType, RetrievalPolicy>::verify_previous_empty() const {
+template <typename NodeType, template <typename> class RetrievalPolicy, bool EagerReclaim>
+void JfrEpochStorageHost<NodeType, RetrievalPolicy, EagerReclaim>::verify_previous_empty() const {
   typedef EmptyVerifier<JfrEpochStorage::Mspace> VerifyEmptyMspace;
   VerifyEmptyMspace vem(_mspace);
   process_live_list(vem, _mspace, true);
--- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp	Tue Jun 30 20:09:03 2020 +0200
@@ -493,14 +493,14 @@
 }
 
 template <typename Mspace, typename List>
-class ReleaseOpWithExcision : public ReleaseOp<Mspace> {
+class ReleaseWithExcisionOp : public ReleaseOp<Mspace> {
  private:
   List& _list;
   typename List::NodePtr _prev;
   size_t _count;
   size_t _amount;
  public:
-  ReleaseOpWithExcision(Mspace* mspace, List& list) :
+  ReleaseWithExcisionOp(Mspace* mspace, List& list) :
     ReleaseOp<Mspace>(mspace), _list(list), _prev(NULL), _count(0), _amount(0) {}
   bool process(typename List::NodePtr node);
   size_t processed() const { return _count; }
@@ -508,7 +508,7 @@
 };
 
 template <typename Mspace, typename List>
-inline bool ReleaseOpWithExcision<Mspace, List>::process(typename List::NodePtr node) {
+inline bool ReleaseWithExcisionOp<Mspace, List>::process(typename List::NodePtr node) {
   assert(node != NULL, "invariant");
   if (node->transient()) {
     _prev = _list.excise(_prev, node);
@@ -569,20 +569,49 @@
 }
 
 template <typename Mspace, typename FromList>
-class ReleaseRetiredToFreeListOp : public StackObj {
+class ReleaseRetiredOp : public StackObj {
+private:
+  Mspace* _mspace;
+  FromList& _list;
+  typename Mspace::NodePtr _prev;
+public:
+  typedef typename Mspace::Node Node;
+  ReleaseRetiredOp(Mspace* mspace, FromList& list) :
+    _mspace(mspace), _list(list), _prev(NULL) {}
+  bool process(Node* node);
+};
+
+template <typename Mspace, typename FromList>
+inline bool ReleaseRetiredOp<Mspace, FromList>::process(typename Mspace::Node* node) {
+  assert(node != NULL, "invariant");
+  if (node->retired()) {
+    _prev = _list.excise(_prev, node);
+    node->reinitialize();
+    assert(node->empty(), "invariant");
+    assert(!node->retired(), "invariant");
+    node->release();
+    mspace_release(node, _mspace);
+  } else {
+    _prev = node;
+  }
+  return true;
+}
+
+template <typename Mspace, typename FromList>
+class ReinitializeAllReleaseRetiredOp : public StackObj {
 private:
   Mspace* _mspace;
   FromList& _list;
   typename Mspace::NodePtr _prev;
  public:
   typedef typename Mspace::Node Node;
-  ReleaseRetiredToFreeListOp(Mspace* mspace, FromList& list) :
+  ReinitializeAllReleaseRetiredOp(Mspace* mspace, FromList& list) :
     _mspace(mspace), _list(list), _prev(NULL) {}
   bool process(Node* node);
 };
 
 template <typename Mspace, typename FromList>
-inline bool ReleaseRetiredToFreeListOp<Mspace, FromList>::process(typename Mspace::Node* node) {
+inline bool ReinitializeAllReleaseRetiredOp<Mspace, FromList>::process(typename Mspace::Node* node) {
   assert(node != NULL, "invariant");
   // assumes some means of exclusive access to node
   const bool retired = node->retired();
--- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp	Tue Jun 30 20:09:03 2020 +0200
@@ -208,7 +208,7 @@
 typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation;
 typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation;
 typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation;
-typedef ReleaseOpWithExcision<JfrStringPoolMspace, JfrStringPoolMspace::LiveList> ReleaseOperation;
+typedef ReleaseWithExcisionOp<JfrStringPoolMspace, JfrStringPoolMspace::LiveList> ReleaseOperation;
 typedef CompositeOperation<ExclusiveWriteOperation, ReleaseOperation> WriteReleaseOperation;
 typedef CompositeOperation<ExclusiveDiscardOperation, ReleaseOperation> DiscardReleaseOperation;
 
--- a/src/hotspot/share/jfr/utilities/jfrConcurrentLinkedListHost.inline.hpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/jfr/utilities/jfrConcurrentLinkedListHost.inline.hpp	Tue Jun 30 20:09:03 2020 +0200
@@ -34,7 +34,7 @@
 
 /*
  * The removal marker (i.e. the excision bit) is represented by '( )' as part of state description comments:
- * node --> next becomes (node) --> next, when node is logically deleted.
+ * "node --> next" becomes "(node) --> next", when node is logically deleted.
  */
 template <typename Node>
 inline Node* mark_for_removal(Node* node) {
@@ -47,7 +47,7 @@
 
 /*
  * The insertion marker (i.e. the insertion bit) is represented by '[ ]' as part of state description comments:
- * "node --> next" becomes "[node} --> next", in an attempt to convey node as being exlusively reserved.
+ * "node --> next" becomes "[node] --> next", in an attempt to convey node as being exlusively reserved.
  */
 template <typename Node>
 inline bool mark_for_insertion(Node* node, const Node* tail) {
--- a/src/hotspot/share/opto/loopnode.hpp	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/hotspot/share/opto/loopnode.hpp	Tue Jun 30 20:09:03 2020 +0200
@@ -485,6 +485,7 @@
   Node *_head;                  // Head of loop
   Node *_tail;                  // Tail of loop
   inline Node *tail();          // Handle lazy update of _tail field
+  inline Node *head();          // Handle lazy update of _head field
   PhaseIdealLoop* _phase;
   int _local_loop_unroll_limit;
   int _local_loop_unroll_factor;
@@ -1579,6 +1580,13 @@
   return _tail;
 }
 
+inline Node* IdealLoopTree::head() {
+  // Handle lazy update of _head field.
+  if (_head->in(0) == NULL) {
+    _head = _phase->get_ctrl(_head);
+  }
+  return _head;
+}
 
 // Iterate over the loop tree using a preorder, left-to-right traversal.
 //
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CompressedReadStream.java	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CompressedReadStream.java	Tue Jun 30 20:09:03 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
  * 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 @@
     int rl = readInt();
     int h = reverseInt(rh);
     int l = reverseInt(rl);
-    return Double.longBitsToDouble((h << 32) | ((long)l & 0x00000000FFFFFFFFL));
+    return Double.longBitsToDouble(((long)h << 32) | ((long)l & 0x00000000FFFFFFFFL));
   }
 
   public long readLong() {
--- a/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/LauncherData.java	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/jdk.incubator.jpackage/share/classes/jdk/incubator/jpackage/internal/LauncherData.java	Tue Jun 30 20:09:03 2020 +0200
@@ -26,7 +26,9 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.Reader;
 import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleReference;
 import java.nio.file.Files;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
@@ -34,7 +36,9 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.Properties;
 import java.util.Set;
 import java.util.function.Supplier;
 import java.util.jar.Attributes;
@@ -42,13 +46,14 @@
 import java.util.jar.Manifest;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import static jdk.incubator.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE;
 
 /**
  * Extracts data needed to run application from parameters.
  */
 final class LauncherData {
     boolean isModular() {
-        return moduleDescriptor != null;
+        return moduleInfo != null;
     }
 
     String qualifiedClassName() {
@@ -65,7 +70,7 @@
 
     String moduleName() {
         verifyIsModular(true);
-        return moduleDescriptor.name();
+        return moduleInfo.name;
     }
 
     List<Path> modulePath() {
@@ -84,11 +89,7 @@
 
     String getAppVersion() {
         if (isModular()) {
-            ModuleDescriptor.Version ver = moduleDescriptor.version().orElse(null);
-            if (ver != null) {
-                return ver.toString();
-            }
-            return moduleDescriptor.rawVersion().orElse(null);
+            return moduleInfo.version;
         }
 
         return null;
@@ -98,7 +99,7 @@
     }
 
     private void verifyIsModular(boolean isModular) {
-        if ((moduleDescriptor != null) != isModular) {
+        if ((moduleInfo != null) != isModular) {
             throw new IllegalStateException();
         }
     }
@@ -107,9 +108,19 @@
             ConfigException, IOException {
 
         final String mainModule = getMainModule(params);
+        final LauncherData result;
         if (mainModule == null) {
-            return createNonModular(params);
+            result = createNonModular(params);
+        } else {
+            result = createModular(mainModule, params);
         }
+        result.initClasspath(params);
+        return result;
+    }
+
+    private static LauncherData createModular(String mainModule,
+            Map<String, ? super Object> params) throws ConfigException,
+            IOException {
 
         LauncherData launcherData = new LauncherData();
 
@@ -123,18 +134,34 @@
         }
         launcherData.modulePath = getModulePath(params);
 
-        launcherData.moduleDescriptor = JLinkBundlerHelper.createModuleFinder(
-                launcherData.modulePath).find(moduleName).orElseThrow(
-                () -> new ConfigException(MessageFormat.format(I18N.getString(
-                        "error.no-module-in-path"), moduleName), null)).descriptor();
+        // Try to find module in the specified module path list.
+        ModuleReference moduleRef = JLinkBundlerHelper.createModuleFinder(
+                launcherData.modulePath).find(moduleName).orElse(null);
+
+        if (moduleRef != null) {
+            launcherData.moduleInfo = ModuleInfo.fromModuleDescriptor(
+                    moduleRef.descriptor());
+        } else if (params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID())) {
+            // Failed to find module in the specified module path list and
+            // there is external runtime given to jpackage.
+            // Lookup module in this runtime.
+            Path cookedRuntime = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params).toPath();
+            launcherData.moduleInfo = ModuleInfo.fromCookedRuntime(moduleName,
+                    cookedRuntime);
+        }
+
+        if (launcherData.moduleInfo == null) {
+            throw new ConfigException(MessageFormat.format(I18N.getString(
+                    "error.no-module-in-path"), moduleName), null);
+        }
 
         if (launcherData.qualifiedClassName == null) {
-            launcherData.qualifiedClassName = launcherData.moduleDescriptor.mainClass().orElseThrow(
-                    () -> new ConfigException(I18N.getString("ERR_NoMainClass"),
-                            null));
+            launcherData.qualifiedClassName = launcherData.moduleInfo.mainClass;
+            if (launcherData.qualifiedClassName == null) {
+                throw new ConfigException(I18N.getString("ERR_NoMainClass"), null);
+            }
         }
 
-        launcherData.initClasspath(params);
         return launcherData;
     }
 
@@ -195,7 +222,6 @@
                             launcherData.mainJarName));
         }
 
-        launcherData.initClasspath(params);
         return launcherData;
     }
 
@@ -263,8 +289,17 @@
 
     private static List<Path> getModulePath(Map<String, ? super Object> params)
             throws ConfigException {
-        return getPathListParameter(Arguments.CLIOptions.MODULE_PATH.getId(),
-                params);
+        List<Path> modulePath = getPathListParameter(Arguments.CLIOptions.MODULE_PATH.getId(), params);
+
+        if (params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID())) {
+            Path runtimePath = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params).toPath();
+            runtimePath = runtimePath.resolve("lib");
+            modulePath = Stream.of(modulePath, List.of(runtimePath))
+                    .flatMap(List::stream)
+                    .collect(Collectors.toUnmodifiableList());
+        }
+
+        return modulePath;
     }
 
     private static List<Path> getPathListParameter(String paramName,
@@ -281,5 +316,77 @@
     private Path mainJarName;
     private List<Path> classPath;
     private List<Path> modulePath;
-    private ModuleDescriptor moduleDescriptor;
+    private ModuleInfo moduleInfo;
+
+    private static final class ModuleInfo {
+        String name;
+        String version;
+        String mainClass;
+
+        static ModuleInfo fromModuleDescriptor(ModuleDescriptor md) {
+            ModuleInfo result = new ModuleInfo();
+            result.name = md.name();
+            result.mainClass = md.mainClass().orElse(null);
+
+            ModuleDescriptor.Version ver = md.version().orElse(null);
+            if (ver != null) {
+                result.version = ver.toString();
+            } else {
+                result.version = md.rawVersion().orElse(null);
+            }
+
+            return result;
+        }
+
+        static ModuleInfo fromCookedRuntime(String moduleName,
+                Path cookedRuntime) {
+            Objects.requireNonNull(moduleName);
+
+            // We can't extract info about version and main class of a module
+            // linked in external runtime without running ModuleFinder in that
+            // runtime. But this is too much work as the runtime might have been
+            // coocked without native launchers. So just make sure the module
+            // is linked in the runtime by simply analysing the data
+            // of `release` file.
+
+            final Path releaseFile;
+            if (!Platform.isMac()) {
+                releaseFile = cookedRuntime.resolve("release");
+            } else {
+                // On Mac `cookedRuntime` can be runtime root or runtime home.
+                Path runtimeHome = cookedRuntime.resolve("Contents/Home");
+                if (!Files.isDirectory(runtimeHome)) {
+                    runtimeHome = cookedRuntime;
+                }
+                releaseFile = runtimeHome.resolve("release");
+            }
+
+            try (Reader reader = Files.newBufferedReader(releaseFile)) {
+                Properties props = new Properties();
+                props.load(reader);
+                String moduleList = props.getProperty("MODULES");
+                if (moduleList == null) {
+                    return null;
+                }
+
+                if ((moduleList.startsWith("\"") && moduleList.endsWith("\""))
+                        || (moduleList.startsWith("\'") && moduleList.endsWith(
+                        "\'"))) {
+                    moduleList = moduleList.substring(1, moduleList.length() - 1);
+                }
+
+                if (!List.of(moduleList.split("\\s+")).contains(moduleName)) {
+                    return null;
+                }
+            } catch (IOException|IllegalArgumentException ex) {
+                Log.verbose(ex);
+                return null;
+            }
+
+            ModuleInfo result = new ModuleInfo();
+            result.name = moduleName;
+
+            return result;
+        }
+    }
 }
--- a/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WinMsiBundler.java	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/WinMsiBundler.java	Tue Jun 30 20:09:03 2020 +0200
@@ -314,6 +314,7 @@
                     .launchersDirectory()
                     .resolve(APP_NAME.fetchFrom(params) + ".exe");
         }
+        installerIcon = installerIcon.toAbsolutePath();
 
         params.put(WIN_APP_IMAGE.getID(), appDir);
 
@@ -461,6 +462,8 @@
         boolean enableLicenseUI = (LICENSE_FILE.fetchFrom(params) != null);
         boolean enableInstalldirUI = INSTALLDIR_CHOOSER.fetchFrom(params);
 
+        wixPipeline.addLightOptions("-sice:ICE27");
+
         if (!MSI_SYSTEM_WIDE.fetchFrom(params)) {
             wixPipeline.addLightOptions("-sice:ICE91");
         }
--- a/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/main.wxs	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/jdk.incubator.jpackage/windows/classes/jdk/incubator/jpackage/internal/resources/main.wxs	Tue Jun 30 20:09:03 2020 +0200
@@ -12,6 +12,17 @@
   <?define JpInstallerVersion=200 ?>
   <?define JpCompressedMsi=yes ?>
 
+  <?ifdef JpAllowUpgrades ?>
+    <?define JpUpgradeVersionOnlyDetectUpgrade="no"?>
+  <?else?>
+    <?define JpUpgradeVersionOnlyDetectUpgrade="yes"?>
+  <?endif?>
+  <?ifdef JpAllowDowngrades ?>
+    <?define JpUpgradeVersionOnlyDetectDowngrade="no"?>
+  <?else?>
+    <?define JpUpgradeVersionOnlyDetectDowngrade="yes"?>
+  <?endif?>
+
   <?include $(var.JpConfigDir)/overrides.wxi ?>
 
   <Product
@@ -32,44 +43,26 @@
 
     <Media Id="1" Cabinet="Data.cab" EmbedCab="yes" />
 
-    <?ifdef JpAllowDowngrades ?>
-    <?ifdef JpAllowUpgrades ?>
-    <MajorUpgrade
-      AllowDowngrades="yes"
-      Disallow="no"
-    />
-    <?endif?>
-    <?endif?>
-
-    <?ifdef JpAllowDowngrades ?>
-    <?ifndef JpAllowUpgrades ?>
-    <MajorUpgrade
-      AllowDowngrades="yes"
-      Disallow="yes"
-      DisallowUpgradeErrorMessage="!(loc.DisallowUpgradeErrorMessage)"
-    />
-    <?endif?>
-    <?endif?>
+    <Upgrade Id="$(var.JpProductUpgradeCode)">
+      <UpgradeVersion
+        OnlyDetect="$(var.JpUpgradeVersionOnlyDetectUpgrade)"
+        Property="JP_UPGRADABLE_FOUND"
+        Maximum="$(var.JpAppVersion)"
+        MigrateFeatures="yes"
+        IncludeMaximum="$(var.JpUpgradeVersionOnlyDetectUpgrade)" />
+      <UpgradeVersion
+        OnlyDetect="$(var.JpUpgradeVersionOnlyDetectDowngrade)"
+        Property="JP_DOWNGRADABLE_FOUND"
+        Minimum="$(var.JpAppVersion)"
+        MigrateFeatures="yes"
+        IncludeMinimum="$(var.JpUpgradeVersionOnlyDetectDowngrade)" />
+    </Upgrade>
 
-    <?ifndef JpAllowDowngrades ?>
-    <?ifdef JpAllowUpgrades ?>
-    <MajorUpgrade
-      AllowDowngrades="no"
-      Disallow="no"
-      DowngradeErrorMessage="!(loc.DowngradeErrorMessage)"
-    />
-    <?endif?>
+    <?ifndef JpAllowUpgrades ?>
+    <CustomAction Id="JpDisallowUpgrade" Error="!(loc.DisallowUpgradeErrorMessage)" />
     <?endif?>
-
     <?ifndef JpAllowDowngrades ?>
-    <?ifndef JpAllowUpgrades ?>
-    <MajorUpgrade
-      AllowDowngrades="no"
-      Disallow="yes"
-      DowngradeErrorMessage="!(loc.DowngradeErrorMessage)"
-      DisallowUpgradeErrorMessage="!(loc.DisallowUpgradeErrorMessage)"
-    />
-    <?endif?>
+    <CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
     <?endif?>
 
     <!-- Standard required root -->
@@ -142,6 +135,14 @@
 
     <InstallExecuteSequence>
       <Custom Action="JpSetARPINSTALLLOCATION" After="CostFinalize">Not Installed</Custom>
+      <?ifndef JpAllowUpgrades ?>
+      <Custom Action="JpDisallowUpgrade" After="FindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
+      <?endif?>
+      <?ifndef JpAllowDowngrades ?>
+      <Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
+      <?endif?>
+      <RemoveExistingProducts Before="CostInitialize"/>
     </InstallExecuteSequence>
+
   </Product>
 </Wix>
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LateMembarInsertionTest.java	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LateMembarInsertionTest.java	Tue Jun 30 20:09:03 2020 +0200
@@ -195,45 +195,6 @@
         verifyMembars("volatileFieldStore", membarsExpected());
     }
 
-    // Unused field load should be optimized out and leave no barrier behind
-    @SuppressWarnings("unused")
-    public static void volatileFieldStoreUnusedVolatileFieldLoadVolatileFieldStore(int v2) {
-        VolatileAccess2.field = v2;
-        int v1 = VolatileAccess.field;
-        VolatileAccess2.field = v2;
-    }
-
-    @Test
-    public void test09() {
-        StructuredGraph graph = getFinalGraph(getResolvedJavaMethod("volatileFieldStoreUnusedVolatileFieldLoadVolatileFieldStore"));
-        List<TypePair> accesses = getAccesses(graph);
-
-        Assert.assertEquals(accesses.size(), 2);
-        Assert.assertEquals(accesses.get(0).getType(), volatileAccess2Type);
-        Assert.assertEquals(accesses.get(1).getType(), volatileAccess2Type);
-        Assert.assertTrue(accesses.get(0).isWrite());
-        Assert.assertTrue(accesses.get(1).isWrite());
-        Assert.assertEquals(membarsExpected() ? 4 : 0, getMembars(graph).size());
-    }
-
-    // Unused field load should be optimized out and leave no barrier behind
-    @SuppressWarnings("unused")
-    public static void unusedVolatileFieldLoadVolatileFieldStore(int v2) {
-        int v1 = VolatileAccess.field;
-        VolatileAccess2.field = v2;
-    }
-
-    @Test
-    public void test10() {
-        StructuredGraph graph = getFinalGraph(getResolvedJavaMethod("unusedVolatileFieldLoadVolatileFieldStore"));
-        List<TypePair> accesses = getAccesses(graph);
-
-        Assert.assertEquals(accesses.size(), 1);
-        Assert.assertEquals(accesses.get(0).getType(), volatileAccess2Type);
-        Assert.assertTrue(accesses.get(0).isWrite());
-        Assert.assertEquals(membarsExpected() ? 2 : 0, getMembars(graph).size());
-    }
-
     public static int unsafeVolatileFieldLoad(Object o, long offset) {
         return UNSAFE.getIntVolatile(o, offset);
     }
@@ -346,11 +307,4 @@
         return javaType;
     }
 
-    private static List<Node> getMembars(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 MembarNode).collect(Collectors.toList());
-    }
 }
--- /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/VolatileReadEliminateWrongMemoryStateTest.java	Tue Jun 30 20:09:03 2020 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, Red Hat Inc. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package org.graalvm.compiler.core.test;
+
+import org.junit.Test;
+
+// See https://bugs.openjdk.java.net/browse/JDK-8247832
+public class VolatileReadEliminateWrongMemoryStateTest extends GraalCompilerTest {
+
+    private static volatile int volatileField;
+    private static int field;
+
+    @SuppressWarnings("unused")
+    public static int testMethod() {
+        field = 0;
+        int v = volatileField;
+        field += 1;
+        v = volatileField;
+        field += 1;
+        return field;
+    }
+
+    @Test
+    public void test1() {
+        test("testMethod");
+    }
+
+    public static void testMethod2(Object obj) {
+        synchronized (obj) {
+            volatileField++;
+        }
+    }
+
+    @Test
+    public void test2() {
+        test("testMethod2", new Object());
+    }
+}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/VolatileReadNode.java	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/VolatileReadNode.java	Tue Jun 30 20:09:03 2020 +0200
@@ -33,8 +33,6 @@
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
-import org.graalvm.compiler.graph.spi.Simplifiable;
-import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
@@ -43,20 +41,11 @@
 import jdk.internal.vm.compiler.word.LocationIdentity;
 
 @NodeInfo(nameTemplate = "VolatileRead#{p#location/s}", allowedUsageTypes = Memory, cycles = CYCLES_2, size = SIZE_1)
-public class VolatileReadNode extends ReadNode implements SingleMemoryKill, Lowerable, Simplifiable {
+public class VolatileReadNode extends ReadNode implements SingleMemoryKill, Lowerable {
     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);
-    }
-
-    @Override
-    public void simplify(SimplifierTool tool) {
-        if (lastLocationAccess != null && hasOnlyUsagesOfType(Memory)) {
-            replaceAtUsages(lastLocationAccess.asNode(), Memory);
-            assert hasNoUsages();
-            graph().removeFixed(this);
-        }
+    public VolatileReadNode(AddressNode address, Stamp stamp, BarrierType barrierType) {
+        super(TYPE, address, LocationIdentity.any(), stamp, null, barrierType, false, null);
     }
 
     @Override
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java	Tue Jun 30 19:20:22 2020 +0200
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java	Tue Jun 30 20:09:03 2020 +0200
@@ -475,7 +475,7 @@
         ReadNode memoryRead = null;
         BarrierType barrierType = barrierSet.fieldLoadBarrierType(field, getStorageKind(field));
         if (loadField.isVolatile()) {
-            memoryRead = graph.add(new VolatileReadNode(address, fieldLocationIdentity(field), loadStamp, barrierType));
+            memoryRead = graph.add(new VolatileReadNode(address, loadStamp, barrierType));
         } else {
             memoryRead = graph.add(new ReadNode(address, fieldLocationIdentity(field), loadStamp, barrierType));
         }
@@ -767,7 +767,7 @@
         AddressNode address = createUnsafeAddress(graph, load.object(), load.offset());
         ReadNode memoryRead = null;
         if (load.isVolatile()) {
-            memoryRead = new VolatileReadNode(address, load.getLocationIdentity(), loadStamp, barrierSet.readBarrierType(load));
+            memoryRead = new VolatileReadNode(address, loadStamp, barrierSet.readBarrierType(load));
         } else {
             memoryRead = new ReadNode(address, load.getLocationIdentity(), loadStamp, barrierSet.readBarrierType(load));
         }
--- a/test/hotspot/jtreg/ProblemList-aot.txt	Tue Jun 30 19:20:22 2020 +0200
+++ b/test/hotspot/jtreg/ProblemList-aot.txt	Tue Jun 30 20:09:03 2020 +0200
@@ -85,8 +85,5 @@
 compiler/intrinsics/sha/sanity/TestSHA1MultiBlockIntrinsics.java    8167430 generic-all
 compiler/intrinsics/sha/sanity/TestSHA512MultiBlockIntrinsics.java  8167430 generic-all
 
-compiler/ciReplay/TestServerVM.java      8248265 generic-all
-compiler/ciReplay/TestVMNoCompLevel.java 8248265 generic-all
-
 vmTestbase/vm/mlvm/indy/stress/java/relinkMutableCallSiteFreq/Test.java   8226689 generic-all
 vmTestbase/vm/mlvm/indy/stress/java/relinkVolatileCallSiteFreq/Test.java  8226689 generic-all
--- a/test/hotspot/jtreg/compiler/ciReplay/CiReplayBase.java	Tue Jun 30 19:20:22 2020 +0200
+++ b/test/hotspot/jtreg/compiler/ciReplay/CiReplayBase.java	Tue Jun 30 20:09:03 2020 +0200
@@ -70,9 +70,12 @@
     private static final String[] REPLAY_OPTIONS = new String[]{DISABLE_COREDUMP_ON_CRASH,
         "-XX:+ReplayCompiles", REPLAY_FILE_OPTION};
     protected final Optional<Boolean> runServer;
+    private static int dummy;
 
-    public static class EmptyMain {
+    public static class TestMain {
         public static void main(String[] args) {
+            // Do something because empty methods might not be called/compiled.
+            dummy = 42;
         }
     }
 
@@ -140,7 +143,7 @@
             options.addAll(Arrays.asList(REPLAY_GENERATION_OPTIONS));
             options.addAll(Arrays.asList(vmopts));
             options.add(needCoreDump ? ENABLE_COREDUMP_ON_CRASH : DISABLE_COREDUMP_ON_CRASH);
-            options.add(EmptyMain.class.getName());
+            options.add(TestMain.class.getName());
             if (needCoreDump) {
                 crashOut = ProcessTools.executeProcess(getTestJvmCommandlineWithPrefix(
                         RUN_SHELL_NO_LIMIT, options.toArray(new String[0])));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestShenandoahLRBInOuterStripMinedLoop.java	Tue Jun 30 20:09:03 2020 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+/**
+ * @test
+ * @bug 8247824
+ * @summary CTW: C2 (Shenandoah) compilation fails with SEGV in SBC2Support::pin_and_expand
+ * @requires vm.flavor == "server"
+ * @requires vm.gc.Shenandoah & !vm.graal.enabled
+ *
+ * @run main/othervm -XX:-BackgroundCompilation -XX:+UseShenandoahGC -XX:LoopMaxUnroll=0 TestShenandoahLRBInOuterStripMinedLoop
+ *
+ */
+
+import java.util.Arrays;
+
+public class TestShenandoahLRBInOuterStripMinedLoop {
+    public static void main(String[] args) {
+        A[] array = new A[4000];
+        Arrays.fill(array, new A());
+        for (int i = 0; i < 20_0000; i++) {
+            test(array);
+        }
+    }
+
+    private static int test(A[] array) {
+        A a = null;
+        int v = 1;
+        A b = null;
+        for (int i = 0; i < 2000; i++) {
+            a = array[i];
+            b = array[2*i];
+            v *= 2;
+        }
+        return a.f + b.f + v;
+    }
+
+    private static class A {
+        public int f;
+    }
+}
--- a/test/jdk/ProblemList.txt	Tue Jun 30 19:20:22 2020 +0200
+++ b/test/jdk/ProblemList.txt	Tue Jun 30 20:09:03 2020 +0200
@@ -924,10 +924,11 @@
 
 # jdk_jpackage
 
-tools/jpackage/share/EmptyFolderPackageTest.java                8248059 macosx-all
-tools/jpackage/share/IconTest.java                              8248059 macosx-all
-tools/jpackage/share/AppImagePackageTest.java                   8248059 macosx-all
-tools/jpackage/share/SimplePackageTest.java                     8248059 macosx-all
-tools/jpackage/share/jdk/jpackage/tests/BasicTest.java          8248059 macosx-all
+tools/jpackage/share/EmptyFolderPackageTest.java                    8248059 macosx-all
+tools/jpackage/share/IconTest.java                                  8248059 macosx-all
+tools/jpackage/share/AppImagePackageTest.java                       8248059 macosx-all
+tools/jpackage/share/SimplePackageTest.java                         8248059 macosx-all
+tools/jpackage/share/jdk/jpackage/tests/BasicTest.java              8248059 macosx-all
+tools/jpackage/share/jdk/jpackage/tests/ModulePathTest3.java#id0    8248418 generic-all
 
 ############################################################################
--- a/test/jdk/java/net/MulticastSocket/Promiscuous.java	Tue Jun 30 19:20:22 2020 +0200
+++ b/test/jdk/java/net/MulticastSocket/Promiscuous.java	Tue Jun 30 20:09:03 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -119,7 +119,7 @@
 
             // join groups on all network interfaces
             NetworkConfiguration.probe()
-                    .multicastInterfaces(false)
+                    .ip4MulticastInterfaces(false)
                     .forEach((nic) -> {
                 try {
                     mc1.joinGroup(toSocketAddress(group1), nic);
@@ -155,7 +155,7 @@
 
             // leave groups on all network interfaces
             NetworkConfiguration.probe()
-                    .multicastInterfaces(false)
+                    .ip4MulticastInterfaces(false)
                     .forEach((nic) -> {
                 try {
                     mc1.leaveGroup(toSocketAddress(group1), nic);
--- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java	Tue Jun 30 19:20:22 2020 +0200
+++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java	Tue Jun 30 20:09:03 2020 +0200
@@ -308,7 +308,7 @@
     public static JPackageCommand helloAppImage(JavaAppDesc javaAppDesc) {
         JPackageCommand cmd = new JPackageCommand();
         cmd.setDefaultInputOutput().setDefaultAppName();
-        PackageType.IMAGE.applyTo(cmd);
+        cmd.setPackageType(PackageType.IMAGE);
         new HelloApp(javaAppDesc).addTo(cmd);
         return cmd;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jpackage/share/jdk/jpackage/tests/ModulePathTest3.java	Tue Jun 30 20:09:03 2020 +0200
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute 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.jpackage.tests;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+import jdk.incubator.jpackage.internal.AppImageFile;
+import jdk.jpackage.test.HelloApp;
+import jdk.jpackage.test.JavaAppDesc;
+import jdk.jpackage.test.Annotations.Test;
+import jdk.jpackage.test.Annotations.Parameter;
+import jdk.jpackage.test.Annotations.Parameters;
+import jdk.jpackage.test.Executor;
+import jdk.jpackage.test.JPackageCommand;
+import jdk.jpackage.test.JavaTool;
+import jdk.jpackage.test.PackageType;
+import jdk.jpackage.test.TKit;
+import org.w3c.dom.Document;
+
+
+/*
+ * @test
+ * @summary jpackage for app's module linked in external runtime
+ * @library ../../../../helpers
+ * @build jdk.jpackage.test.*
+ * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
+ * @compile ModulePathTest3.java
+ * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
+ *  --jpt-run=jdk.jpackage.tests.ModulePathTest3
+ */
+
+/*
+ * @test
+ * @summary jpackage for app's module linked in external runtime
+ * @library ../../../../helpers
+ * @build jdk.jpackage.test.*
+ * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
+ * @compile ModulePathTest3.java
+ * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
+ *  --jpt-run=jdk.jpackage.tests.ModulePathTest3
+ *  --jpt-exclude=test8248418
+ */
+
+public final class ModulePathTest3 {
+
+    public ModulePathTest3(String jlinkOutputSubdir, String runtimeSubdir) {
+        this.jlinkOutputSubdir = Path.of(jlinkOutputSubdir);
+        this.runtimeSubdir = Path.of(runtimeSubdir);
+    }
+
+    /**
+     * Test case for JDK-8248254.
+     * App's module in runtime directory.
+     */
+    @Test
+    public void test8248254() throws XPathExpressionException, IOException {
+        testIt("me.mymodule/me.mymodule.Main");
+    }
+
+    /**
+     * Test case for JDK-8248418.
+     * App's module with version specified in runtime directory.
+     */
+    @Test
+    public void test8248418() throws XPathExpressionException, IOException {
+        testIt("me.mymodule/me.mymodule.Main@3.7");
+    }
+
+    private void testIt(String mainAppDesc) throws XPathExpressionException,
+            IOException {
+        final JavaAppDesc appDesc = JavaAppDesc.parse(mainAppDesc);
+        final Path moduleOutputDir = TKit.createTempDirectory("modules");
+        HelloApp.createBundle(appDesc, moduleOutputDir);
+
+        final Path workDir = TKit.createTempDirectory("runtime").resolve("data");
+        final Path jlinkOutputDir = workDir.resolve(jlinkOutputSubdir);
+        Files.createDirectories(jlinkOutputDir.getParent());
+
+        new Executor()
+        .setToolProvider(JavaTool.JLINK)
+        .dumpOutput()
+        .addArguments(
+                "--add-modules", appDesc.moduleName(),
+                "--output", jlinkOutputDir.toString(),
+                "--module-path", moduleOutputDir.resolve(appDesc.jarFileName()).toString(),
+                "--strip-debug",
+                "--no-header-files",
+                "--no-man-pages",
+                "--strip-native-commands")
+        .execute();
+
+        JPackageCommand cmd = new JPackageCommand()
+        .setDefaultAppName()
+        .setPackageType(PackageType.IMAGE)
+        .setDefaultInputOutput()
+        .removeArgumentWithValue("--input")
+        .addArguments("--module", appDesc.moduleName() + "/" + appDesc.className())
+        .setArgumentValue("--runtime-image", workDir.resolve(runtimeSubdir));
+
+        cmd.executeAndAssertHelloAppImageCreated();
+
+        if (appDesc.moduleVersion() != null) {
+            Document xml = AppImageFile.readXml(cmd.outputBundle());
+            String actualVersion = XPathFactory.newInstance().newXPath().evaluate(
+                    "/jpackage-state/app-version/text()", xml,
+                    XPathConstants.STRING).toString();
+
+            TKit.assertEquals(appDesc.moduleVersion(), actualVersion,
+                    "Check application version");
+        }
+    }
+
+    @Parameters
+    public static Collection data() {
+        final List<String[]> paths = new ArrayList<>();
+        paths.add(new String[] { "", "" });
+        if (TKit.isOSX()) {
+            // On OSX jpackage should accept both runtime root and runtime home
+            // directories.
+            paths.add(new String[] { "Contents/Home", "" });
+        }
+
+        List<Object[]> data = new ArrayList<>();
+        for (var pathCfg : paths) {
+            data.add(new Object[] { pathCfg[0], pathCfg[1] });
+        }
+
+        return data;
+    }
+
+    private final Path jlinkOutputSubdir;
+    private final Path runtimeSubdir;
+}