1 /*
  2  * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #ifndef SHARE_OOPS_STACKCHUNKFRAMESTREAM_INLINE_HPP
 26 #define SHARE_OOPS_STACKCHUNKFRAMESTREAM_INLINE_HPP
 27 
 28 #include "runtime/stackChunkFrameStream.hpp"
 29 
 30 #include "code/codeCache.inline.hpp"
 31 #include "compiler/oopMap.hpp"
 32 #include "interpreter/interpreter.hpp"
 33 #include "logging/log.hpp"
 34 #include "oops/instanceStackChunkKlass.inline.hpp"
 35 #include "oops/method.hpp"
 36 #include "oops/oop.hpp"
 37 #include "oops/stackChunkOop.inline.hpp"
 38 #include "runtime/frame.inline.hpp"
 39 #include "utilities/debug.hpp"
 40 #include "utilities/devirtualizer.inline.hpp"
 41 #include "utilities/globalDefinitions.hpp"
 42 #include "utilities/macros.hpp"
 43 #include CPU_HEADER_INLINE(stackChunkFrameStream)
 44 
 45 #ifdef ASSERT
 46 extern "C" bool dbg_is_safe(const void* p, intptr_t errvalue);
 47 #endif
 48 
 49 template <ChunkFrames frame_kind>
 50 StackChunkFrameStream<frame_kind>::StackChunkFrameStream(stackChunkOop chunk) DEBUG_ONLY(: _chunk(chunk)) {
 51   assert(chunk->is_stackChunk_noinline(), "");
 52   assert(frame_kind == ChunkFrames::Mixed || !chunk->has_mixed_frames(), "");
 53 
 54   DEBUG_ONLY(_index = 0;)
 55   _end = chunk->bottom_address();
 56   _sp = chunk->start_address() + chunk->sp();
 57   assert(_sp <= chunk->end_address() + frame::metadata_words, "");
 58   _callee_augmented = false;
 59 
 60   get_cb();
 61 
 62   if (frame_kind == ChunkFrames::Mixed) {
 63     _unextended_sp = (!is_done() && is_interpreted()) ? unextended_sp_for_interpreter_frame() : _sp;
 64     assert(_unextended_sp >= _sp - frame::metadata_words, "");
 65   } else {
 66     _unextended_sp = _sp;
 67   }
 68 
 69   if (is_stub()) {
 70     get_oopmap(pc(), 0);
 71     DEBUG_ONLY(_has_stub = true);
 72   } DEBUG_ONLY(else _has_stub = false;)
 73 }
 74 
 75 template <ChunkFrames frame_kind>
 76 StackChunkFrameStream<frame_kind>::StackChunkFrameStream(stackChunkOop chunk, const frame& f)
 77   DEBUG_ONLY(: _chunk(chunk)) {
 78   assert(chunk->is_stackChunk_noinline(), "");
 79   assert(frame_kind == ChunkFrames::Mixed || !chunk->has_mixed_frames(), "");
 80   // assert(!is_empty(), ""); -- allowed to be empty
 81 
 82   DEBUG_ONLY(_index = 0;)
 83 
 84   _end = chunk->bottom_address();
 85 
 86   assert(chunk->is_in_chunk(f.sp()), "");
 87   _sp = f.sp();
 88   if (frame_kind == ChunkFrames::Mixed) {
 89     _unextended_sp = f.unextended_sp();
 90     assert(_unextended_sp >= _sp - frame::metadata_words, "");
 91   } else {
 92     _unextended_sp = _sp;
 93   }
 94   assert(_sp >= chunk->start_address(), "");
 95   assert(_sp <= chunk->end_address() + frame::metadata_words, "");
 96   _callee_augmented = false;
 97 
 98   if (f.cb() != nullptr) {
 99     _oopmap = nullptr;
100     _cb = f.cb();
101   } else {
102     get_cb();
103   }
104 
105   if (is_stub()) {
106     get_oopmap(pc(), 0);
107     DEBUG_ONLY(_has_stub = true);
108   } DEBUG_ONLY(else _has_stub = false;)
109 }
110 
111 template <ChunkFrames frame_kind>
112 inline bool StackChunkFrameStream<frame_kind>::is_stub() const {
113   return cb() != nullptr && _cb->is_runtime_stub();
114 }
115 
116 template <ChunkFrames frame_kind>
117 inline bool StackChunkFrameStream<frame_kind>::is_compiled() const {
118   return cb() != nullptr && _cb->is_nmethod();
119 }
120 
121 template <>
122 inline bool StackChunkFrameStream<ChunkFrames::Mixed>::is_interpreted() const {
123   return !is_done() && Interpreter::contains(pc());
124 }
125 
126 template <>
127 inline bool StackChunkFrameStream<ChunkFrames::CompiledOnly>::is_interpreted() const {
128   return false;
129 }
130 
131 // StackChunkFrameStream<frame_kind>::frame_size() returns the words required to
132 // store the given frame as the only frame in a StackChunk. This is the size of the
133 // frame itself plus its stack arguments plus metadata at the caller's frame top (1)
134 //
135 // |====================|          ---
136 // | F0's stackargs     |           ^
137 // |                    |           |
138 // |--------------------|           |
139 // | metadata@top       | <- caller's sp
140 // |====================|           |
141 // | metadata@bottom(2) |           |
142 // |--------------------|
143 // |                    |       size S0
144 // | Frame F0           |                 ---     |====================|          ---
145 // |                    |           |      ^      | F1's stackargs     |           ^
146 // |                    |           |      |      |                    |           |
147 // |--------------------|           |   overlap   |--------------------|           |
148 // | metadata@top(1)    |<- sp      v      v      | metadata@top       | <- caller's sp
149 // |====================|          ---    ---     |====================|           |
150 //                                                | metadata@bottom    |           |
151 //           |                                    |--------------------|
152 //           |                                    | Frame F1           |       size S1
153 //      Stack Growth                              | (F0's callee)      |
154 //           |                                    |                    |           |
155 //           |                                    |                    |           |
156 //           v                                    |--------------------|           |
157 //                                                | metadata@top       |<- sp      v
158 //                                                |====================|          ---
159 //
160 // 2 frames of the same kind (interpreted or compiled) overlap. So the total
161 // size required in the StackChunk is S0 + S1 - overlap, where the overlap is
162 // the size of F1's stackargs plus frame::metadata_words_at_top.
163 //
164 // The callers of frame_size() are supposed to deduct the overlap.  The bottom
165 // frame in the StackChunk obviously does not overlap with it's caller, as it is
166 // in the parent chunk.
167 //
168 // There is no overlap if caller/callee are of different kinds. In that case the
169 // caller is extended to accomodate the callee's stack arguments. The extension
170 // is not counted though in the caller's size, so there is indeed no overlap.
171 //
172 // See ppc implementation of StackChunkFrameStream<frame_kind>::interpreter_frame_size()
173 // for more details.
174 //
175 // (1) Metadata at frame top (see frame::metadata_words_at_top)
176 //     Part of the overlap. Used on ppc64, empty on x86_64, aarch64
177 // (2) Metadata at the frame bottom (see frame::metadata_words_at_bottom)
178 //     Not part of the overlap.
179 //     Used on x86_64 (saved rbp, ret. addr.), aarch64. Empty on ppc64.
180 //
181 template <ChunkFrames frame_kind>
182 inline int StackChunkFrameStream<frame_kind>::frame_size() const {
183   return is_interpreted() ? interpreter_frame_size()
184                           : cb()->frame_size() + stack_argsize() + frame::metadata_words_at_top;
185 }
186 
187 template <ChunkFrames frame_kind>
188 inline int StackChunkFrameStream<frame_kind>::stack_argsize() const {
189   if (is_interpreted()) {
190     return interpreter_frame_stack_argsize();
191   }
192   if (is_stub()) {
193     return 0;
194   }
195   assert(cb() != nullptr, "");
196   assert(cb()->is_nmethod(), "");
197   assert(cb()->as_nmethod()->method() != nullptr, "");
198   return (cb()->as_nmethod()->num_stack_arg_slots() * VMRegImpl::stack_slot_size) >> LogBytesPerWord;
199 }
200 
201 template <ChunkFrames frame_kind>
202 template <typename RegisterMapT>
203 inline int StackChunkFrameStream<frame_kind>::num_oops(RegisterMapT* map) const {
204   if (is_interpreted()) {
205     return interpreter_frame_num_oops(map);
206   } else if (is_compiled()) {
207     return oopmap()->num_oops();
208   } else {
209     assert(is_stub(), "invariant");
210     return 0;
211   }
212 }
213 
214 template <ChunkFrames frame_kind>
215 inline void StackChunkFrameStream<frame_kind>::initialize_register_map(RegisterMap* map) {
216   update_reg_map_pd(map);
217 }
218 
219 template <ChunkFrames frame_kind>
220 template <typename RegisterMapT>
221 inline void StackChunkFrameStream<frame_kind>::next(RegisterMapT* map, bool stop) {
222   update_reg_map(map);
223   bool is_runtime_stub = is_stub();
224   _callee_augmented = false;
225   if (frame_kind == ChunkFrames::Mixed) {
226     if (is_interpreted()) {
227       next_for_interpreter_frame();
228     } else {
229       _sp = _unextended_sp + cb()->frame_size();
230       if (_sp >= _end - frame::metadata_words) {
231         _sp = _end;
232       }
233       if (is_interpreted()) {
234         _unextended_sp = unextended_sp_for_interpreter_frame();
235 #ifndef ZERO
236       } else if (cb()->is_nmethod() && cb()->as_nmethod()->needs_stack_repair()) {
237         _unextended_sp = frame::repair_sender_sp(cb()->as_nmethod(), _unextended_sp, (intptr_t**)(_sp - frame::sender_sp_offset));
238         _callee_augmented = _unextended_sp != _sp;
239 #endif
240       } else {
241         _unextended_sp = _sp;
242       }
243     }
244     assert(_unextended_sp >= _sp - frame::metadata_words, "");
245   } else {
246     _sp = _unextended_sp + cb()->frame_size();
247 #ifndef ZERO
248     if (cb()->is_nmethod() && cb()->as_nmethod()->needs_stack_repair()) {
249       _unextended_sp = frame::repair_sender_sp(cb()->as_nmethod(), _unextended_sp, (intptr_t**)(_sp - frame::sender_sp_offset));
250       _callee_augmented = _unextended_sp != _sp;
251     } else
252 #endif
253     {
254       _unextended_sp = _sp;
255     }
256   }
257   assert(!is_interpreted() || _unextended_sp == unextended_sp_for_interpreter_frame(), "");
258 
259   DEBUG_ONLY(_index++;)
260   if (stop) {
261     return;
262   }
263 
264   get_cb();
265   update_reg_map_pd(map);
266   if (is_runtime_stub && cb() != nullptr) { // there's no post-call nop and no fast oopmap lookup
267     // caller could have been deoptimized so use orig_pc()
268     _oopmap = cb()->oop_map_for_return_address(orig_pc());
269   }
270 }
271 
272 template <ChunkFrames frame_kind>
273 inline void StackChunkFrameStream<frame_kind>::get_cb() {
274   _oopmap = nullptr;
275   if (is_done() || is_interpreted()) {
276     _cb = nullptr;
277     return;
278   }
279 
280   assert(pc() != nullptr, "");
281   assert(dbg_is_safe(pc(), -1), "");
282 
283   _cb = CodeCache::find_blob_fast(pc());
284 
285   assert(_cb != nullptr, "");
286   assert(is_interpreted() || ((is_stub() || is_compiled()) && _cb->frame_size() > 0), "");
287 }
288 
289 template <ChunkFrames frame_kind>
290 inline void StackChunkFrameStream<frame_kind>::get_oopmap() const {
291   if (is_interpreted()) {
292     return;
293   }
294   assert(is_compiled(), "");
295   get_oopmap(pc(), CodeCache::find_oopmap_slot_fast(pc()));
296 }
297 
298 template <ChunkFrames frame_kind>
299 inline void StackChunkFrameStream<frame_kind>::get_oopmap(address pc, int oopmap_slot) const {
300   assert(cb() != nullptr, "");
301   assert(!is_compiled() || !cb()->as_nmethod()->is_deopt_pc(pc), "");
302   if (oopmap_slot >= 0) {
303     assert(oopmap_slot >= 0, "");
304     assert(cb()->oop_map_for_slot(oopmap_slot, pc) != nullptr, "");
305     assert(cb()->oop_map_for_slot(oopmap_slot, pc) == cb()->oop_map_for_return_address(pc), "");
306 
307     _oopmap = cb()->oop_map_for_slot(oopmap_slot, pc);
308   } else {
309     _oopmap = cb()->oop_map_for_return_address(pc);
310   }
311   assert(_oopmap != nullptr, "");
312 }
313 
314 template <ChunkFrames frame_kind>
315 template <typename RegisterMapT>
316 inline void* StackChunkFrameStream<frame_kind>::reg_to_loc(VMReg reg, const RegisterMapT* map) const {
317   assert(!is_done(), "");
318   return reg->is_reg() ? (void*)map->location(reg, sp()) // see frame::update_map_with_saved_link(&map, link_addr);
319                        : (void*)((address)unextended_sp() + (reg->reg2stack() * VMRegImpl::stack_slot_size));
320 }
321 
322 template<>
323 template<>
324 inline void StackChunkFrameStream<ChunkFrames::Mixed>::update_reg_map(RegisterMap* map) {
325   assert(!map->in_cont() || map->stack_chunk() == _chunk, "");
326   if (map->update_map() && is_stub()) {
327     frame f = to_frame();
328     oopmap()->update_register_map(&f, map); // we have callee-save registers in this case
329   }
330 }
331 
332 template<>
333 template<>
334 inline void StackChunkFrameStream<ChunkFrames::CompiledOnly>::update_reg_map(RegisterMap* map) {
335   assert(!map->in_cont() || map->stack_chunk() == _chunk, "");
336   if (map->update_map() && is_stub()) {
337     frame f = to_frame();
338     oopmap()->update_register_map(&f, map); // we have callee-save registers in this case
339   }
340 }
341 
342 template <ChunkFrames frame_kind>
343 template <typename RegisterMapT>
344 inline void StackChunkFrameStream<frame_kind>::update_reg_map(RegisterMapT* map) {}
345 
346 template <ChunkFrames frame_kind>
347 inline address StackChunkFrameStream<frame_kind>::orig_pc() const {
348   address pc1 = pc();
349   if (is_interpreted() || is_stub()) {
350     return pc1;
351   }
352   nmethod* nm = cb()->as_nmethod();
353   if (nm->is_deopt_pc(pc1)) {
354     pc1 = *(address*)((address)unextended_sp() + nm->orig_pc_offset());
355   }
356 
357   assert(pc1 != nullptr, "");
358   assert(!nm->is_deopt_pc(pc1), "");
359   assert(_cb == CodeCache::find_blob_fast(pc1), "");
360 
361   return pc1;
362 }
363 
364 template<ChunkFrames frame_kind>
365 void StackChunkFrameStream<frame_kind>::handle_deopted() const {
366   assert(!is_done(), "");
367 
368   if (_oopmap != nullptr) {
369     return;
370   }
371   if (is_interpreted()) {
372     return;
373   }
374   assert(is_compiled(), "");
375 
376   address pc1 = pc();
377   int oopmap_slot = CodeCache::find_oopmap_slot_fast(pc1);
378   if (oopmap_slot < 0) { // UNLIKELY; we could have marked frames for deoptimization in thaw_chunk
379     if (cb()->as_nmethod()->is_deopt_pc(pc1)) {
380       pc1 = orig_pc();
381       oopmap_slot = CodeCache::find_oopmap_slot_fast(pc1);
382     }
383   }
384   get_oopmap(pc1, oopmap_slot);
385 }
386 
387 template <ChunkFrames frame_kind>
388 template <class OopClosureType, class RegisterMapT>
389 inline void StackChunkFrameStream<frame_kind>::iterate_oops(OopClosureType* closure, const RegisterMapT* map) const {
390   if (is_interpreted()) {
391     frame f = to_frame();
392     f.oops_interpreted_do(closure, map, true);
393   } else {
394     DEBUG_ONLY(int oops = 0;)
395     for (OopMapStream oms(oopmap()); !oms.is_done(); oms.next()) {
396       OopMapValue omv = oms.current();
397       if (omv.type() != OopMapValue::oop_value && omv.type() != OopMapValue::narrowoop_value) {
398         continue;
399       }
400 
401       assert(UseCompressedOops || omv.type() == OopMapValue::oop_value, "");
402       DEBUG_ONLY(oops++;)
403 
404       void* p = reg_to_loc(omv.reg(), map);
405       assert(p != nullptr, "");
406       assert((_has_stub && _index == 1) || is_in_frame(p), "");
407 
408       log_develop_trace(continuations)("StackChunkFrameStream::iterate_oops narrow: %d reg: %s p: " INTPTR_FORMAT " sp offset: " INTPTR_FORMAT,
409           omv.type() == OopMapValue::narrowoop_value, omv.reg()->name(), p2i(p), (intptr_t*)p - sp());
410           omv.type() == OopMapValue::narrowoop_value ? Devirtualizer::do_oop(closure, (narrowOop*)p) : Devirtualizer::do_oop(closure, (oop*)p);
411     }
412     assert(oops == oopmap()->num_oops(), "oops: %d oopmap->num_oops(): %d", oops, oopmap()->num_oops());
413   }
414 }
415 
416 template <ChunkFrames frame_kind>
417 template <class DerivedOopClosureType, class RegisterMapT>
418 inline void StackChunkFrameStream<frame_kind>::iterate_derived_pointers(DerivedOopClosureType* closure, const RegisterMapT* map) const {
419   if (!is_compiled()) {
420     // Only compiled frames have derived pointers
421     return;
422   }
423 
424   assert(oopmap()->has_derived_oops() == oopmap()->has_any(OopMapValue::derived_oop_value), "");
425   if (!oopmap()->has_derived_oops()) {
426     return;
427   }
428 
429   for (OopMapStream oms(oopmap()); !oms.is_done(); oms.next()) {
430     OopMapValue omv = oms.current();
431     if (omv.type() != OopMapValue::derived_oop_value) {
432       continue;
433     }
434 
435     // see OopMapDo<OopMapFnT, DerivedOopFnT, ValueFilterT>::walk_derived_pointers1
436     intptr_t* derived_loc = (intptr_t*)reg_to_loc(omv.reg(), map);
437     intptr_t* base_loc    = (intptr_t*)reg_to_loc(omv.content_reg(), map);
438 
439     assert((_has_stub && _index == 1) || is_in_frame(base_loc), "");
440     assert((_has_stub && _index == 1) || is_in_frame(derived_loc), "");
441     assert(derived_loc != base_loc, "Base and derived in same location");
442     assert(is_in_oops(base_loc, map), "not found: " INTPTR_FORMAT, p2i(base_loc));
443     assert(!is_in_oops(derived_loc, map), "found: " INTPTR_FORMAT, p2i(derived_loc));
444 
445     Devirtualizer::do_derived_oop(closure, (derived_base*)base_loc, (derived_pointer*)derived_loc);
446   }
447 }
448 
449 #ifdef ASSERT
450 
451 template <ChunkFrames frame_kind>
452 template <typename RegisterMapT>
453 bool StackChunkFrameStream<frame_kind>::is_in_oops(void* p, const RegisterMapT* map) const {
454   for (OopMapStream oms(oopmap()); !oms.is_done(); oms.next()) {
455     if (oms.current().type() != OopMapValue::oop_value) {
456       continue;
457     }
458     if (reg_to_loc(oms.current().reg(), map) == p) {
459       return true;
460     }
461   }
462   return false;
463 }
464 
465 template <ChunkFrames frame_kind>
466 void StackChunkFrameStream<frame_kind>::assert_is_interpreted_and_frame_type_mixed() const {
467   assert(is_interpreted(), "");
468   assert(frame_kind == ChunkFrames::Mixed, "");
469 }
470 
471 #endif
472 
473 #endif // SHARE_OOPS_STACKCHUNKFRAMESTREAM_INLINE_HPP