1 /* 2 * Copyright (c) 1998, 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_COMPILER_OOPMAP_HPP 26 #define SHARE_COMPILER_OOPMAP_HPP 27 28 #include "code/compressedStream.hpp" 29 #include "code/vmreg.hpp" 30 #include "memory/allocation.hpp" 31 #include "memory/iterator.hpp" 32 #include "oops/oopsHierarchy.hpp" 33 #include "utilities/checkedCast.hpp" 34 #include "utilities/growableArray.hpp" 35 36 // Interface for generating the frame map for compiled code. A frame map 37 // describes for a specific pc whether each register and frame stack slot is: 38 // Oop - A GC root for current frame 39 // Dead - Dead; can be Zapped for debugging 40 // CalleeXX - Callee saved; also describes which caller register is saved 41 // DerivedXX - A derived oop; original oop is described. 42 // 43 // OopMapValue describes a single OopMap entry 44 45 enum class DerivedPointerIterationMode; 46 class frame; 47 class RegisterMap; 48 class OopClosure; 49 class CodeBlob; 50 class ImmutableOopMap; 51 52 enum class derived_base : intptr_t {}; 53 enum class derived_pointer : intptr_t {}; 54 55 class OopMapValue: public StackObj { 56 friend class VMStructs; 57 private: 58 unsigned short _value; 59 unsigned short value() const { return _value; } 60 void set_value(unsigned short value) { _value = value; } 61 short _content_reg; 62 63 public: 64 // Constants 65 enum { type_bits = 2, 66 register_bits = BitsPerShort - type_bits }; 67 68 enum { type_shift = 0, 69 register_shift = type_bits }; 70 71 enum { type_mask = right_n_bits(type_bits), 72 type_mask_in_place = type_mask << type_shift, 73 register_mask = right_n_bits(register_bits), 74 register_mask_in_place = register_mask << register_shift }; 75 76 enum oop_types { 77 oop_value, 78 narrowoop_value, 79 callee_saved_value, 80 derived_oop_value, 81 unused_value = -1 // Only used as a sentinel value 82 }; 83 84 // Constructors 85 OopMapValue () { set_value(0); set_content_reg(VMRegImpl::Bad()); } 86 OopMapValue (VMReg reg, oop_types t, VMReg reg2) { 87 set_reg_type(reg, t); 88 set_content_reg(reg2); 89 } 90 91 private: 92 void set_reg_type(VMReg p, oop_types t) { 93 set_value(checked_cast<unsigned short>((p->value() << register_shift) | t)); 94 assert(reg() == p, "sanity check" ); 95 assert(type() == t, "sanity check" ); 96 } 97 98 void set_content_reg(VMReg r) { 99 if (is_callee_saved()) { 100 // This can never be a stack location, so we don't need to transform it. 101 assert(r->is_reg(), "Trying to callee save a stack location"); 102 } else if (is_derived_oop()) { 103 assert (r->is_valid(), "must have a valid VMReg"); 104 } else { 105 assert (!r->is_valid(), "valid VMReg not allowed"); 106 } 107 _content_reg = checked_cast<short>(r->value()); 108 } 109 110 public: 111 // Archiving 112 void write_on(CompressedWriteStream* stream) { 113 stream->write_int(value()); 114 if(is_callee_saved() || is_derived_oop()) { 115 stream->write_int(checked_cast<int>(content_reg()->value())); 116 } 117 } 118 119 void read_from(CompressedReadStream* stream) { 120 set_value(checked_cast<unsigned short>(stream->read_int())); 121 if (is_callee_saved() || is_derived_oop()) { 122 set_content_reg(VMRegImpl::as_VMReg(stream->read_int(), true)); 123 } 124 } 125 126 // Querying 127 bool is_oop() { return mask_bits(value(), type_mask_in_place) == oop_value; } 128 bool is_narrowoop() { return mask_bits(value(), type_mask_in_place) == narrowoop_value; } 129 bool is_callee_saved() { return mask_bits(value(), type_mask_in_place) == callee_saved_value; } 130 bool is_derived_oop() { return mask_bits(value(), type_mask_in_place) == derived_oop_value; } 131 132 VMReg reg() const { return VMRegImpl::as_VMReg(checked_cast<int>(mask_bits(value(), register_mask_in_place) >> register_shift)); } 133 oop_types type() const { return (oop_types)mask_bits(value(), type_mask_in_place); } 134 135 static bool legal_vm_reg_name(VMReg p) { 136 return (p->value() == (p->value() & register_mask)); 137 } 138 139 VMReg content_reg() const { return VMRegImpl::as_VMReg(_content_reg, true); } 140 141 // Returns offset from sp. 142 int stack_offset() { 143 assert(reg()->is_stack(), "must be stack location"); 144 return reg()->reg2stack(); 145 } 146 147 void print_on(outputStream* st) const; 148 void print() const; 149 }; 150 151 152 class OopMap: public ResourceObj { 153 friend class OopMapStream; 154 friend class VMStructs; 155 friend class OopMapSet; 156 friend class OopMapSort; 157 friend class SCCReader; 158 private: 159 int _pc_offset; // offset in the code that this OopMap corresponds to 160 int _omv_count; // number of OopMapValues in the stream 161 int _num_oops; // number of oops 162 int _index; // index in OopMapSet 163 bool _has_derived_oops; 164 CompressedWriteStream* _write_stream; 165 166 debug_only( OopMapValue::oop_types* _locs_used; int _locs_length;) 167 168 // Accessors 169 int omv_count() const { return _omv_count; } 170 void set_omv_count(int value) { _omv_count = value; } 171 void increment_count() { _omv_count++; } 172 void increment_num_oops() { _num_oops++; } 173 void set_has_derived_oops(bool value) { _has_derived_oops = value; } 174 CompressedWriteStream* write_stream() const { return _write_stream; } 175 void set_write_stream(CompressedWriteStream* value) { _write_stream = value; } 176 177 enum DeepCopyToken { _deep_copy_token }; 178 OopMap(DeepCopyToken, OopMap* source); // used only by deep_copy 179 180 void set_xxx(VMReg reg, OopMapValue::oop_types x, VMReg optional); 181 182 public: 183 OopMap(int frame_size, int arg_count); 184 OopMap(int data_size); 185 186 // pc-offset handling 187 int offset() const { return _pc_offset; } 188 void set_offset(int o) { _pc_offset = o; } 189 int count() const { return _omv_count; } 190 int data_size() const { return write_stream()->position(); } 191 address data() const { return write_stream()->buffer(); } 192 int num_oops() const { return _num_oops; } 193 bool has_derived_oops() const { return _has_derived_oops; } 194 int index() const { return _index; } 195 196 // Construction 197 // frame_size units are stack-slots (4 bytes) NOT intptr_t; we can name odd 198 // slots to hold 4-byte values like ints and floats in the LP64 build. 199 void set_oop ( VMReg local); 200 void set_narrowoop(VMReg local); 201 void set_callee_saved( VMReg local, VMReg caller_machine_register ); 202 void set_derived_oop ( VMReg local, VMReg derived_from_local_register ); 203 204 int heap_size() const; 205 void copy_data_to(address addr) const; 206 void copy_and_sort_data_to(address addr) const; 207 OopMap* deep_copy(); 208 209 bool legal_vm_reg_name(VMReg local) { 210 return OopMapValue::legal_vm_reg_name(local); 211 } 212 213 // Printing 214 void print_on(outputStream* st) const; 215 void print() const; 216 bool equals(const OopMap* other) const; 217 }; 218 219 class OopMapSet : public ResourceObj { 220 friend class VMStructs; 221 friend class SCCReader; 222 private: 223 GrowableArray<OopMap*> _list; 224 225 int add(OopMap* value) { return _list.append(value); } 226 227 public: 228 OopMapSet(); 229 OopMapSet(int size); 230 231 // returns the number of OopMaps in this OopMapSet 232 int size() const { return _list.length(); } 233 // returns the OopMap at a given index 234 OopMap* at(int index) const { return _list.at(index); } 235 236 // Collect OopMaps. 237 int add_gc_map(int pc, OopMap* map); 238 239 // Methods oops_do() and all_do() filter out nullptr oops and 240 // oop == CompressedOops::base() before passing oops 241 // to closures. 242 243 static const ImmutableOopMap* find_map(const CodeBlob* cb, address pc); 244 static const ImmutableOopMap* find_map(const frame *fr); 245 246 // Iterates through frame for a compiled method 247 static void oops_do (const frame* fr, 248 const RegisterMap* reg_map, 249 OopClosure* f, 250 DerivedOopClosure* df); 251 static void oops_do (const frame* fr, 252 const RegisterMap* reg_map, 253 OopClosure* f, 254 DerivedPointerIterationMode mode); 255 static void update_register_map(const frame* fr, RegisterMap *reg_map); 256 257 #ifndef PRODUCT 258 static void trace_codeblob_maps(const frame *fr, const RegisterMap *reg_map); 259 #endif 260 261 // Printing 262 void print_on(outputStream* st) const; 263 void print() const; 264 }; 265 266 class ImmutableOopMapBuilder; 267 268 class OopMapClosure : public Closure { 269 public: 270 virtual bool handle_type(OopMapValue::oop_types type) { return true; } 271 virtual void do_value(VMReg reg, OopMapValue::oop_types type) = 0; 272 }; 273 274 template <typename OopFnT, typename DerivedOopFnT, typename ValueFilterT> 275 class OopMapDo; 276 277 class ImmutableOopMap { 278 friend class OopMapStream; 279 friend class VMStructs; 280 template <typename OopFnT, typename DerivedOopFnT, typename ValueFilterT> 281 friend class OopMapDo; 282 #ifdef ASSERT 283 friend class ImmutableOopMapBuilder; 284 #endif 285 private: 286 int _count; // contains the number of entries in this OopMap 287 int _num_oops; 288 bool _has_derived_oops; 289 290 address data_addr() const { return (address) this + sizeof(ImmutableOopMap); } 291 public: 292 ImmutableOopMap(const OopMap* oopmap); 293 294 int count() const { return _count; } 295 int num_oops() const { return _num_oops; } 296 bool has_derived_oops() const { return _has_derived_oops; } 297 bool has_any(OopMapValue::oop_types type) const; 298 299 #ifdef ASSERT 300 int nr_of_bytes() const; // this is an expensive operation, only used in debug builds 301 #endif 302 303 void oops_do(const frame* fr, const RegisterMap* reg_map, OopClosure* f, DerivedOopClosure* df) const; 304 void oops_do(const frame* fr, const RegisterMap* reg_map, OopClosure* f, DerivedPointerIterationMode derived_mode) const; 305 void all_type_do(const frame *fr, OopMapValue::oop_types type, OopMapClosure* fn) const; 306 void all_type_do(const frame *fr, OopMapClosure* fn) const; 307 void update_register_map(const frame* fr, RegisterMap *reg_map) const; 308 309 // Printing 310 void print_on(outputStream* st) const; 311 void print() const; 312 }; 313 314 class ImmutableOopMapSet; 315 class ImmutableOopMap; 316 class OopMapSet; 317 318 class ImmutableOopMapPair { 319 friend class VMStructs; 320 private: 321 int _pc_offset; // program counter offset from the beginning of the method 322 int _oopmap_offset; // offset in the data in the ImmutableOopMapSet where the ImmutableOopMap is located 323 public: 324 ImmutableOopMapPair(int pc_offset, int oopmap_offset) : _pc_offset(pc_offset), _oopmap_offset(oopmap_offset) { 325 assert(pc_offset >= 0 && oopmap_offset >= 0, "check"); 326 } 327 const ImmutableOopMap* get_from(const ImmutableOopMapSet* set) const; 328 329 int pc_offset() const { return _pc_offset; } 330 int oopmap_offset() const { return _oopmap_offset; } 331 }; 332 333 class ImmutableOopMapSet { 334 friend class VMStructs; 335 private: 336 int _count; // nr of ImmutableOopMapPairs in the Set 337 int _size; // nr of bytes including ImmutableOopMapSet itself 338 339 address data() const { return (address) this + sizeof(*this) + sizeof(ImmutableOopMapPair) * _count; } 340 341 public: 342 void operator delete(void* p); 343 344 ImmutableOopMapSet(const OopMapSet* oopmap_set, int size) : _count(oopmap_set->size()), _size(size) {} 345 ~ImmutableOopMapSet() = default; 346 347 ImmutableOopMap* oopmap_at_offset(int offset) const { 348 assert(offset >= 0 && offset < _size, "must be within boundaries"); 349 address addr = data() + offset; 350 return (ImmutableOopMap*) addr; 351 } 352 353 ImmutableOopMapPair* get_pairs() const { return (ImmutableOopMapPair*) ((address) this + sizeof(*this)); } 354 355 static ImmutableOopMapSet* build_from(const OopMapSet* oopmap_set); 356 357 int find_slot_for_offset(int pc_offset) const; 358 const ImmutableOopMap* find_map_at_offset(int pc_offset) const; 359 const ImmutableOopMap* find_map_at_slot(int slot, int pc_offset) const; 360 361 const ImmutableOopMapPair* pair_at(int index) const { assert(index >= 0 && index < _count, "check"); return &get_pairs()[index]; } 362 363 int count() const { return _count; } 364 int nr_of_bytes() const { return _size; } 365 366 void print_on(outputStream* st) const; 367 void print() const; 368 }; 369 370 class OopMapStream : public StackObj { 371 private: 372 CompressedReadStream _stream; 373 int _size; 374 int _position; 375 bool _valid_omv; 376 OopMapValue _omv; 377 void find_next(); 378 379 public: 380 OopMapStream(const OopMap* oop_map); 381 OopMapStream(const ImmutableOopMap* oop_map); 382 bool is_done() { if(!_valid_omv) { find_next(); } return !_valid_omv; } 383 void next() { find_next(); } 384 OopMapValue current() { return _omv; } 385 #ifdef ASSERT 386 int stream_position() const { return _stream.position(); } 387 #endif 388 }; 389 390 class ImmutableOopMapBuilder { 391 private: 392 class Mapping; 393 394 private: 395 const OopMapSet* _set; 396 const OopMap* _empty; 397 const OopMap* _last; 398 int _empty_offset; 399 int _last_offset; 400 int _offset; 401 int _required; 402 Mapping* _mapping; 403 ImmutableOopMapSet* _new_set; 404 405 /* Used for bookkeeping when building ImmutableOopMaps */ 406 class Mapping : public ResourceObj { 407 public: 408 enum kind_t { OOPMAP_UNKNOWN = 0, OOPMAP_NEW = 1, OOPMAP_EMPTY = 2, OOPMAP_DUPLICATE = 3 }; 409 410 kind_t _kind; 411 int _offset; 412 int _size; 413 const OopMap* _map; 414 const OopMap* _other; 415 416 Mapping() : _kind(OOPMAP_UNKNOWN), _offset(-1), _size(-1), _map(nullptr) {} 417 418 void set(kind_t kind, int offset, int size, const OopMap* map, const OopMap* other = nullptr) { 419 _kind = kind; 420 _offset = offset; 421 _size = size; 422 _map = map; 423 _other = other; 424 } 425 }; 426 427 public: 428 ImmutableOopMapBuilder(const OopMapSet* set); 429 430 int heap_size(); 431 ImmutableOopMapSet* build(); 432 ImmutableOopMapSet* generate_into(address buffer); 433 private: 434 bool is_empty(const OopMap* map) const { 435 return map->count() == 0; 436 } 437 438 bool is_last_duplicate(const OopMap* map) { 439 if (_last != nullptr && _last->count() > 0 && _last->equals(map)) { 440 return true; 441 } 442 return false; 443 } 444 445 #ifdef ASSERT 446 void verify(address buffer, int size, const ImmutableOopMapSet* set); 447 #endif 448 449 bool has_empty() const { 450 return _empty_offset != -1; 451 } 452 453 int size_for(const OopMap* map) const; 454 void fill_pair(ImmutableOopMapPair* pair, const OopMap* map, int offset, const ImmutableOopMapSet* set); 455 int fill_map(ImmutableOopMapPair* pair, const OopMap* map, int offset, const ImmutableOopMapSet* set); 456 void fill(ImmutableOopMapSet* set, int size); 457 }; 458 459 class SkipNullValue { 460 public: 461 static inline bool should_skip(void* val); 462 }; 463 464 class IncludeAllValues { 465 public: 466 static bool should_skip(void* value) { return false; } 467 }; 468 469 template <typename OopFnT, typename DerivedOopFnT, typename ValueFilterT> 470 class OopMapDo { 471 private: 472 OopFnT* _oop_fn; 473 DerivedOopFnT* _derived_oop_fn; 474 public: 475 OopMapDo(OopFnT* oop_fn, DerivedOopFnT* derived_oop_fn) : _oop_fn(oop_fn), _derived_oop_fn(derived_oop_fn) {} 476 template <typename RegisterMapT> 477 void oops_do(const frame* fr, const RegisterMapT* reg_map, const ImmutableOopMap* oopmap); 478 private: 479 template <typename RegisterMapT> 480 void iterate_oops_do(const frame *fr, const RegisterMapT *reg_map, const ImmutableOopMap* oopmap); 481 }; 482 483 // Derived pointer support. This table keeps track of all derived points on a 484 // stack. It is cleared before each scavenge/GC. During the traversal of all 485 // oops, it is filled in with references to all locations that contains a 486 // derived oop (assumed to be very few). When the GC is complete, the derived 487 // pointers are updated based on their base pointers new value and an offset. 488 #if COMPILER2_OR_JVMCI 489 class DerivedPointerTable : public AllStatic { 490 friend class VMStructs; 491 private: 492 class Entry; 493 static bool _active; // do not record pointers for verify pass etc. 494 495 public: 496 static void clear(); // Called before scavenge/GC 497 static void add(derived_pointer* derived, derived_base* base); // Called during scavenge/GC 498 static void update_pointers(); // Called after scavenge/GC 499 static bool is_empty(); 500 static bool is_active() { return _active; } 501 static void set_active(bool value) { _active = value; } 502 }; 503 504 // A utility class to temporarily "deactivate" the DerivedPointerTable. 505 // (Note: clients are responsible for any MT-safety issues) 506 class DerivedPointerTableDeactivate: public StackObj { 507 private: 508 bool _active; 509 public: 510 DerivedPointerTableDeactivate() { 511 _active = DerivedPointerTable::is_active(); 512 if (_active) { 513 DerivedPointerTable::set_active(false); 514 } 515 } 516 517 ~DerivedPointerTableDeactivate() { 518 assert(!DerivedPointerTable::is_active(), 519 "Inconsistency: not MT-safe"); 520 if (_active) { 521 DerivedPointerTable::set_active(true); 522 } 523 } 524 }; 525 #endif // COMPILER2_OR_JVMCI 526 527 #endif // SHARE_COMPILER_OOPMAP_HPP