| /* | |
| * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| #include "classfile/classFileParser.hpp" | |
| #include "classfile/fieldLayoutBuilder.hpp" | |
| #include "jvm.h" | |
| #include "memory/resourceArea.hpp" | |
| #include "oops/array.hpp" | |
| #include "oops/fieldStreams.inline.hpp" | |
| #include "oops/instanceMirrorKlass.hpp" | |
| #include "oops/instanceKlass.inline.hpp" | |
| #include "oops/klass.inline.hpp" | |
| #include "runtime/fieldDescriptor.inline.hpp" | |
| LayoutRawBlock::LayoutRawBlock(Kind kind, int size) : | |
| _next_block(nullptr), | |
| _prev_block(nullptr), | |
| _kind(kind), | |
| _offset(-1), | |
| _alignment(1), | |
| _size(size), | |
| _field_index(-1), | |
| _is_reference(false) { | |
| assert(kind == EMPTY || kind == RESERVED || kind == PADDING || kind == INHERITED, | |
| "Otherwise, should use the constructor with a field index argument"); | |
| assert(size > 0, "Sanity check"); | |
| } | |
| LayoutRawBlock::LayoutRawBlock(int index, Kind kind, int size, int alignment, bool is_reference) : | |
| _next_block(nullptr), | |
| _prev_block(nullptr), | |
| _kind(kind), | |
| _offset(-1), | |
| _alignment(alignment), | |
| _size(size), | |
| _field_index(index), | |
| _is_reference(is_reference) { | |
| assert(kind == REGULAR || kind == FLATTENED || kind == INHERITED, | |
| "Other kind do not have a field index"); | |
| assert(size > 0, "Sanity check"); | |
| assert(alignment > 0, "Sanity check"); | |
| } | |
| bool LayoutRawBlock::fit(int size, int alignment) { | |
| int adjustment = 0; | |
| if ((_offset % alignment) != 0) { | |
| adjustment = alignment - (_offset % alignment); | |
| } | |
| return _size >= size + adjustment; | |
| } | |
| FieldGroup::FieldGroup(int contended_group) : | |
| _next(nullptr), | |
| _primitive_fields(nullptr), | |
| _oop_fields(nullptr), | |
| _contended_group(contended_group), // -1 means no contended group, 0 means default contended group | |
| _oop_count(0) {} | |
| void FieldGroup::add_primitive_field(int idx, BasicType type) { | |
| int size = type2aelembytes(type); | |
| LayoutRawBlock* block = new LayoutRawBlock(idx, LayoutRawBlock::REGULAR, size, size /* alignment == size for primitive types */, false); | |
| if (_primitive_fields == nullptr) { | |
| _primitive_fields = new GrowableArray<LayoutRawBlock*>(INITIAL_LIST_SIZE); | |
| } | |
| _primitive_fields->append(block); | |
| } | |
| void FieldGroup::add_oop_field(int idx) { | |
| int size = type2aelembytes(T_OBJECT); | |
| LayoutRawBlock* block = new LayoutRawBlock(idx, LayoutRawBlock::REGULAR, size, size /* alignment == size for oops */, true); | |
| if (_oop_fields == nullptr) { | |
| _oop_fields = new GrowableArray<LayoutRawBlock*>(INITIAL_LIST_SIZE); | |
| } | |
| _oop_fields->append(block); | |
| _oop_count++; | |
| } | |
| void FieldGroup::sort_by_size() { | |
| if (_primitive_fields != nullptr) { | |
| _primitive_fields->sort(LayoutRawBlock::compare_size_inverted); | |
| } | |
| } | |
| FieldLayout::FieldLayout(GrowableArray<FieldInfo>* field_info, ConstantPool* cp) : | |
| _field_info(field_info), | |
| _cp(cp), | |
| _blocks(nullptr), | |
| _start(_blocks), | |
| _last(_blocks) {} | |
| void FieldLayout::initialize_static_layout() { | |
| _blocks = new LayoutRawBlock(LayoutRawBlock::EMPTY, INT_MAX); | |
| _blocks->set_offset(0); | |
| _last = _blocks; | |
| _start = _blocks; | |
| // Note: at this stage, InstanceMirrorKlass::offset_of_static_fields() could be zero, because | |
| // during bootstrapping, the size of the java.lang.Class is still not known when layout | |
| // of static field is computed. Field offsets are fixed later when the size is known | |
| // (see java_lang_Class::fixup_mirror()) | |
| if (InstanceMirrorKlass::offset_of_static_fields() > 0) { | |
| insert(first_empty_block(), new LayoutRawBlock(LayoutRawBlock::RESERVED, InstanceMirrorKlass::offset_of_static_fields())); | |
| _blocks->set_offset(0); | |
| } | |
| } | |
| void FieldLayout::initialize_instance_layout(const InstanceKlass* super_klass, bool& super_ends_with_oop) { | |
| if (super_klass == nullptr) { | |
| super_ends_with_oop = false; | |
| _blocks = new LayoutRawBlock(LayoutRawBlock::EMPTY, INT_MAX); | |
| _blocks->set_offset(0); | |
| _last = _blocks; | |
| _start = _blocks; | |
| insert(first_empty_block(), new LayoutRawBlock(LayoutRawBlock::RESERVED, instanceOopDesc::base_offset_in_bytes())); | |
| } else { | |
| bool super_has_instance_fields = false; | |
| reconstruct_layout(super_klass, super_has_instance_fields, super_ends_with_oop); | |
| fill_holes(super_klass); | |
| if (!super_klass->has_contended_annotations() || !super_has_instance_fields) { | |
| _start = _blocks; // start allocating fields from the first empty block | |
| } else { | |
| _start = _last; // append fields at the end of the reconstructed layout | |
| } | |
| } | |
| } | |
| LayoutRawBlock* FieldLayout::first_field_block() { | |
| LayoutRawBlock* block = _start; | |
| while (block->kind() != LayoutRawBlock::INHERITED && block->kind() != LayoutRawBlock::REGULAR | |
| && block->kind() != LayoutRawBlock::FLATTENED && block->kind() != LayoutRawBlock::PADDING) { | |
| block = block->next_block(); | |
| } | |
| return block; | |
| } | |
| // Insert a set of fields into a layout using a best-fit strategy. | |
| // For each field, search for the smallest empty slot able to fit the field | |
| // (satisfying both size and alignment requirements), if none is found, | |
| // add the field at the end of the layout. | |
| // Fields cannot be inserted before the block specified in the "start" argument | |
| void FieldLayout::add(GrowableArray<LayoutRawBlock*>* list, LayoutRawBlock* start) { | |
| if (list == nullptr) return; | |
| if (start == nullptr) start = this->_start; | |
| bool last_search_success = false; | |
| int last_size = 0; | |
| int last_alignment = 0; | |
| for (int i = 0; i < list->length(); i ++) { | |
| LayoutRawBlock* b = list->at(i); | |
| LayoutRawBlock* cursor = nullptr; | |
| LayoutRawBlock* candidate = nullptr; | |
| // if start is the last block, just append the field | |
| if (start == last_block()) { | |
| candidate = last_block(); | |
| } | |
| // Before iterating over the layout to find an empty slot fitting the field's requirements, | |
| // check if the previous field had the same requirements and if the search for a fitting slot | |
| // was successful. If the requirements were the same but the search failed, a new search will | |
| // fail the same way, so just append the field at the of the layout. | |
| else if (b->size() == last_size && b->alignment() == last_alignment && !last_search_success) { | |
| candidate = last_block(); | |
| } else { | |
| // Iterate over the layout to find an empty slot fitting the field's requirements | |
| last_size = b->size(); | |
| last_alignment = b->alignment(); | |
| cursor = last_block()->prev_block(); | |
| assert(cursor != nullptr, "Sanity check"); | |
| last_search_success = true; | |
| while (cursor != start) { | |
| if (cursor->kind() == LayoutRawBlock::EMPTY && cursor->fit(b->size(), b->alignment())) { | |
| if (candidate == nullptr || cursor->size() < candidate->size()) { | |
| candidate = cursor; | |
| } | |
| } | |
| cursor = cursor->prev_block(); | |
| } | |
| if (candidate == nullptr) { | |
| candidate = last_block(); | |
| last_search_success = false; | |
| } | |
| assert(candidate != nullptr, "Candidate must not be null"); | |
| assert(candidate->kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block"); | |
| assert(candidate->fit(b->size(), b->alignment()), "Candidate must be able to store the block"); | |
| } | |
| insert_field_block(candidate, b); | |
| } | |
| } | |
| // Used for classes with hard coded field offsets, insert a field at the specified offset */ | |
| void FieldLayout::add_field_at_offset(LayoutRawBlock* block, int offset, LayoutRawBlock* start) { | |
| assert(block != nullptr, "Sanity check"); | |
| block->set_offset(offset); | |
| if (start == nullptr) { | |
| start = this->_start; | |
| } | |
| LayoutRawBlock* slot = start; | |
| while (slot != nullptr) { | |
| if ((slot->offset() <= block->offset() && (slot->offset() + slot->size()) > block->offset()) || | |
| slot == _last){ | |
| assert(slot->kind() == LayoutRawBlock::EMPTY, "Matching slot must be an empty slot"); | |
| assert(slot->size() >= block->offset() + block->size() ,"Matching slot must be big enough"); | |
| if (slot->offset() < block->offset()) { | |
| int adjustment = block->offset() - slot->offset(); | |
| LayoutRawBlock* adj = new LayoutRawBlock(LayoutRawBlock::EMPTY, adjustment); | |
| insert(slot, adj); | |
| } | |
| insert(slot, block); | |
| if (slot->size() == 0) { | |
| remove(slot); | |
| } | |
| _field_info->adr_at(block->field_index())->set_offset(block->offset()); | |
| return; | |
| } | |
| slot = slot->next_block(); | |
| } | |
| fatal("Should have found a matching slot above, corrupted layout or invalid offset"); | |
| } | |
| // The allocation logic uses a best fit strategy: the set of fields is allocated | |
| // in the first empty slot big enough to contain the whole set ((including padding | |
| // to fit alignment constraints). | |
| void FieldLayout::add_contiguously(GrowableArray<LayoutRawBlock*>* list, LayoutRawBlock* start) { | |
| if (list == nullptr) return; | |
| if (start == nullptr) { | |
| start = _start; | |
| } | |
| // This code assumes that if the first block is well aligned, the following | |
| // blocks would naturally be well aligned (no need for adjustment) | |
| int size = 0; | |
| for (int i = 0; i < list->length(); i++) { | |
| size += list->at(i)->size(); | |
| } | |
| LayoutRawBlock* candidate = nullptr; | |
| if (start == last_block()) { | |
| candidate = last_block(); | |
| } else { | |
| LayoutRawBlock* first = list->at(0); | |
| candidate = last_block()->prev_block(); | |
| while (candidate->kind() != LayoutRawBlock::EMPTY || !candidate->fit(size, first->alignment())) { | |
| if (candidate == start) { | |
| candidate = last_block(); | |
| break; | |
| } | |
| candidate = candidate->prev_block(); | |
| } | |
| assert(candidate != nullptr, "Candidate must not be null"); | |
| assert(candidate->kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block"); | |
| assert(candidate->fit(size, first->alignment()), "Candidate must be able to store the whole contiguous block"); | |
| } | |
| for (int i = 0; i < list->length(); i++) { | |
| LayoutRawBlock* b = list->at(i); | |
| insert_field_block(candidate, b); | |
| assert((candidate->offset() % b->alignment() == 0), "Contiguous blocks must be naturally well aligned"); | |
| } | |
| } | |
| LayoutRawBlock* FieldLayout::insert_field_block(LayoutRawBlock* slot, LayoutRawBlock* block) { | |
| assert(slot->kind() == LayoutRawBlock::EMPTY, "Blocks can only be inserted in empty blocks"); | |
| if (slot->offset() % block->alignment() != 0) { | |
| int adjustment = block->alignment() - (slot->offset() % block->alignment()); | |
| LayoutRawBlock* adj = new LayoutRawBlock(LayoutRawBlock::EMPTY, adjustment); | |
| insert(slot, adj); | |
| } | |
| insert(slot, block); | |
| if (slot->size() == 0) { | |
| remove(slot); | |
| } | |
| _field_info->adr_at(block->field_index())->set_offset(block->offset()); | |
| return block; | |
| } | |
| void FieldLayout::reconstruct_layout(const InstanceKlass* ik, bool& has_instance_fields, bool& ends_with_oop) { | |
| has_instance_fields = ends_with_oop = false; | |
| GrowableArray<LayoutRawBlock*>* all_fields = new GrowableArray<LayoutRawBlock*>(32); | |
| BasicType last_type; | |
| int last_offset = -1; | |
| while (ik != nullptr) { | |
| for (AllFieldStream fs(ik); !fs.done(); fs.next()) { | |
| BasicType type = Signature::basic_type(fs.signature()); | |
| // distinction between static and non-static fields is missing | |
| if (fs.access_flags().is_static()) continue; | |
| has_instance_fields = true; | |
| if (fs.offset() > last_offset) { | |
| last_offset = fs.offset(); | |
| last_type = type; | |
| } | |
| int size = type2aelembytes(type); | |
| // INHERITED blocks are marked as non-reference because oop_maps are handled by their holder class | |
| LayoutRawBlock* block = new LayoutRawBlock(fs.index(), LayoutRawBlock::INHERITED, size, size, false); | |
| block->set_offset(fs.offset()); | |
| all_fields->append(block); | |
| } | |
| ik = ik->super() == nullptr ? nullptr : InstanceKlass::cast(ik->super()); | |
| } | |
| assert(last_offset == -1 || last_offset > 0, "Sanity"); | |
| if (last_offset > 0 && | |
| (last_type == BasicType::T_ARRAY || last_type == BasicType::T_OBJECT)) { | |
| ends_with_oop = true; | |
| } | |
| all_fields->sort(LayoutRawBlock::compare_offset); | |
| _blocks = new LayoutRawBlock(LayoutRawBlock::RESERVED, instanceOopDesc::base_offset_in_bytes()); | |
| _blocks->set_offset(0); | |
| _last = _blocks; | |
| for(int i = 0; i < all_fields->length(); i++) { | |
| LayoutRawBlock* b = all_fields->at(i); | |
| _last->set_next_block(b); | |
| b->set_prev_block(_last); | |
| _last = b; | |
| } | |
| _start = _blocks; | |
| } | |
| // Called during the reconstruction of a layout, after fields from super | |
| // classes have been inserted. It fills unused slots between inserted fields | |
| // with EMPTY blocks, so the regular field insertion methods would work. | |
| // This method handles classes with @Contended annotations differently | |
| // by inserting PADDING blocks instead of EMPTY block to prevent subclasses' | |
| // fields to interfere with contended fields/classes. | |
| void FieldLayout::fill_holes(const InstanceKlass* super_klass) { | |
| assert(_blocks != nullptr, "Sanity check"); | |
| assert(_blocks->offset() == 0, "first block must be at offset zero"); | |
| LayoutRawBlock::Kind filling_type = super_klass->has_contended_annotations() ? LayoutRawBlock::PADDING: LayoutRawBlock::EMPTY; | |
| LayoutRawBlock* b = _blocks; | |
| while (b->next_block() != nullptr) { | |
| if (b->next_block()->offset() > (b->offset() + b->size())) { | |
| int size = b->next_block()->offset() - (b->offset() + b->size()); | |
| LayoutRawBlock* empty = new LayoutRawBlock(filling_type, size); | |
| empty->set_offset(b->offset() + b->size()); | |
| empty->set_next_block(b->next_block()); | |
| b->next_block()->set_prev_block(empty); | |
| b->set_next_block(empty); | |
| empty->set_prev_block(b); | |
| } | |
| b = b->next_block(); | |
| } | |
| assert(b->next_block() == nullptr, "Invariant at this point"); | |
| assert(b->kind() != LayoutRawBlock::EMPTY, "Sanity check"); | |
| // If the super class has @Contended annotation, a padding block is | |
| // inserted at the end to ensure that fields from the subclasses won't share | |
| // the cache line of the last field of the contended class | |
| if (super_klass->has_contended_annotations() && ContendedPaddingWidth > 0) { | |
| LayoutRawBlock* p = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth); | |
| p->set_offset(b->offset() + b->size()); | |
| b->set_next_block(p); | |
| p->set_prev_block(b); | |
| b = p; | |
| } | |
| LayoutRawBlock* last = new LayoutRawBlock(LayoutRawBlock::EMPTY, INT_MAX); | |
| last->set_offset(b->offset() + b->size()); | |
| assert(last->offset() > 0, "Sanity check"); | |
| b->set_next_block(last); | |
| last->set_prev_block(b); | |
| _last = last; | |
| } | |
| LayoutRawBlock* FieldLayout::insert(LayoutRawBlock* slot, LayoutRawBlock* block) { | |
| assert(slot->kind() == LayoutRawBlock::EMPTY, "Blocks can only be inserted in empty blocks"); | |
| assert(slot->offset() % block->alignment() == 0, "Incompatible alignment"); | |
| block->set_offset(slot->offset()); | |
| slot->set_offset(slot->offset() + block->size()); | |
| assert((slot->size() - block->size()) < slot->size(), "underflow checking"); | |
| assert(slot->size() - block->size() >= 0, "no negative size allowed"); | |
| slot->set_size(slot->size() - block->size()); | |
| block->set_prev_block(slot->prev_block()); | |
| block->set_next_block(slot); | |
| slot->set_prev_block(block); | |
| if (block->prev_block() != nullptr) { | |
| block->prev_block()->set_next_block(block); | |
| } | |
| if (_blocks == slot) { | |
| _blocks = block; | |
| } | |
| return block; | |
| } | |
| void FieldLayout::remove(LayoutRawBlock* block) { | |
| assert(block != nullptr, "Sanity check"); | |
| assert(block != _last, "Sanity check"); | |
| if (_blocks == block) { | |
| _blocks = block->next_block(); | |
| if (_blocks != nullptr) { | |
| _blocks->set_prev_block(nullptr); | |
| } | |
| } else { | |
| assert(block->prev_block() != nullptr, "_prev should be set for non-head blocks"); | |
| block->prev_block()->set_next_block(block->next_block()); | |
| block->next_block()->set_prev_block(block->prev_block()); | |
| } | |
| if (block == _start) { | |
| _start = block->prev_block(); | |
| } | |
| } | |
| void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlass* super) { | |
| ResourceMark rm; | |
| LayoutRawBlock* b = _blocks; | |
| while(b != _last) { | |
| switch(b->kind()) { | |
| case LayoutRawBlock::REGULAR: { | |
| FieldInfo* fi = _field_info->adr_at(b->field_index()); | |
| output->print_cr(" @%d \"%s\" %s %d/%d %s", | |
| b->offset(), | |
| fi->name(_cp)->as_C_string(), | |
| fi->signature(_cp)->as_C_string(), | |
| b->size(), | |
| b->alignment(), | |
| "REGULAR"); | |
| break; | |
| } | |
| case LayoutRawBlock::FLATTENED: { | |
| FieldInfo* fi = _field_info->adr_at(b->field_index()); | |
| output->print_cr(" @%d \"%s\" %s %d/%d %s", | |
| b->offset(), | |
| fi->name(_cp)->as_C_string(), | |
| fi->signature(_cp)->as_C_string(), | |
| b->size(), | |
| b->alignment(), | |
| "FLATTENED"); | |
| break; | |
| } | |
| case LayoutRawBlock::RESERVED: { | |
| output->print_cr(" @%d %d/- %s", | |
| b->offset(), | |
| b->size(), | |
| "RESERVED"); | |
| break; | |
| } | |
| case LayoutRawBlock::INHERITED: { | |
| assert(!is_static, "Static fields are not inherited in layouts"); | |
| assert(super != nullptr, "super klass must be provided to retrieve inherited fields info"); | |
| bool found = false; | |
| const InstanceKlass* ik = super; | |
| while (!found && ik != nullptr) { | |
| for (AllFieldStream fs(ik); !fs.done(); fs.next()) { | |
| if (fs.offset() == b->offset()) { | |
| output->print_cr(" @%d \"%s\" %s %d/%d %s", | |
| b->offset(), | |
| fs.name()->as_C_string(), | |
| fs.signature()->as_C_string(), | |
| b->size(), | |
| b->size(), // so far, alignment constraint == size, will change with Valhalla | |
| "INHERITED"); | |
| found = true; | |
| break; | |
| } | |
| } | |
| ik = ik->java_super(); | |
| } | |
| break; | |
| } | |
| case LayoutRawBlock::EMPTY: | |
| output->print_cr(" @%d %d/1 %s", | |
| b->offset(), | |
| b->size(), | |
| "EMPTY"); | |
| break; | |
| case LayoutRawBlock::PADDING: | |
| output->print_cr(" @%d %d/1 %s", | |
| b->offset(), | |
| b->size(), | |
| "PADDING"); | |
| break; | |
| } | |
| b = b->next_block(); | |
| } | |
| } | |
| FieldLayoutBuilder::FieldLayoutBuilder(const Symbol* classname, const InstanceKlass* super_klass, ConstantPool* constant_pool, | |
| GrowableArray<FieldInfo>* field_info, bool is_contended, FieldLayoutInfo* info) : | |
| _classname(classname), | |
| _super_klass(super_klass), | |
| _constant_pool(constant_pool), | |
| _field_info(field_info), | |
| _info(info), | |
| _root_group(nullptr), | |
| _contended_groups(GrowableArray<FieldGroup*>(8)), | |
| _static_fields(nullptr), | |
| _layout(nullptr), | |
| _static_layout(nullptr), | |
| _nonstatic_oopmap_count(0), | |
| _alignment(-1), | |
| _has_nonstatic_fields(false), | |
| _is_contended(is_contended) {} | |
| FieldGroup* FieldLayoutBuilder::get_or_create_contended_group(int g) { | |
| assert(g > 0, "must only be called for named contended groups"); | |
| FieldGroup* fg = nullptr; | |
| for (int i = 0; i < _contended_groups.length(); i++) { | |
| fg = _contended_groups.at(i); | |
| if (fg->contended_group() == g) return fg; | |
| } | |
| fg = new FieldGroup(g); | |
| _contended_groups.append(fg); | |
| return fg; | |
| } | |
| void FieldLayoutBuilder::prologue() { | |
| _layout = new FieldLayout(_field_info, _constant_pool); | |
| const InstanceKlass* super_klass = _super_klass; | |
| _layout->initialize_instance_layout(super_klass, _super_ends_with_oop); | |
| if (super_klass != nullptr) { | |
| _has_nonstatic_fields = super_klass->has_nonstatic_fields(); | |
| } | |
| _static_layout = new FieldLayout(_field_info, _constant_pool); | |
| _static_layout->initialize_static_layout(); | |
| _static_fields = new FieldGroup(); | |
| _root_group = new FieldGroup(); | |
| } | |
| // Field sorting for regular classes: | |
| // - fields are sorted in static and non-static fields | |
| // - non-static fields are also sorted according to their contention group | |
| // (support of the @Contended annotation) | |
| // - @Contended annotation is ignored for static fields | |
| void FieldLayoutBuilder::regular_field_sorting() { | |
| int idx = 0; | |
| for (GrowableArrayIterator<FieldInfo> it = _field_info->begin(); it != _field_info->end(); ++it, ++idx) { | |
| FieldInfo ctrl = _field_info->at(0); | |
| FieldGroup* group = nullptr; | |
| FieldInfo fieldinfo = *it; | |
| if (fieldinfo.access_flags().is_static()) { | |
| group = _static_fields; | |
| } else { | |
| _has_nonstatic_fields = true; | |
| if (fieldinfo.field_flags().is_contended()) { | |
| int g = fieldinfo.contended_group(); | |
| if (g == 0) { | |
| group = new FieldGroup(true); | |
| _contended_groups.append(group); | |
| } else { | |
| group = get_or_create_contended_group(g); | |
| } | |
| } else { | |
| group = _root_group; | |
| } | |
| } | |
| assert(group != nullptr, "invariant"); | |
| BasicType type = Signature::basic_type(fieldinfo.signature(_constant_pool)); | |
| switch(type) { | |
| case T_BYTE: | |
| case T_CHAR: | |
| case T_DOUBLE: | |
| case T_FLOAT: | |
| case T_INT: | |
| case T_LONG: | |
| case T_SHORT: | |
| case T_BOOLEAN: | |
| group->add_primitive_field(idx, type); | |
| break; | |
| case T_OBJECT: | |
| case T_ARRAY: | |
| if (group != _static_fields) _nonstatic_oopmap_count++; | |
| group->add_oop_field(idx); | |
| break; | |
| default: | |
| fatal("Something wrong?"); | |
| } | |
| } | |
| _root_group->sort_by_size(); | |
| _static_fields->sort_by_size(); | |
| if (!_contended_groups.is_empty()) { | |
| for (int i = 0; i < _contended_groups.length(); i++) { | |
| _contended_groups.at(i)->sort_by_size(); | |
| } | |
| } | |
| } | |
| void FieldLayoutBuilder::insert_contended_padding(LayoutRawBlock* slot) { | |
| if (ContendedPaddingWidth > 0) { | |
| LayoutRawBlock* padding = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth); | |
| _layout->insert(slot, padding); | |
| } | |
| } | |
| // Computation of regular classes layout is an evolution of the previous default layout | |
| // (FieldAllocationStyle 1): | |
| // - primitive fields are allocated first (from the biggest to the smallest) | |
| // - oop fields are allocated, either in existing gaps or at the end of | |
| // the layout. We allocate oops in a single block to have a single oop map entry. | |
| // - if the super class ended with an oop, we lead with oops. That will cause the | |
| // trailing oop map entry of the super class and the oop map entry of this class | |
| // to be folded into a single entry later. Correspondingly, if the super class | |
| // ends with a primitive field, we gain nothing by leading with oops; therefore | |
| // we let oop fields trail, thus giving future derived classes the chance to apply | |
| // the same trick. | |
| void FieldLayoutBuilder::compute_regular_layout() { | |
| bool need_tail_padding = false; | |
| prologue(); | |
| regular_field_sorting(); | |
| if (_is_contended) { | |
| _layout->set_start(_layout->last_block()); | |
| // insertion is currently easy because the current strategy doesn't try to fill holes | |
| // in super classes layouts => the _start block is by consequence the _last_block | |
| insert_contended_padding(_layout->start()); | |
| need_tail_padding = true; | |
| } | |
| if (_super_ends_with_oop) { | |
| _layout->add(_root_group->oop_fields()); | |
| _layout->add(_root_group->primitive_fields()); | |
| } else { | |
| _layout->add(_root_group->primitive_fields()); | |
| _layout->add(_root_group->oop_fields()); | |
| } | |
| if (!_contended_groups.is_empty()) { | |
| for (int i = 0; i < _contended_groups.length(); i++) { | |
| FieldGroup* cg = _contended_groups.at(i); | |
| LayoutRawBlock* start = _layout->last_block(); | |
| insert_contended_padding(start); | |
| _layout->add(cg->primitive_fields(), start); | |
| _layout->add(cg->oop_fields(), start); | |
| need_tail_padding = true; | |
| } | |
| } | |
| if (need_tail_padding) { | |
| insert_contended_padding(_layout->last_block()); | |
| } | |
| _static_layout->add_contiguously(this->_static_fields->oop_fields()); | |
| _static_layout->add(this->_static_fields->primitive_fields()); | |
| epilogue(); | |
| } | |
| void FieldLayoutBuilder::epilogue() { | |
| // Computing oopmaps | |
| int super_oop_map_count = (_super_klass == nullptr) ? 0 :_super_klass->nonstatic_oop_map_count(); | |
| int max_oop_map_count = super_oop_map_count + _nonstatic_oopmap_count; | |
| OopMapBlocksBuilder* nonstatic_oop_maps = | |
| new OopMapBlocksBuilder(max_oop_map_count); | |
| if (super_oop_map_count > 0) { | |
| nonstatic_oop_maps->initialize_inherited_blocks(_super_klass->start_of_nonstatic_oop_maps(), | |
| _super_klass->nonstatic_oop_map_count()); | |
| } | |
| if (_root_group->oop_fields() != nullptr) { | |
| for (int i = 0; i < _root_group->oop_fields()->length(); i++) { | |
| LayoutRawBlock* b = _root_group->oop_fields()->at(i); | |
| nonstatic_oop_maps->add(b->offset(), 1); | |
| } | |
| } | |
| if (!_contended_groups.is_empty()) { | |
| for (int i = 0; i < _contended_groups.length(); i++) { | |
| FieldGroup* cg = _contended_groups.at(i); | |
| if (cg->oop_count() > 0) { | |
| assert(cg->oop_fields() != nullptr && cg->oop_fields()->at(0) != nullptr, "oop_count > 0 but no oop fields found"); | |
| nonstatic_oop_maps->add(cg->oop_fields()->at(0)->offset(), cg->oop_count()); | |
| } | |
| } | |
| } | |
| nonstatic_oop_maps->compact(); | |
| int instance_end = align_up(_layout->last_block()->offset(), wordSize); | |
| int static_fields_end = align_up(_static_layout->last_block()->offset(), wordSize); | |
| int static_fields_size = (static_fields_end - | |
| InstanceMirrorKlass::offset_of_static_fields()) / wordSize; | |
| int nonstatic_field_end = align_up(_layout->last_block()->offset(), heapOopSize); | |
| // Pass back information needed for InstanceKlass creation | |
| _info->oop_map_blocks = nonstatic_oop_maps; | |
| _info->_instance_size = align_object_size(instance_end / wordSize); | |
| _info->_static_field_size = static_fields_size; | |
| _info->_nonstatic_field_size = (nonstatic_field_end - instanceOopDesc::base_offset_in_bytes()) / heapOopSize; | |
| _info->_has_nonstatic_fields = _has_nonstatic_fields; | |
| if (PrintFieldLayout) { | |
| ResourceMark rm; | |
| tty->print_cr("Layout of class %s", _classname->as_C_string()); | |
| tty->print_cr("Instance fields:"); | |
| _layout->print(tty, false, _super_klass); | |
| tty->print_cr("Static fields:"); | |
| _static_layout->print(tty, true, nullptr); | |
| tty->print_cr("Instance size = %d bytes", _info->_instance_size * wordSize); | |
| tty->print_cr("---"); | |
| } | |
| } | |
| void FieldLayoutBuilder::build_layout() { | |
| compute_regular_layout(); | |
| } |