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