1 /* 2 * Copyright (c) 2019, 2024, 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/method.hpp" 35 #include "oops/oop.hpp" 36 #include "oops/stackChunkOop.inline.hpp" 37 #include "oops/instanceStackChunkKlass.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 inline int StackChunkFrameStream<frame_kind>::num_oops() const { 203 if (is_interpreted()) { 204 return interpreter_frame_num_oops(); 205 } else if (is_compiled()) { 206 return oopmap()->num_oops(); 207 } else { 208 assert(is_stub(), "invariant"); 209 return 0; 210 } 211 } 212 213 template <ChunkFrames frame_kind> 214 inline void StackChunkFrameStream<frame_kind>::initialize_register_map(RegisterMap* map) { 215 update_reg_map_pd(map); 216 } 217 218 template <ChunkFrames frame_kind> 219 template <typename RegisterMapT> 220 inline void StackChunkFrameStream<frame_kind>::next(RegisterMapT* map, bool stop) { 221 update_reg_map(map); 222 bool is_runtime_stub = is_stub(); 223 _callee_augmented = false; 224 if (frame_kind == ChunkFrames::Mixed) { 225 if (is_interpreted()) { 226 next_for_interpreter_frame(); 227 } else { 228 _sp = _unextended_sp + cb()->frame_size(); 229 if (_sp >= _end - frame::metadata_words) { 230 _sp = _end; 231 } 232 if (is_interpreted()) { 233 _unextended_sp = unextended_sp_for_interpreter_frame(); 234 } else if (cb()->is_nmethod() && cb()->as_nmethod()->needs_stack_repair()) { 235 _unextended_sp = frame::repair_sender_sp(cb()->as_nmethod(), _unextended_sp, (intptr_t**)(_sp - frame::sender_sp_offset)); 236 _callee_augmented = _unextended_sp != _sp; 237 } else { 238 _unextended_sp = _sp; 239 } 240 } 241 assert(_unextended_sp >= _sp - frame::metadata_words, ""); 242 } else { 243 _sp = _unextended_sp + cb()->frame_size(); 244 if (cb()->is_nmethod() && cb()->as_nmethod()->needs_stack_repair()) { 245 _unextended_sp = frame::repair_sender_sp(cb()->as_nmethod(), _unextended_sp, (intptr_t**)(_sp - frame::sender_sp_offset)); 246 _callee_augmented = _unextended_sp != _sp; 247 } else { 248 _unextended_sp = _sp; 249 } 250 } 251 assert(!is_interpreted() || _unextended_sp == unextended_sp_for_interpreter_frame(), ""); 252 253 DEBUG_ONLY(_index++;) 254 if (stop) { 255 return; 256 } 257 258 get_cb(); 259 update_reg_map_pd(map); 260 if (is_runtime_stub && cb() != nullptr) { // there's no post-call nop and no fast oopmap lookup 261 // caller could have been deoptimized so use orig_pc() 262 _oopmap = cb()->oop_map_for_return_address(orig_pc()); 263 } 264 } 265 266 template <ChunkFrames frame_kind> 267 inline void StackChunkFrameStream<frame_kind>::get_cb() { 268 _oopmap = nullptr; 269 if (is_done() || is_interpreted()) { 270 _cb = nullptr; 271 return; 272 } 273 274 assert(pc() != nullptr, ""); 275 assert(dbg_is_safe(pc(), -1), ""); 276 277 _cb = CodeCache::find_blob_fast(pc()); 278 279 assert(_cb != nullptr, ""); 280 assert(is_interpreted() || ((is_stub() || is_compiled()) && _cb->frame_size() > 0), ""); 281 } 282 283 template <ChunkFrames frame_kind> 284 inline void StackChunkFrameStream<frame_kind>::get_oopmap() const { 285 if (is_interpreted()) { 286 return; 287 } 288 assert(is_compiled(), ""); 289 get_oopmap(pc(), CodeCache::find_oopmap_slot_fast(pc())); 290 } 291 292 template <ChunkFrames frame_kind> 293 inline void StackChunkFrameStream<frame_kind>::get_oopmap(address pc, int oopmap_slot) const { 294 assert(cb() != nullptr, ""); 295 assert(!is_compiled() || !cb()->as_nmethod()->is_deopt_pc(pc), ""); 296 if (oopmap_slot >= 0) { 297 assert(oopmap_slot >= 0, ""); 298 assert(cb()->oop_map_for_slot(oopmap_slot, pc) != nullptr, ""); 299 assert(cb()->oop_map_for_slot(oopmap_slot, pc) == cb()->oop_map_for_return_address(pc), ""); 300 301 _oopmap = cb()->oop_map_for_slot(oopmap_slot, pc); 302 } else { 303 _oopmap = cb()->oop_map_for_return_address(pc); 304 } 305 assert(_oopmap != nullptr, ""); 306 } 307 308 template <ChunkFrames frame_kind> 309 template <typename RegisterMapT> 310 inline void* StackChunkFrameStream<frame_kind>::reg_to_loc(VMReg reg, const RegisterMapT* map) const { 311 assert(!is_done(), ""); 312 return reg->is_reg() ? (void*)map->location(reg, sp()) // see frame::update_map_with_saved_link(&map, link_addr); 313 : (void*)((address)unextended_sp() + (reg->reg2stack() * VMRegImpl::stack_slot_size)); 314 } 315 316 template<> 317 template<> 318 inline void StackChunkFrameStream<ChunkFrames::Mixed>::update_reg_map(RegisterMap* map) { 319 assert(!map->in_cont() || map->stack_chunk() == _chunk, ""); 320 if (map->update_map() && is_stub()) { 321 frame f = to_frame(); 322 oopmap()->update_register_map(&f, map); // we have callee-save registers in this case 323 } 324 } 325 326 template<> 327 template<> 328 inline void StackChunkFrameStream<ChunkFrames::CompiledOnly>::update_reg_map(RegisterMap* map) { 329 assert(!map->in_cont() || map->stack_chunk() == _chunk, ""); 330 if (map->update_map() && is_stub()) { 331 frame f = to_frame(); 332 oopmap()->update_register_map(&f, map); // we have callee-save registers in this case 333 } 334 } 335 336 template <ChunkFrames frame_kind> 337 template <typename RegisterMapT> 338 inline void StackChunkFrameStream<frame_kind>::update_reg_map(RegisterMapT* map) {} 339 340 template <ChunkFrames frame_kind> 341 inline address StackChunkFrameStream<frame_kind>::orig_pc() const { 342 address pc1 = pc(); 343 if (is_interpreted() || is_stub()) { 344 return pc1; 345 } 346 nmethod* nm = cb()->as_nmethod(); 347 if (nm->is_deopt_pc(pc1)) { 348 pc1 = *(address*)((address)unextended_sp() + nm->orig_pc_offset()); 349 } 350 351 assert(pc1 != nullptr, ""); 352 assert(!nm->is_deopt_pc(pc1), ""); 353 assert(_cb == CodeCache::find_blob_fast(pc1), ""); 354 355 return pc1; 356 } 357 358 template<ChunkFrames frame_kind> 359 void StackChunkFrameStream<frame_kind>::handle_deopted() const { 360 assert(!is_done(), ""); 361 362 if (_oopmap != nullptr) { 363 return; 364 } 365 if (is_interpreted()) { 366 return; 367 } 368 assert(is_compiled(), ""); 369 370 address pc1 = pc(); 371 int oopmap_slot = CodeCache::find_oopmap_slot_fast(pc1); 372 if (oopmap_slot < 0) { // UNLIKELY; we could have marked frames for deoptimization in thaw_chunk 373 if (cb()->as_nmethod()->is_deopt_pc(pc1)) { 374 pc1 = orig_pc(); 375 oopmap_slot = CodeCache::find_oopmap_slot_fast(pc1); 376 } 377 } 378 get_oopmap(pc1, oopmap_slot); 379 } 380 381 template <ChunkFrames frame_kind> 382 template <class OopClosureType, class RegisterMapT> 383 inline void StackChunkFrameStream<frame_kind>::iterate_oops(OopClosureType* closure, const RegisterMapT* map) const { 384 if (is_interpreted()) { 385 frame f = to_frame(); 386 f.oops_interpreted_do(closure, nullptr, true); 387 } else { 388 DEBUG_ONLY(int oops = 0;) 389 for (OopMapStream oms(oopmap()); !oms.is_done(); oms.next()) { 390 OopMapValue omv = oms.current(); 391 if (omv.type() != OopMapValue::oop_value && omv.type() != OopMapValue::narrowoop_value) { 392 continue; 393 } 394 395 assert(UseCompressedOops || omv.type() == OopMapValue::oop_value, ""); 396 DEBUG_ONLY(oops++;) 397 398 void* p = reg_to_loc(omv.reg(), map); 399 assert(p != nullptr, ""); 400 assert((_has_stub && _index == 1) || is_in_frame(p), ""); 401 402 log_develop_trace(continuations)("StackChunkFrameStream::iterate_oops narrow: %d reg: %s p: " INTPTR_FORMAT " sp offset: " INTPTR_FORMAT, 403 omv.type() == OopMapValue::narrowoop_value, omv.reg()->name(), p2i(p), (intptr_t*)p - sp()); 404 omv.type() == OopMapValue::narrowoop_value ? Devirtualizer::do_oop(closure, (narrowOop*)p) : Devirtualizer::do_oop(closure, (oop*)p); 405 } 406 assert(oops == oopmap()->num_oops(), "oops: %d oopmap->num_oops(): %d", oops, oopmap()->num_oops()); 407 } 408 } 409 410 template <ChunkFrames frame_kind> 411 template <class DerivedOopClosureType, class RegisterMapT> 412 inline void StackChunkFrameStream<frame_kind>::iterate_derived_pointers(DerivedOopClosureType* closure, const RegisterMapT* map) const { 413 if (!is_compiled()) { 414 // Only compiled frames have derived pointers 415 return; 416 } 417 418 assert(oopmap()->has_derived_oops() == oopmap()->has_any(OopMapValue::derived_oop_value), ""); 419 if (!oopmap()->has_derived_oops()) { 420 return; 421 } 422 423 for (OopMapStream oms(oopmap()); !oms.is_done(); oms.next()) { 424 OopMapValue omv = oms.current(); 425 if (omv.type() != OopMapValue::derived_oop_value) { 426 continue; 427 } 428 429 // see OopMapDo<OopMapFnT, DerivedOopFnT, ValueFilterT>::walk_derived_pointers1 430 intptr_t* derived_loc = (intptr_t*)reg_to_loc(omv.reg(), map); 431 intptr_t* base_loc = (intptr_t*)reg_to_loc(omv.content_reg(), map); 432 433 assert((_has_stub && _index == 1) || is_in_frame(base_loc), ""); 434 assert((_has_stub && _index == 1) || is_in_frame(derived_loc), ""); 435 assert(derived_loc != base_loc, "Base and derived in same location"); 436 assert(is_in_oops(base_loc, map), "not found: " INTPTR_FORMAT, p2i(base_loc)); 437 assert(!is_in_oops(derived_loc, map), "found: " INTPTR_FORMAT, p2i(derived_loc)); 438 439 Devirtualizer::do_derived_oop(closure, (derived_base*)base_loc, (derived_pointer*)derived_loc); 440 } 441 } 442 443 #ifdef ASSERT 444 445 template <ChunkFrames frame_kind> 446 template <typename RegisterMapT> 447 bool StackChunkFrameStream<frame_kind>::is_in_oops(void* p, const RegisterMapT* map) const { 448 for (OopMapStream oms(oopmap()); !oms.is_done(); oms.next()) { 449 if (oms.current().type() != OopMapValue::oop_value) { 450 continue; 451 } 452 if (reg_to_loc(oms.current().reg(), map) == p) { 453 return true; 454 } 455 } 456 return false; 457 } 458 459 template <ChunkFrames frame_kind> 460 void StackChunkFrameStream<frame_kind>::assert_is_interpreted_and_frame_type_mixed() const { 461 assert(is_interpreted(), ""); 462 assert(frame_kind == ChunkFrames::Mixed, ""); 463 } 464 465 #endif 466 467 #endif // SHARE_OOPS_STACKCHUNKFRAMESTREAM_INLINE_HPP