< prev index next >

src/hotspot/share/runtime/continuationFreezeThaw.cpp

Print this page
@@ -455,13 +455,13 @@
    inline bool stack_overflow();
  
    static frame sender(const frame& f) { return f.is_interpreted_frame() ? sender<ContinuationHelper::InterpretedFrame>(f)
                                                                          : sender<ContinuationHelper::NonInterpretedUnknownFrame>(f); }
    template<typename FKind> static inline frame sender(const frame& f);
-   template<typename FKind> frame new_heap_frame(frame& f, frame& caller);
+   template<typename FKind> frame new_heap_frame(frame& f, frame& caller, int size_adjust = 0);
    inline void set_top_frame_metadata_pd(const frame& hf);
-   inline void patch_pd(frame& callee, const frame& caller);
+   inline void patch_pd(frame& callee, const frame& caller, bool is_bottom_frame);
    void adjust_interpreted_frame_unextended_sp(frame& f);
    static inline void prepare_freeze_interpreted_top_frame(frame& f);
    static inline void relativize_interpreted_frame_metadata(const frame& f, const frame& hf);
  
  protected:

@@ -1157,11 +1157,11 @@
      ContinuationHelper::Frame::patch_pc(caller, last_pc);
    } else {
      assert(!caller.is_empty(), "");
    }
  
-   patch_pd(hf, caller);
+   patch_pd(hf, caller, is_bottom_frame);
  
    if (f.is_interpreted_frame()) {
      assert(hf.is_heap_frame(), "should be");
      ContinuationHelper::InterpretedFrame::patch_sender_sp(hf, caller);
    }

@@ -1254,31 +1254,54 @@
                                                          bool callee_interpreted) {
    // The frame's top never includes the stack arguments to the callee
    intptr_t* const stack_frame_top = ContinuationHelper::CompiledFrame::frame_top(f, callee_argsize, callee_interpreted);
    intptr_t* const stack_frame_bottom = ContinuationHelper::CompiledFrame::frame_bottom(f);
    // including metadata between f and its stackargs
-   const int argsize = ContinuationHelper::CompiledFrame::stack_argsize(f) + frame::metadata_words_at_top;
-   const int fsize = pointer_delta_as_int(stack_frame_bottom + argsize, stack_frame_top);
+   int argsize = ContinuationHelper::CompiledFrame::stack_argsize(f) + frame::metadata_words_at_top;
+   int fsize = pointer_delta_as_int(stack_frame_bottom + argsize, stack_frame_top);
+ 
+   int real_frame_size = 0;
+   bool augmented = f.was_augmented_on_entry(real_frame_size);
+   if (augmented) {
+     // The args reside inside the frame so clear argsize. If the caller is compiled,
+     // this will cause the stack arguments passed by the caller to be freezed when
+     // freezing the caller frame itself. If the caller is interpreted this will have
+     // the effect of discarding the arg area created in the i2c stub.
+     argsize = 0;
+     fsize = real_frame_size - (callee_interpreted ? 0 : callee_argsize);
+ #ifdef ASSERT
+     nmethod* nm = f.cb()->as_nmethod();
+     Method* method = nm->method();
+     address return_pc = ContinuationHelper::CompiledFrame::return_pc(f);
+     CodeBlob* caller_cb = CodeCache::find_blob_fast(return_pc);
+     assert(nm->is_compiled_by_c2() || (caller_cb->is_nmethod() && caller_cb->as_nmethod()->is_compiled_by_c2()), "caller or callee should be c2 compiled");
+     assert((!caller_cb->is_nmethod() && nm->is_compiled_by_c2()) ||
+            (nm->compiler_type() != caller_cb->as_nmethod()->compiler_type()) ||
+            (nm->is_compiled_by_c2() && !method->is_static() && method->method_holder()->is_inline_klass()),
+            "frame should not be extended");
+ #endif
+   }
  
-   log_develop_trace(continuations)("recurse_freeze_compiled_frame %s _size: %d fsize: %d argsize: %d",
+   log_develop_trace(continuations)("recurse_freeze_compiled_frame %s _size: %d fsize: %d argsize: %d augmented: %d",
                               ContinuationHelper::Frame::frame_method(f) != nullptr ?
                               ContinuationHelper::Frame::frame_method(f)->name_and_sig_as_C_string() : "",
-                              _freeze_size, fsize, argsize);
+                              _freeze_size, fsize, argsize, augmented);
    // we'd rather not yield inside methods annotated with @JvmtiMountTransition
    assert(!ContinuationHelper::Frame::frame_method(f)->jvmti_mount_transition(), "");
  
    freeze_result result = recurse_freeze_java_frame<ContinuationHelper::CompiledFrame>(f, caller, fsize, argsize);
    if (UNLIKELY(result > freeze_ok_bottom)) {
      return result;
    }
  
    bool is_bottom_frame = result == freeze_ok_bottom;
    assert(!caller.is_empty() || is_bottom_frame, "");
+   assert(!is_bottom_frame || !augmented, "thaw extended frame without caller?");
  
    DEBUG_ONLY(before_freeze_java_frame(f, caller, fsize, argsize, is_bottom_frame);)
  
-   frame hf = new_heap_frame<ContinuationHelper::CompiledFrame>(f, caller);
+   frame hf = new_heap_frame<ContinuationHelper::CompiledFrame>(f, caller, augmented ? real_frame_size - f.cb()->as_nmethod()->frame_size() : 0);
  
    intptr_t* heap_frame_top = ContinuationHelper::CompiledFrame::frame_top(hf, callee_argsize, callee_interpreted);
  
    copy_to_chunk(stack_frame_top, heap_frame_top, fsize);
    assert(!is_bottom_frame || !caller.is_compiled_frame() || (heap_frame_top + fsize) == (caller.unextended_sp() + argsize), "");

@@ -1936,10 +1959,11 @@
    }
  
    void clear_chunk(stackChunkOop chunk);
    template<bool check_stub>
    int remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &argsize);
+   int remove_scalarized_frames(StackChunkFrameStream<ChunkFrames::CompiledOnly>& scfs, stackChunkOop chunk, int &argsize);
    void copy_from_chunk(intptr_t* from, intptr_t* to, int size);
  
    void thaw_lockstack(stackChunkOop chunk);
  
    // fast path

@@ -1959,21 +1983,21 @@
  
    inline bool seen_by_gc();
  
    inline void before_thaw_java_frame(const frame& hf, const frame& caller, bool bottom, int num_frame);
    inline void after_thaw_java_frame(const frame& f, bool bottom);
-   inline void patch(frame& f, const frame& caller, bool bottom);
+   inline void patch(frame& f, const frame& caller, bool bottom, bool augmented = false);
    void clear_bitmap_bits(address start, address end);
  
    NOINLINE void recurse_thaw_interpreted_frame(const frame& hf, frame& caller, int num_frames);
    void recurse_thaw_compiled_frame(const frame& hf, frame& caller, int num_frames, bool stub_caller);
    void recurse_thaw_stub_frame(const frame& hf, frame& caller, int num_frames);
    void recurse_thaw_native_frame(const frame& hf, frame& caller, int num_frames);
  
    void push_return_frame(frame& f);
    inline frame new_entry_frame();
-   template<typename FKind> frame new_stack_frame(const frame& hf, frame& caller, bool bottom);
+   template<typename FKind> frame new_stack_frame(const frame& hf, frame& caller, bool bottom, int size_adjust = 0);
    inline void patch_pd(frame& f, const frame& sender);
    inline void patch_pd(frame& f, intptr_t* caller_sp);
    inline intptr_t* align(const frame& hf, intptr_t* frame_sp, frame& caller, bool bottom);
  
    void maybe_set_fastpath(intptr_t* sp) { if (sp > _fastpath) _fastpath = sp; }

@@ -2046,18 +2070,63 @@
  inline void ThawBase::clear_chunk(stackChunkOop chunk) {
    chunk->set_sp(chunk->bottom());
    chunk->set_max_thawing_size(0);
  }
  
+ int ThawBase::remove_scalarized_frames(StackChunkFrameStream<ChunkFrames::CompiledOnly>& f, stackChunkOop chunk, int &argsize) {
+   DEBUG_ONLY(intptr_t* const chunk_sp = chunk->start_address() + chunk->sp();)
+   intptr_t* top = f.sp();
+ 
+   while (f.cb()->as_nmethod_or_null()->needs_stack_repair()) {
+     f.next(SmallRegisterMap::instance(), false /* stop */);
+   }
+   assert(!f.is_done(), "");
+   assert(f.is_compiled(), "");
+ 
+   intptr_t* bottom = f.sp() + f.cb()->frame_size();
+   argsize = f.stack_argsize();
+   int frames_size = bottom - top;
+ 
+   f.next(SmallRegisterMap::instance(), true /* stop */);
+   bool empty = f.is_done();
+   assert(!empty || argsize == chunk->argsize(), "");
+ 
+   if (empty) {
+     clear_chunk(chunk);
+   } else {
+     chunk->set_sp(chunk->sp() + frames_size);
+     chunk->set_max_thawing_size(chunk->max_thawing_size() - frames_size);
+     // We set chunk->pc to the return pc into the next frame
+     chunk->set_pc(f.pc());
+ #ifdef ASSERT
+     {
+       intptr_t* retaddr_slot = (chunk_sp
+                                 + frames_size
+                                 - frame::sender_sp_ret_address_offset());
+       assert(f.pc() == ContinuationHelper::return_address_at(retaddr_slot),
+              "unexpected pc");
+     }
+ #endif
+   }
+   assert(empty == chunk->is_empty(), "");
+   // returns the size required to store the frame on stack, and because it is a
+   // compiled frame, it must include a copy of the arguments passed by the caller
+   return frames_size + argsize + frame::metadata_words_at_top;
+ }
+ 
  template<bool check_stub>
  int ThawBase::remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &argsize) {
    bool empty = false;
    StackChunkFrameStream<ChunkFrames::CompiledOnly> f(chunk);
    DEBUG_ONLY(intptr_t* const chunk_sp = chunk->start_address() + chunk->sp();)
    assert(chunk_sp == f.sp(), "");
    assert(chunk_sp == f.unextended_sp(), "");
  
+   if (f.cb()->as_nmethod_or_null()->needs_stack_repair()) {
+     return remove_scalarized_frames(f, chunk, argsize);
+   }
+ 
    int frame_size = f.cb()->frame_size();
    argsize = f.stack_argsize();
  
    assert(!f.is_stub() || check_stub, "");
    if (check_stub && f.is_stub()) {

@@ -2355,17 +2424,23 @@
    assert(num_frames > 0, "");
  
    DEBUG_ONLY(_frames++;)
  
    int argsize = _stream.stack_argsize();
+   CodeBlob* cb = _stream.cb();
  
    _stream.next(SmallRegisterMap::instance());
    assert(_stream.to_frame().is_empty() == _stream.is_done(), "");
  
-   // we never leave a compiled caller of an interpreted frame as the top frame in the chunk
-   // as it makes detecting that situation and adjusting unextended_sp tricky
-   if (num_frames == 1 && !_stream.is_done() && FKind::interpreted && _stream.is_compiled()) {
+   // We never leave a compiled caller of an interpreted frame as the top frame in the chunk
+   // as it makes detecting that situation and adjusting unextended_sp tricky. We also always
+   // thaw the caller of a frame that needs_stack_repair, as it would otherwise complicate things:
+   // - Regardless of whether the frame was extended or not, we would need to copy the right arg
+   //   size if its greater than the one given by the normal method signature (non-scalarized).
+   // - If the frame was indeed extended, leaving its caller as the top frame would complicate walking
+   //   the chunk (we need unextended_sp, but we only have sp).
+   if (num_frames == 1 && !_stream.is_done() && ((FKind::interpreted && _stream.is_compiled()) || (FKind::compiled && cb->as_nmethod_or_null()->needs_stack_repair()))) {
      log_develop_trace(continuations)("thawing extra compiled frame to not leave a compiled interpreted-caller at top");
      num_frames++;
    }
  
    if (num_frames == 1 || _stream.is_done()) { // end recursion

@@ -2421,19 +2496,19 @@
      print_frame_layout(f, false, &ls); // f.print_on(&ls);
    }
  #endif
  }
  
- inline void ThawBase::patch(frame& f, const frame& caller, bool bottom) {
+ inline void ThawBase::patch(frame& f, const frame& caller, bool bottom, bool augmented) {
    assert(!bottom || caller.fp() == _cont.entryFP(), "");
    if (bottom) {
      ContinuationHelper::Frame::patch_pc(caller, _cont.is_empty() ? caller.pc()
                                                                   : StubRoutines::cont_returnBarrier());
-   } else {
+   } else if (caller.is_compiled_frame()){
      // caller might have been deoptimized during thaw but we've overwritten the return address when copying f from the heap.
      // If the caller is not deoptimized, pc is unchanged.
-     ContinuationHelper::Frame::patch_pc(caller, caller.raw_pc());
+     ContinuationHelper::Frame::patch_pc(caller, caller.raw_pc(), augmented /*callee_augmented*/);
    }
  
    patch_pd(f, caller);
  
    if (f.is_interpreted_frame()) {

@@ -2592,21 +2667,28 @@
  
    if ((!is_bottom_frame && caller.is_interpreted_frame()) || (is_bottom_frame && Interpreter::contains(_cont.tail()->pc()))) {
      _align_size += frame::align_wiggle; // we add one whether or not we've aligned because we add it in recurse_freeze_compiled_frame
    }
  
+   int fsize = 0;
+   int added_argsize = 0;
+   bool augmented = hf.was_augmented_on_entry(fsize);
+   if (!augmented) {
+     added_argsize = (is_bottom_frame || caller.is_interpreted_frame()) ? hf.compiled_frame_stack_argsize() : 0;
+     fsize += added_argsize;
+   }
+   assert(!is_bottom_frame || !augmented, "");
+ 
+ 
    // new_stack_frame must construct the resulting frame using hf.pc() rather than hf.raw_pc() because the frame is not
    // yet laid out in the stack, and so the original_pc is not stored in it.
    // As a result, f.is_deoptimized_frame() is always false and we must test hf to know if the frame is deoptimized.
-   frame f = new_stack_frame<ContinuationHelper::CompiledFrame>(hf, caller, is_bottom_frame);
+   frame f = new_stack_frame<ContinuationHelper::CompiledFrame>(hf, caller, is_bottom_frame, augmented ? fsize - hf.cb()->frame_size() : 0);
+   assert(f.cb()->frame_size() == (int)(caller.sp() - f.sp()), "");
+ 
    intptr_t* const stack_frame_top = f.sp();
    intptr_t* const heap_frame_top = hf.unextended_sp();
- 
-   const int added_argsize = (is_bottom_frame || caller.is_interpreted_frame()) ? hf.compiled_frame_stack_argsize() : 0;
-   int fsize = ContinuationHelper::CompiledFrame::size(hf) + added_argsize;
-   assert(fsize <= (int)(caller.unextended_sp() - f.unextended_sp()), "");
- 
    intptr_t* from = heap_frame_top - frame::metadata_words_at_bottom;
    intptr_t* to   = stack_frame_top - frame::metadata_words_at_bottom;
    // copy metadata, except the metadata at the top of the (unextended) entry frame
    int sz = fsize + frame::metadata_words_at_bottom + (is_bottom_frame && added_argsize == 0 ? 0 : frame::metadata_words_at_top);
  

@@ -2615,11 +2697,11 @@
    assert(!is_bottom_frame || (_cont.entrySP() - 1 <= to + sz && to + sz <= _cont.entrySP()), "");
    assert(!is_bottom_frame || hf.compiled_frame_stack_argsize() != 0 || (to + sz && to + sz == _cont.entrySP()), "");
  
    copy_from_chunk(from, to, sz); // copying good oops because we invoked barriers above
  
-   patch(f, caller, is_bottom_frame);
+   patch(f, caller, is_bottom_frame, augmented);
  
    // f.is_deoptimized_frame() is always false and we must test hf.is_deoptimized_frame() (see comment above)
    assert(!f.is_deoptimized_frame(), "");
    if (hf.is_deoptimized_frame()) {
      maybe_set_fastpath(f.sp());
< prev index next >