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 #ifdef ASSERT 296 int nr_of_bytes() const; // this is an expensive operation, only used in debug builds 297 #endif 298 299 void oops_do(const frame* fr, const RegisterMap* reg_map, OopClosure* f, DerivedOopClosure* df) const; 300 void oops_do(const frame* fr, const RegisterMap* reg_map, OopClosure* f, DerivedPointerIterationMode derived_mode) const; 301 void all_type_do(const frame *fr, OopMapValue::oop_types type, OopMapClosure* fn) const; 302 void all_type_do(const frame *fr, OopMapClosure* fn) const; 303 void update_register_map(const frame* fr, RegisterMap *reg_map) const; 304 305 // Printing 306 void print_on(outputStream* st) const; 307 void print() const; 308 }; 309 310 class ImmutableOopMapSet; 311 class ImmutableOopMap; 312 class OopMapSet; 313 314 class ImmutableOopMapPair { 315 friend class VMStructs; 316 private: 317 int _pc_offset; // program counter offset from the beginning of the method 318 int _oopmap_offset; // offset in the data in the ImmutableOopMapSet where the ImmutableOopMap is located 319 public: 320 ImmutableOopMapPair(int pc_offset, int oopmap_offset) : _pc_offset(pc_offset), _oopmap_offset(oopmap_offset) { 321 assert(pc_offset >= 0 && oopmap_offset >= 0, "check"); 322 } 323 const ImmutableOopMap* get_from(const ImmutableOopMapSet* set) const; 324 325 int pc_offset() const { return _pc_offset; } 326 int oopmap_offset() const { return _oopmap_offset; } 327 }; 328 329 class ImmutableOopMapSet { 330 friend class VMStructs; 331 private: 332 int _count; // nr of ImmutableOopMapPairs in the Set 333 int _size; // nr of bytes including ImmutableOopMapSet itself 334 335 address data() const { return (address) this + sizeof(*this) + sizeof(ImmutableOopMapPair) * _count; } 336 337 public: 338 void operator delete(void* p); 339 340 ImmutableOopMapSet(const OopMapSet* oopmap_set, int size) : _count(oopmap_set->size()), _size(size) {} 341 ~ImmutableOopMapSet() = default; 342 343 ImmutableOopMap* oopmap_at_offset(int offset) const { 344 assert(offset >= 0 && offset < _size, "must be within boundaries"); 345 address addr = data() + offset; 346 return (ImmutableOopMap*) addr; 347 } 348 349 ImmutableOopMapPair* get_pairs() const { return (ImmutableOopMapPair*) ((address) this + sizeof(*this)); } 350 351 static ImmutableOopMapSet* build_from(const OopMapSet* oopmap_set); 352 353 int find_slot_for_offset(int pc_offset) const; 354 const ImmutableOopMap* find_map_at_offset(int pc_offset) const; 355 const ImmutableOopMap* find_map_at_slot(int slot, int pc_offset) const; 356 357 const ImmutableOopMapPair* pair_at(int index) const { assert(index >= 0 && index < _count, "check"); return &get_pairs()[index]; } 358 359 int count() const { return _count; } 360 int nr_of_bytes() const { return _size; } 361 362 void print_on(outputStream* st) const; 363 void print() const; 364 }; 365 366 class OopMapStream : public StackObj { 367 private: 368 CompressedReadStream _stream; 369 int _size; 370 int _position; 371 bool _valid_omv; 372 OopMapValue _omv; 373 void find_next(); 374 375 public: 376 OopMapStream(const OopMap* oop_map); 377 OopMapStream(const ImmutableOopMap* oop_map); 378 bool is_done() { if(!_valid_omv) { find_next(); } return !_valid_omv; } 379 void next() { find_next(); } 380 OopMapValue current() { return _omv; } 381 #ifdef ASSERT 382 int stream_position() const { return _stream.position(); } 383 #endif 384 }; 385 386 class ImmutableOopMapBuilder { 387 private: 388 class Mapping; 389 390 private: 391 const OopMapSet* _set; 392 const OopMap* _empty; 393 const OopMap* _last; 394 int _empty_offset; 395 int _last_offset; 396 int _offset; 397 int _required; 398 Mapping* _mapping; 399 ImmutableOopMapSet* _new_set; 400 401 /* Used for bookkeeping when building ImmutableOopMaps */ 402 class Mapping : public ResourceObj { 403 public: 404 enum kind_t { OOPMAP_UNKNOWN = 0, OOPMAP_NEW = 1, OOPMAP_EMPTY = 2, OOPMAP_DUPLICATE = 3 }; 405 406 kind_t _kind; 407 int _offset; 408 int _size; 409 const OopMap* _map; 410 const OopMap* _other; 411 412 Mapping() : _kind(OOPMAP_UNKNOWN), _offset(-1), _size(-1), _map(nullptr) {} 413 414 void set(kind_t kind, int offset, int size, const OopMap* map, const OopMap* other = nullptr) { 415 _kind = kind; 416 _offset = offset; 417 _size = size; 418 _map = map; 419 _other = other; 420 } 421 }; 422 423 public: 424 ImmutableOopMapBuilder(const OopMapSet* set); 425 426 int heap_size(); 427 ImmutableOopMapSet* build(); 428 ImmutableOopMapSet* generate_into(address buffer); 429 private: 430 bool is_empty(const OopMap* map) const { 431 return map->count() == 0; 432 } 433 434 bool is_last_duplicate(const OopMap* map) { 435 if (_last != nullptr && _last->count() > 0 && _last->equals(map)) { 436 return true; 437 } 438 return false; 439 } 440 441 #ifdef ASSERT 442 void verify(address buffer, int size, const ImmutableOopMapSet* set); 443 #endif 444 445 bool has_empty() const { 446 return _empty_offset != -1; 447 } 448 449 int size_for(const OopMap* map) const; 450 void fill_pair(ImmutableOopMapPair* pair, const OopMap* map, int offset, const ImmutableOopMapSet* set); 451 int fill_map(ImmutableOopMapPair* pair, const OopMap* map, int offset, const ImmutableOopMapSet* set); 452 void fill(ImmutableOopMapSet* set, int size); 453 }; 454 455 class SkipNullValue { 456 public: 457 static inline bool should_skip(void* val); 458 }; 459 460 class IncludeAllValues { 461 public: 462 static bool should_skip(void* value) { return false; } 463 }; 464 465 template <typename OopFnT, typename DerivedOopFnT, typename ValueFilterT> 466 class OopMapDo { 467 private: 468 OopFnT* _oop_fn; 469 DerivedOopFnT* _derived_oop_fn; 470 public: 471 OopMapDo(OopFnT* oop_fn, DerivedOopFnT* derived_oop_fn) : _oop_fn(oop_fn), _derived_oop_fn(derived_oop_fn) {} 472 template <typename RegisterMapT> 473 void oops_do(const frame* fr, const RegisterMapT* reg_map, const ImmutableOopMap* oopmap); 474 private: 475 template <typename RegisterMapT> 476 void iterate_oops_do(const frame *fr, const RegisterMapT *reg_map, const ImmutableOopMap* oopmap); 477 }; 478 479 // Derived pointer support. This table keeps track of all derived points on a 480 // stack. It is cleared before each scavenge/GC. During the traversal of all 481 // oops, it is filled in with references to all locations that contains a 482 // derived oop (assumed to be very few). When the GC is complete, the derived 483 // pointers are updated based on their base pointers new value and an offset. 484 #if COMPILER2_OR_JVMCI 485 class DerivedPointerTable : public AllStatic { 486 friend class VMStructs; 487 private: 488 class Entry; 489 static bool _active; // do not record pointers for verify pass etc. 490 491 public: 492 static void clear(); // Called before scavenge/GC 493 static void add(derived_pointer* derived, derived_base* base); // Called during scavenge/GC 494 static void update_pointers(); // Called after scavenge/GC 495 static bool is_empty(); 496 static bool is_active() { return _active; } 497 static void set_active(bool value) { _active = value; } 498 }; 499 500 // A utility class to temporarily "deactivate" the DerivedPointerTable. 501 // (Note: clients are responsible for any MT-safety issues) 502 class DerivedPointerTableDeactivate: public StackObj { 503 private: 504 bool _active; 505 public: 506 DerivedPointerTableDeactivate() { 507 _active = DerivedPointerTable::is_active(); 508 if (_active) { 509 DerivedPointerTable::set_active(false); 510 } 511 } 512 513 ~DerivedPointerTableDeactivate() { 514 assert(!DerivedPointerTable::is_active(), 515 "Inconsistency: not MT-safe"); 516 if (_active) { 517 DerivedPointerTable::set_active(true); 518 } 519 } 520 }; 521 #endif // COMPILER2_OR_JVMCI 522 523 #endif // SHARE_COMPILER_OOPMAP_HPP