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 private: 158 int _pc_offset; // offset in the code that this OopMap corresponds to 159 int _omv_count; // number of OopMapValues in the stream 160 int _num_oops; // number of oops 161 int _index; // index in OopMapSet 162 bool _has_derived_oops; 163 CompressedWriteStream* _write_stream; 164 165 DEBUG_ONLY( OopMapValue::oop_types* _locs_used; int _locs_length;) 166 167 // Accessors 168 int omv_count() const { return _omv_count; } 169 void set_omv_count(int value) { _omv_count = value; } 170 void increment_count() { _omv_count++; } 171 void increment_num_oops() { _num_oops++; } 172 void set_has_derived_oops(bool value) { _has_derived_oops = value; } 173 CompressedWriteStream* write_stream() const { return _write_stream; } 174 void set_write_stream(CompressedWriteStream* value) { _write_stream = value; } 175 176 enum DeepCopyToken { _deep_copy_token }; 177 OopMap(DeepCopyToken, OopMap* source); // used only by deep_copy 178 179 void set_xxx(VMReg reg, OopMapValue::oop_types x, VMReg optional); 180 181 public: 182 OopMap(int frame_size, int arg_count); 183 184 // pc-offset handling 185 int offset() const { return _pc_offset; } 186 void set_offset(int o) { _pc_offset = o; } 187 int count() const { return _omv_count; } 188 int data_size() const { return write_stream()->position(); } 189 address data() const { return write_stream()->buffer(); } 190 int num_oops() const { return _num_oops; } 191 bool has_derived_oops() const { return _has_derived_oops; } 192 int index() const { return _index; } 193 194 // Construction 195 // frame_size units are stack-slots (4 bytes) NOT intptr_t; we can name odd 196 // slots to hold 4-byte values like ints and floats in the LP64 build. 197 void set_oop ( VMReg local); 198 void set_narrowoop(VMReg local); 199 void set_callee_saved( VMReg local, VMReg caller_machine_register ); 200 void set_derived_oop ( VMReg local, VMReg derived_from_local_register ); 201 202 int heap_size() const; 203 void copy_data_to(address addr) const; 204 void copy_and_sort_data_to(address addr) const; 205 OopMap* deep_copy(); 206 207 bool legal_vm_reg_name(VMReg local) { 208 return OopMapValue::legal_vm_reg_name(local); 209 } 210 211 // Printing 212 void print_on(outputStream* st) const; 213 void print() const; 214 bool equals(const OopMap* other) const; 215 }; 216 217 class OopMapSet : public ResourceObj { 218 friend class VMStructs; 219 private: 220 GrowableArray<OopMap*> _list; 221 222 int add(OopMap* value) { return _list.append(value); } 223 224 public: 225 OopMapSet(); 226 227 // returns the number of OopMaps in this OopMapSet 228 int size() const { return _list.length(); } 229 // returns the OopMap at a given index 230 OopMap* at(int index) const { return _list.at(index); } 231 232 // Collect OopMaps. 233 int add_gc_map(int pc, OopMap* map); 234 235 // Methods oops_do() and all_do() filter out nullptr oops and 236 // oop == CompressedOops::base() before passing oops 237 // to closures. 238 239 static const ImmutableOopMap* find_map(const CodeBlob* cb, address pc); 240 static const ImmutableOopMap* find_map(const frame *fr); 241 242 // Iterates through frame for a compiled method 243 static void oops_do (const frame* fr, 244 const RegisterMap* reg_map, 245 OopClosure* f, 246 DerivedOopClosure* df); 247 static void oops_do (const frame* fr, 248 const RegisterMap* reg_map, 249 OopClosure* f, 250 DerivedPointerIterationMode mode); 251 static void update_register_map(const frame* fr, RegisterMap *reg_map); 252 253 #ifndef PRODUCT 254 static void trace_codeblob_maps(const frame *fr, const RegisterMap *reg_map); 255 #endif 256 257 // Printing 258 void print_on(outputStream* st) const; 259 void print() const; 260 }; 261 262 class ImmutableOopMapBuilder; 263 264 class OopMapClosure : public Closure { 265 public: 266 virtual bool handle_type(OopMapValue::oop_types type) { return true; } 267 virtual void do_value(VMReg reg, OopMapValue::oop_types type) = 0; 268 }; 269 270 template <typename OopFnT, typename DerivedOopFnT, typename ValueFilterT> 271 class OopMapDo; 272 273 class ImmutableOopMap { 274 friend class OopMapStream; 275 friend class VMStructs; 276 template <typename OopFnT, typename DerivedOopFnT, typename ValueFilterT> 277 friend class OopMapDo; 278 #ifdef ASSERT 279 friend class ImmutableOopMapBuilder; 280 #endif 281 private: 282 int _count; // contains the number of entries in this OopMap 283 int _num_oops; 284 bool _has_derived_oops; 285 286 address data_addr() const { return (address) this + sizeof(ImmutableOopMap); } 287 public: 288 ImmutableOopMap(const OopMap* oopmap); 289 290 int count() const { return _count; } 291 int num_oops() const { return _num_oops; } 292 bool has_derived_oops() const { return _has_derived_oops; } 293 bool has_any(OopMapValue::oop_types type) const; 294 295 int nr_of_bytes() const; // this is an expensive operation, only used in debug builds or in aot code generation 296 297 void oops_do(const frame* fr, const RegisterMap* reg_map, OopClosure* f, DerivedOopClosure* df) const; 298 void oops_do(const frame* fr, const RegisterMap* reg_map, OopClosure* f, DerivedPointerIterationMode derived_mode) const; 299 void all_type_do(const frame *fr, OopMapValue::oop_types type, OopMapClosure* fn) const; 300 void all_type_do(const frame *fr, OopMapClosure* fn) const; 301 void update_register_map(const frame* fr, RegisterMap *reg_map) const; 302 303 // Printing 304 void print_on(outputStream* st) const; 305 void print() const; 306 }; 307 308 class ImmutableOopMapSet; 309 class ImmutableOopMap; 310 class OopMapSet; 311 312 class ImmutableOopMapPair { 313 friend class VMStructs; 314 private: 315 int _pc_offset; // program counter offset from the beginning of the method 316 int _oopmap_offset; // offset in the data in the ImmutableOopMapSet where the ImmutableOopMap is located 317 public: 318 ImmutableOopMapPair(int pc_offset, int oopmap_offset) : _pc_offset(pc_offset), _oopmap_offset(oopmap_offset) { 319 assert(pc_offset >= 0 && oopmap_offset >= 0, "check"); 320 } 321 const ImmutableOopMap* get_from(const ImmutableOopMapSet* set) const; 322 323 int pc_offset() const { return _pc_offset; } 324 int oopmap_offset() const { return _oopmap_offset; } 325 }; 326 327 class ImmutableOopMapSet { 328 friend class VMStructs; 329 private: 330 int _count; // nr of ImmutableOopMapPairs in the Set 331 int _size; // nr of bytes including ImmutableOopMapSet itself 332 333 address data() const { return (address) this + sizeof(*this) + sizeof(ImmutableOopMapPair) * _count; } 334 335 public: 336 void operator delete(void* p); 337 338 ImmutableOopMapSet(const OopMapSet* oopmap_set, int size) : _count(oopmap_set->size()), _size(size) {} 339 ~ImmutableOopMapSet() = default; 340 341 ImmutableOopMap* oopmap_at_offset(int offset) const { 342 assert(offset >= 0 && offset < _size, "must be within boundaries"); 343 address addr = data() + offset; 344 return (ImmutableOopMap*) addr; 345 } 346 347 ImmutableOopMapPair* get_pairs() const { return (ImmutableOopMapPair*) ((address) this + sizeof(*this)); } 348 349 static ImmutableOopMapSet* build_from(const OopMapSet* oopmap_set); 350 351 int find_slot_for_offset(int pc_offset) const; 352 const ImmutableOopMap* find_map_at_offset(int pc_offset) const; 353 const ImmutableOopMap* find_map_at_slot(int slot, int pc_offset) const; 354 355 const ImmutableOopMapPair* pair_at(int index) const { assert(index >= 0 && index < _count, "check"); return &get_pairs()[index]; } 356 357 int count() const { return _count; } 358 int nr_of_bytes() const { return _size; } 359 360 void print_on(outputStream* st) const; 361 void print() const; 362 }; 363 364 class OopMapStream : public StackObj { 365 private: 366 CompressedReadStream _stream; 367 int _size; 368 int _position; 369 bool _valid_omv; 370 OopMapValue _omv; 371 void find_next(); 372 373 public: 374 OopMapStream(const OopMap* oop_map); 375 OopMapStream(const ImmutableOopMap* oop_map); 376 bool is_done() { if(!_valid_omv) { find_next(); } return !_valid_omv; } 377 void next() { find_next(); } 378 OopMapValue current() { return _omv; } 379 int stream_position() const { return _stream.position(); } 380 }; 381 382 class ImmutableOopMapBuilder { 383 private: 384 class Mapping; 385 386 private: 387 const OopMapSet* _set; 388 const OopMap* _empty; 389 const OopMap* _last; 390 int _empty_offset; 391 int _last_offset; 392 int _offset; 393 int _required; 394 Mapping* _mapping; 395 ImmutableOopMapSet* _new_set; 396 397 /* Used for bookkeeping when building ImmutableOopMaps */ 398 class Mapping : public ResourceObj { 399 public: 400 enum kind_t { OOPMAP_UNKNOWN = 0, OOPMAP_NEW = 1, OOPMAP_EMPTY = 2, OOPMAP_DUPLICATE = 3 }; 401 402 kind_t _kind; 403 int _offset; 404 int _size; 405 const OopMap* _map; 406 const OopMap* _other; 407 408 Mapping() : _kind(OOPMAP_UNKNOWN), _offset(-1), _size(-1), _map(nullptr) {} 409 410 void set(kind_t kind, int offset, int size, const OopMap* map, const OopMap* other = nullptr) { 411 _kind = kind; 412 _offset = offset; 413 _size = size; 414 _map = map; 415 _other = other; 416 } 417 }; 418 419 public: 420 ImmutableOopMapBuilder(const OopMapSet* set); 421 422 int heap_size(); 423 ImmutableOopMapSet* build(); 424 ImmutableOopMapSet* generate_into(address buffer); 425 private: 426 bool is_empty(const OopMap* map) const { 427 return map->count() == 0; 428 } 429 430 bool is_last_duplicate(const OopMap* map) { 431 if (_last != nullptr && _last->count() > 0 && _last->equals(map)) { 432 return true; 433 } 434 return false; 435 } 436 437 #ifdef ASSERT 438 void verify(address buffer, int size, const ImmutableOopMapSet* set); 439 #endif 440 441 bool has_empty() const { 442 return _empty_offset != -1; 443 } 444 445 int size_for(const OopMap* map) const; 446 void fill_pair(ImmutableOopMapPair* pair, const OopMap* map, int offset, const ImmutableOopMapSet* set); 447 int fill_map(ImmutableOopMapPair* pair, const OopMap* map, int offset, const ImmutableOopMapSet* set); 448 void fill(ImmutableOopMapSet* set, int size); 449 }; 450 451 class SkipNullValue { 452 public: 453 static inline bool should_skip(void* val); 454 }; 455 456 class IncludeAllValues { 457 public: 458 static bool should_skip(void* value) { return false; } 459 }; 460 461 template <typename OopFnT, typename DerivedOopFnT, typename ValueFilterT> 462 class OopMapDo { 463 private: 464 OopFnT* _oop_fn; 465 DerivedOopFnT* _derived_oop_fn; 466 public: 467 OopMapDo(OopFnT* oop_fn, DerivedOopFnT* derived_oop_fn) : _oop_fn(oop_fn), _derived_oop_fn(derived_oop_fn) {} 468 template <typename RegisterMapT> 469 void oops_do(const frame* fr, const RegisterMapT* reg_map, const ImmutableOopMap* oopmap); 470 private: 471 template <typename RegisterMapT> 472 void iterate_oops_do(const frame *fr, const RegisterMapT *reg_map, const ImmutableOopMap* oopmap); 473 }; 474 475 // Derived pointer support. This table keeps track of all derived points on a 476 // stack. It is cleared before each scavenge/GC. During the traversal of all 477 // oops, it is filled in with references to all locations that contains a 478 // derived oop (assumed to be very few). When the GC is complete, the derived 479 // pointers are updated based on their base pointers new value and an offset. 480 #if COMPILER2_OR_JVMCI 481 class DerivedPointerTable : public AllStatic { 482 friend class VMStructs; 483 private: 484 class Entry; 485 static bool _active; // do not record pointers for verify pass etc. 486 487 public: 488 static void clear(); // Called before scavenge/GC 489 static void add(derived_pointer* derived, derived_base* base); // Called during scavenge/GC 490 static void update_pointers(); // Called after scavenge/GC 491 static bool is_empty(); 492 static bool is_active() { return _active; } 493 static void set_active(bool value) { _active = value; } 494 }; 495 496 // A utility class to temporarily "deactivate" the DerivedPointerTable. 497 // (Note: clients are responsible for any MT-safety issues) 498 class DerivedPointerTableDeactivate: public StackObj { 499 private: 500 bool _active; 501 public: 502 DerivedPointerTableDeactivate() { 503 _active = DerivedPointerTable::is_active(); 504 if (_active) { 505 DerivedPointerTable::set_active(false); 506 } 507 } 508 509 ~DerivedPointerTableDeactivate() { 510 assert(!DerivedPointerTable::is_active(), 511 "Inconsistency: not MT-safe"); 512 if (_active) { 513 DerivedPointerTable::set_active(true); 514 } 515 } 516 }; 517 #endif // COMPILER2_OR_JVMCI 518 519 #endif // SHARE_COMPILER_OOPMAP_HPP