8352075: Perf regression accessing fields
Reviewed-by: coleenp, iklam, jsjolen
This commit is contained in:
parent
e5ce5c57c8
commit
e18277b470
@ -3740,6 +3740,7 @@ void ClassFileParser::apply_parsed_class_metadata(
|
|||||||
_cp->set_pool_holder(this_klass);
|
_cp->set_pool_holder(this_klass);
|
||||||
this_klass->set_constants(_cp);
|
this_klass->set_constants(_cp);
|
||||||
this_klass->set_fieldinfo_stream(_fieldinfo_stream);
|
this_klass->set_fieldinfo_stream(_fieldinfo_stream);
|
||||||
|
this_klass->set_fieldinfo_search_table(_fieldinfo_search_table);
|
||||||
this_klass->set_fields_status(_fields_status);
|
this_klass->set_fields_status(_fields_status);
|
||||||
this_klass->set_methods(_methods);
|
this_klass->set_methods(_methods);
|
||||||
this_klass->set_inner_classes(_inner_classes);
|
this_klass->set_inner_classes(_inner_classes);
|
||||||
@ -3749,6 +3750,8 @@ void ClassFileParser::apply_parsed_class_metadata(
|
|||||||
this_klass->set_permitted_subclasses(_permitted_subclasses);
|
this_klass->set_permitted_subclasses(_permitted_subclasses);
|
||||||
this_klass->set_record_components(_record_components);
|
this_klass->set_record_components(_record_components);
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(_cp, _fieldinfo_stream, _fieldinfo_search_table));
|
||||||
|
|
||||||
// Delay the setting of _local_interfaces and _transitive_interfaces until after
|
// Delay the setting of _local_interfaces and _transitive_interfaces until after
|
||||||
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
|
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
|
||||||
// be shared with _transitive_interfaces and _transitive_interfaces may be shared with
|
// be shared with _transitive_interfaces and _transitive_interfaces may be shared with
|
||||||
@ -5056,6 +5059,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
|
|||||||
// note that is not safe to use the fields in the parser from this point on
|
// note that is not safe to use the fields in the parser from this point on
|
||||||
assert(nullptr == _cp, "invariant");
|
assert(nullptr == _cp, "invariant");
|
||||||
assert(nullptr == _fieldinfo_stream, "invariant");
|
assert(nullptr == _fieldinfo_stream, "invariant");
|
||||||
|
assert(nullptr == _fieldinfo_search_table, "invariant");
|
||||||
assert(nullptr == _fields_status, "invariant");
|
assert(nullptr == _fields_status, "invariant");
|
||||||
assert(nullptr == _methods, "invariant");
|
assert(nullptr == _methods, "invariant");
|
||||||
assert(nullptr == _inner_classes, "invariant");
|
assert(nullptr == _inner_classes, "invariant");
|
||||||
@ -5276,6 +5280,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
|
|||||||
_super_klass(),
|
_super_klass(),
|
||||||
_cp(nullptr),
|
_cp(nullptr),
|
||||||
_fieldinfo_stream(nullptr),
|
_fieldinfo_stream(nullptr),
|
||||||
|
_fieldinfo_search_table(nullptr),
|
||||||
_fields_status(nullptr),
|
_fields_status(nullptr),
|
||||||
_methods(nullptr),
|
_methods(nullptr),
|
||||||
_inner_classes(nullptr),
|
_inner_classes(nullptr),
|
||||||
@ -5352,6 +5357,7 @@ void ClassFileParser::clear_class_metadata() {
|
|||||||
// deallocated if classfile parsing returns an error.
|
// deallocated if classfile parsing returns an error.
|
||||||
_cp = nullptr;
|
_cp = nullptr;
|
||||||
_fieldinfo_stream = nullptr;
|
_fieldinfo_stream = nullptr;
|
||||||
|
_fieldinfo_search_table = nullptr;
|
||||||
_fields_status = nullptr;
|
_fields_status = nullptr;
|
||||||
_methods = nullptr;
|
_methods = nullptr;
|
||||||
_inner_classes = nullptr;
|
_inner_classes = nullptr;
|
||||||
@ -5374,6 +5380,7 @@ ClassFileParser::~ClassFileParser() {
|
|||||||
if (_fieldinfo_stream != nullptr) {
|
if (_fieldinfo_stream != nullptr) {
|
||||||
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_stream);
|
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_stream);
|
||||||
}
|
}
|
||||||
|
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_search_table);
|
||||||
|
|
||||||
if (_fields_status != nullptr) {
|
if (_fields_status != nullptr) {
|
||||||
MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status);
|
MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status);
|
||||||
@ -5774,6 +5781,7 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st
|
|||||||
_fieldinfo_stream =
|
_fieldinfo_stream =
|
||||||
FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
|
FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
|
||||||
injected_fields_count, loader_data(), CHECK);
|
injected_fields_count, loader_data(), CHECK);
|
||||||
|
_fieldinfo_search_table = FieldInfoStream::create_search_table(_cp, _fieldinfo_stream, _loader_data, CHECK);
|
||||||
_fields_status =
|
_fields_status =
|
||||||
MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(),
|
MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(),
|
||||||
FieldStatus(0), CHECK);
|
FieldStatus(0), CHECK);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -123,6 +123,7 @@ class ClassFileParser {
|
|||||||
const InstanceKlass* _super_klass;
|
const InstanceKlass* _super_klass;
|
||||||
ConstantPool* _cp;
|
ConstantPool* _cp;
|
||||||
Array<u1>* _fieldinfo_stream;
|
Array<u1>* _fieldinfo_stream;
|
||||||
|
Array<u1>* _fieldinfo_search_table;
|
||||||
Array<FieldStatus>* _fields_status;
|
Array<FieldStatus>* _fields_status;
|
||||||
Array<Method*>* _methods;
|
Array<Method*>* _methods;
|
||||||
Array<u2>* _inner_classes;
|
Array<u2>* _inner_classes;
|
||||||
|
@ -301,7 +301,7 @@ void FieldLayout::reconstruct_layout(const InstanceKlass* ik, bool& has_instance
|
|||||||
BasicType last_type;
|
BasicType last_type;
|
||||||
int last_offset = -1;
|
int last_offset = -1;
|
||||||
while (ik != nullptr) {
|
while (ik != nullptr) {
|
||||||
for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
|
for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
|
||||||
BasicType type = Signature::basic_type(fs.signature());
|
BasicType type = Signature::basic_type(fs.signature());
|
||||||
// distinction between static and non-static fields is missing
|
// distinction between static and non-static fields is missing
|
||||||
if (fs.access_flags().is_static()) continue;
|
if (fs.access_flags().is_static()) continue;
|
||||||
@ -461,7 +461,7 @@ void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlas
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
const InstanceKlass* ik = super;
|
const InstanceKlass* ik = super;
|
||||||
while (!found && ik != nullptr) {
|
while (!found && ik != nullptr) {
|
||||||
for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
|
for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
|
||||||
if (fs.offset() == b->offset()) {
|
if (fs.offset() == b->offset()) {
|
||||||
output->print_cr(" @%d \"%s\" %s %d/%d %s",
|
output->print_cr(" @%d \"%s\" %s %d/%d %s",
|
||||||
b->offset(),
|
b->offset(),
|
||||||
|
@ -967,6 +967,13 @@ void java_lang_Class::fixup_mirror(Klass* k, TRAPS) {
|
|||||||
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, k->class_loader_data(), CHECK);
|
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, k->class_loader_data(), CHECK);
|
||||||
ik->set_fieldinfo_stream(new_fis);
|
ik->set_fieldinfo_stream(new_fis);
|
||||||
MetadataFactory::free_array<u1>(k->class_loader_data(), old_stream);
|
MetadataFactory::free_array<u1>(k->class_loader_data(), old_stream);
|
||||||
|
|
||||||
|
Array<u1>* old_table = ik->fieldinfo_search_table();
|
||||||
|
Array<u1>* search_table = FieldInfoStream::create_search_table(ik->constants(), new_fis, k->class_loader_data(), CHECK);
|
||||||
|
ik->set_fieldinfo_search_table(search_table);
|
||||||
|
MetadataFactory::free_array<u1>(k->class_loader_data(), old_table);
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(ik->constants(), new_fis, search_table));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,8 +22,11 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "oops/fieldInfo.inline.hpp"
|
#include "oops/fieldInfo.inline.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
|
#include "utilities/packedTable.hpp"
|
||||||
|
|
||||||
void FieldInfo::print(outputStream* os, ConstantPool* cp) {
|
void FieldInfo::print(outputStream* os, ConstantPool* cp) {
|
||||||
os->print_cr("index=%d name_index=%d name=%s signature_index=%d signature=%s offset=%d "
|
os->print_cr("index=%d name_index=%d name=%s signature_index=%d signature=%s offset=%d "
|
||||||
@ -37,8 +40,10 @@ void FieldInfo::print(outputStream* os, ConstantPool* cp) {
|
|||||||
field_flags().as_uint(),
|
field_flags().as_uint(),
|
||||||
initializer_index(),
|
initializer_index(),
|
||||||
generic_signature_index(),
|
generic_signature_index(),
|
||||||
_field_flags.is_injected() ? lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8(),
|
_field_flags.is_generic() ? (_field_flags.is_injected() ?
|
||||||
contended_group());
|
lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8()
|
||||||
|
) : "",
|
||||||
|
is_contended() ? contended_group() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldInfo::print_from_growable_array(outputStream* os, GrowableArray<FieldInfo>* array, ConstantPool* cp) {
|
void FieldInfo::print_from_growable_array(outputStream* os, GrowableArray<FieldInfo>* array, ConstantPool* cp) {
|
||||||
@ -62,13 +67,17 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
|
|||||||
StreamSizer s;
|
StreamSizer s;
|
||||||
StreamFieldSizer sizer(&s);
|
StreamFieldSizer sizer(&s);
|
||||||
|
|
||||||
|
assert(fields->length() == java_fields + injected_fields, "must be");
|
||||||
|
|
||||||
sizer.consumer()->accept_uint(java_fields);
|
sizer.consumer()->accept_uint(java_fields);
|
||||||
sizer.consumer()->accept_uint(injected_fields);
|
sizer.consumer()->accept_uint(injected_fields);
|
||||||
for (int i = 0; i < fields->length(); i++) {
|
for (int i = 0; i < fields->length(); i++) {
|
||||||
FieldInfo* fi = fields->adr_at(i);
|
FieldInfo* fi = fields->adr_at(i);
|
||||||
sizer.map_field_info(*fi);
|
sizer.map_field_info(*fi);
|
||||||
}
|
}
|
||||||
int storage_size = sizer.consumer()->position() + 1;
|
// Originally there was an extra byte with 0 terminating the reading;
|
||||||
|
// now we check limits instead.
|
||||||
|
int storage_size = sizer.consumer()->position();
|
||||||
Array<u1>* const fis = MetadataFactory::new_array<u1>(loader_data, storage_size, CHECK_NULL);
|
Array<u1>* const fis = MetadataFactory::new_array<u1>(loader_data, storage_size, CHECK_NULL);
|
||||||
|
|
||||||
using StreamWriter = UNSIGNED5::Writer<Array<u1>*, int, ArrayHelper<Array<u1>*, int>>;
|
using StreamWriter = UNSIGNED5::Writer<Array<u1>*, int, ArrayHelper<Array<u1>*, int>>;
|
||||||
@ -79,15 +88,14 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
|
|||||||
writer.consumer()->accept_uint(java_fields);
|
writer.consumer()->accept_uint(java_fields);
|
||||||
writer.consumer()->accept_uint(injected_fields);
|
writer.consumer()->accept_uint(injected_fields);
|
||||||
for (int i = 0; i < fields->length(); i++) {
|
for (int i = 0; i < fields->length(); i++) {
|
||||||
FieldInfo* fi = fields->adr_at(i);
|
writer.map_field_info(fields->at(i));
|
||||||
writer.map_field_info(*fi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
FieldInfoReader r(fis);
|
FieldInfoReader r(fis);
|
||||||
int jfc = r.next_uint();
|
int jfc, ifc;
|
||||||
|
r.read_field_counts(&jfc, &ifc);
|
||||||
assert(jfc == java_fields, "Must be");
|
assert(jfc == java_fields, "Must be");
|
||||||
int ifc = r.next_uint();
|
|
||||||
assert(ifc == injected_fields, "Must be");
|
assert(ifc == injected_fields, "Must be");
|
||||||
for (int i = 0; i < jfc + ifc; i++) {
|
for (int i = 0; i < jfc + ifc; i++) {
|
||||||
FieldInfo fi;
|
FieldInfo fi;
|
||||||
@ -113,30 +121,221 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
|
|||||||
return fis;
|
return fis;
|
||||||
}
|
}
|
||||||
|
|
||||||
GrowableArray<FieldInfo>* FieldInfoStream::create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count) {
|
int FieldInfoStream::compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2) {
|
||||||
int length = FieldInfoStream::num_total_fields(fis);
|
int cmp = n1->fast_compare(n2);
|
||||||
GrowableArray<FieldInfo>* array = new GrowableArray<FieldInfo>(length);
|
return cmp != 0 ? cmp : s1->fast_compare(s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// We use both name and signature during the comparison; while JLS require unique
|
||||||
|
// names for fields, JVMS requires only unique name + signature combination.
|
||||||
|
struct field_pos {
|
||||||
|
Symbol* _name;
|
||||||
|
Symbol* _signature;
|
||||||
|
int _index;
|
||||||
|
int _position;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FieldInfoSupplier: public PackedTableBuilder::Supplier {
|
||||||
|
const field_pos* _positions;
|
||||||
|
size_t _elements;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FieldInfoSupplier(const field_pos* positions, size_t elements): _positions(positions), _elements(elements) {}
|
||||||
|
|
||||||
|
bool next(uint32_t* key, uint32_t* value) override {
|
||||||
|
if (_elements == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*key = _positions->_position;
|
||||||
|
*value = _positions->_index;
|
||||||
|
++_positions;
|
||||||
|
--_elements;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Array<u1>* FieldInfoStream::create_search_table(ConstantPool* cp, const Array<u1>* fis, ClassLoaderData* loader_data, TRAPS) {
|
||||||
|
if (CDSConfig::is_dumping_dynamic_archive()) {
|
||||||
|
// We cannot use search table; in case of dynamic archives it should be sorted by "requested" addresses,
|
||||||
|
// but Symbol* addresses are coming from _constants, which has "buffered" addresses.
|
||||||
|
// For background, see new comments inside allocate_node_impl in symbolTable.cpp
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
FieldInfoReader r(fis);
|
FieldInfoReader r(fis);
|
||||||
*java_fields_count = r.next_uint();
|
int java_fields;
|
||||||
*injected_fields_count = r.next_uint();
|
int injected_fields;
|
||||||
|
r.read_field_counts(&java_fields, &injected_fields);
|
||||||
|
assert(java_fields >= 0, "must be");
|
||||||
|
if (java_fields == 0 || fis->length() == 0 || static_cast<uint>(java_fields) < BinarySearchThreshold) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceMark rm;
|
||||||
|
field_pos* positions = NEW_RESOURCE_ARRAY(field_pos, java_fields);
|
||||||
|
for (int i = 0; i < java_fields; ++i) {
|
||||||
|
assert(r.has_next(), "number of fields must match");
|
||||||
|
|
||||||
|
positions[i]._position = r.position();
|
||||||
|
FieldInfo fi;
|
||||||
|
r.read_field_info(fi);
|
||||||
|
|
||||||
|
positions[i]._name = fi.name(cp);
|
||||||
|
positions[i]._signature = fi.signature(cp);
|
||||||
|
positions[i]._index = i;
|
||||||
|
}
|
||||||
|
auto compare_pair = [](const void* v1, const void* v2) {
|
||||||
|
const field_pos* p1 = reinterpret_cast<const field_pos*>(v1);
|
||||||
|
const field_pos* p2 = reinterpret_cast<const field_pos*>(v2);
|
||||||
|
return compare_name_and_sig(p1->_name, p1->_signature, p2->_name, p2->_signature);
|
||||||
|
};
|
||||||
|
qsort(positions, java_fields, sizeof(field_pos), compare_pair);
|
||||||
|
|
||||||
|
PackedTableBuilder builder(fis->length() - 1, java_fields - 1);
|
||||||
|
Array<u1>* table = MetadataFactory::new_array<u1>(loader_data, java_fields * builder.element_bytes(), CHECK_NULL);
|
||||||
|
FieldInfoSupplier supplier(positions, java_fields);
|
||||||
|
builder.fill(table->data(), static_cast<size_t>(table->length()), supplier);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrowableArray<FieldInfo>* FieldInfoStream::create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count) {
|
||||||
|
FieldInfoReader r(fis);
|
||||||
|
r.read_field_counts(java_fields_count, injected_fields_count);
|
||||||
|
int length = *java_fields_count + *injected_fields_count;
|
||||||
|
|
||||||
|
GrowableArray<FieldInfo>* array = new GrowableArray<FieldInfo>(length);
|
||||||
while (r.has_next()) {
|
while (r.has_next()) {
|
||||||
FieldInfo fi;
|
FieldInfo fi;
|
||||||
r.read_field_info(fi);
|
r.read_field_info(fi);
|
||||||
array->append(fi);
|
array->append(fi);
|
||||||
}
|
}
|
||||||
assert(array->length() == length, "Must be");
|
assert(array->length() == length, "Must be");
|
||||||
assert(array->length() == *java_fields_count + *injected_fields_count, "Must be");
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldInfoStream::print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp) {
|
void FieldInfoStream::print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp) {
|
||||||
int length = FieldInfoStream::num_total_fields(fis);
|
|
||||||
FieldInfoReader r(fis);
|
FieldInfoReader r(fis);
|
||||||
int java_field_count = r.next_uint();
|
int java_fields_count;
|
||||||
int injected_fields_count = r.next_uint();
|
int injected_fields_count;
|
||||||
|
r.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
while (r.has_next()) {
|
while (r.has_next()) {
|
||||||
FieldInfo fi;
|
FieldInfo fi;
|
||||||
r.read_field_info(fi);
|
r.read_field_info(fi);
|
||||||
fi.print(os, cp);
|
fi.print(os, cp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FieldInfoComparator: public PackedTableLookup::Comparator {
|
||||||
|
const FieldInfoReader* _reader;
|
||||||
|
ConstantPool* _cp;
|
||||||
|
const Symbol* _name;
|
||||||
|
const Symbol* _signature;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FieldInfoComparator(const FieldInfoReader* reader, ConstantPool* cp, const Symbol* name, const Symbol* signature):
|
||||||
|
_reader(reader), _cp(cp), _name(name), _signature(signature) {}
|
||||||
|
|
||||||
|
int compare_to(uint32_t position) override {
|
||||||
|
FieldInfoReader r2(*_reader);
|
||||||
|
r2.set_position_and_next_index(position, -1);
|
||||||
|
u2 name_index, sig_index;
|
||||||
|
r2.read_name_and_signature(&name_index, &sig_index);
|
||||||
|
Symbol* mid_name = _cp->symbol_at(name_index);
|
||||||
|
Symbol* mid_sig = _cp->symbol_at(sig_index);
|
||||||
|
|
||||||
|
return FieldInfoStream::compare_name_and_sig(_name, _signature, mid_name, mid_sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
void reset(uint32_t position) override {
|
||||||
|
FieldInfoReader r2(*_reader);
|
||||||
|
r2.set_position_and_next_index(position, -1);
|
||||||
|
u2 name_index, signature_index;
|
||||||
|
r2.read_name_and_signature(&name_index, &signature_index);
|
||||||
|
_name = _cp->symbol_at(name_index);
|
||||||
|
_signature = _cp->symbol_at(signature_index);
|
||||||
|
}
|
||||||
|
#endif // ASSERT
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
void FieldInfoStream::validate_search_table(ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table) {
|
||||||
|
if (search_table == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FieldInfoReader reader(fis);
|
||||||
|
int java_fields, injected_fields;
|
||||||
|
reader.read_field_counts(&java_fields, &injected_fields);
|
||||||
|
assert(java_fields > 0, "must be");
|
||||||
|
|
||||||
|
PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
|
||||||
|
assert(lookup.element_bytes() * java_fields == static_cast<unsigned int>(search_table->length()), "size does not match");
|
||||||
|
|
||||||
|
FieldInfoComparator comparator(&reader, cp, nullptr, nullptr);
|
||||||
|
// Check 1: assert that elements have the correct order based on the comparison function
|
||||||
|
lookup.validate_order(comparator);
|
||||||
|
|
||||||
|
// Check 2: Iterate through the original stream (not just search_table) and try if lookup works as expected
|
||||||
|
reader.set_position_and_next_index(0, 0);
|
||||||
|
reader.read_field_counts(&java_fields, &injected_fields);
|
||||||
|
while (reader.has_next()) {
|
||||||
|
int field_start = reader.position();
|
||||||
|
FieldInfo fi;
|
||||||
|
reader.read_field_info(fi);
|
||||||
|
if (fi.field_flags().is_injected()) {
|
||||||
|
// checking only java fields that precede injected ones
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldInfoReader r2(fis);
|
||||||
|
int index = r2.search_table_lookup(search_table, fi.name(cp), fi.signature(cp), cp, java_fields);
|
||||||
|
assert(index == static_cast<int>(fi.index()), "wrong index: %d != %u", index, fi.index());
|
||||||
|
assert(index == r2.next_index(), "index should match");
|
||||||
|
assert(field_start == r2.position(), "must find the same position");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ASSERT
|
||||||
|
|
||||||
|
void FieldInfoStream::print_search_table(outputStream* st, ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table) {
|
||||||
|
if (search_table == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FieldInfoReader reader(fis);
|
||||||
|
int java_fields, injected_fields;
|
||||||
|
reader.read_field_counts(&java_fields, &injected_fields);
|
||||||
|
assert(java_fields > 0, "must be");
|
||||||
|
PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
|
||||||
|
auto printer = [&] (size_t offset, uint32_t position, uint32_t index) {
|
||||||
|
reader.set_position_and_next_index(position, -1);
|
||||||
|
u2 name_index, sig_index;
|
||||||
|
reader.read_name_and_signature(&name_index, &sig_index);
|
||||||
|
Symbol* name = cp->symbol_at(name_index);
|
||||||
|
Symbol* sig = cp->symbol_at(sig_index);
|
||||||
|
st->print(" [%zu] #%d,#%d = ", offset, name_index, sig_index);
|
||||||
|
name->print_symbol_on(st);
|
||||||
|
st->print(":");
|
||||||
|
sig->print_symbol_on(st);
|
||||||
|
st->print(" @ %p,%p", name, sig);
|
||||||
|
st->cr();
|
||||||
|
};
|
||||||
|
|
||||||
|
lookup.iterate(printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FieldInfoReader::search_table_lookup(const Array<u1>* search_table, const Symbol* name, const Symbol* signature, ConstantPool* cp, int java_fields) {
|
||||||
|
assert(java_fields >= 0, "must be");
|
||||||
|
if (java_fields == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
FieldInfoComparator comp(this, cp, name, signature);
|
||||||
|
PackedTableLookup lookup(_r.limit() - 1, java_fields - 1, search_table);
|
||||||
|
uint32_t position;
|
||||||
|
static_assert(sizeof(uint32_t) == sizeof(_next_index), "field size assert");
|
||||||
|
if (lookup.search(comp, &position, reinterpret_cast<uint32_t*>(&_next_index))) {
|
||||||
|
_r.set_position(static_cast<int>(position));
|
||||||
|
return _next_index;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -222,29 +222,28 @@ public:
|
|||||||
void map_field_info(const FieldInfo& fi);
|
void map_field_info(const FieldInfo& fi);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Gadget for decoding and reading the stream of field records.
|
// Gadget for decoding and reading the stream of field records.
|
||||||
class FieldInfoReader {
|
class FieldInfoReader {
|
||||||
friend class FieldInfoStream;
|
|
||||||
friend class ClassFileParser;
|
|
||||||
friend class FieldStreamBase;
|
|
||||||
friend class FieldInfo;
|
|
||||||
|
|
||||||
UNSIGNED5::Reader<const u1*, int> _r;
|
UNSIGNED5::Reader<const u1*, int> _r;
|
||||||
int _next_index;
|
int _next_index;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FieldInfoReader(const Array<u1>* fi);
|
FieldInfoReader(const Array<u1>* fi);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t next_uint() { return _r.next_uint(); }
|
inline uint32_t next_uint() { return _r.next_uint(); }
|
||||||
void skip(int n) { int s = _r.try_skip(n); assert(s == n,""); }
|
void skip(int n) { int s = _r.try_skip(n); assert(s == n,""); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int has_next() { return _r.has_next(); }
|
void read_field_counts(int* java_fields, int* injected_fields);
|
||||||
int position() { return _r.position(); }
|
int has_next() const { return _r.position() < _r.limit(); }
|
||||||
int next_index() { return _next_index; }
|
int position() const { return _r.position(); }
|
||||||
|
int next_index() const { return _next_index; }
|
||||||
|
void read_name_and_signature(u2* name_index, u2* signature_index);
|
||||||
void read_field_info(FieldInfo& fi);
|
void read_field_info(FieldInfo& fi);
|
||||||
|
|
||||||
|
int search_table_lookup(const Array<u1>* search_table, const Symbol* name, const Symbol* signature, ConstantPool* cp, int java_fields);
|
||||||
|
|
||||||
// skip a whole field record, both required and optional bits
|
// skip a whole field record, both required and optional bits
|
||||||
FieldInfoReader& skip_field_info();
|
FieldInfoReader& skip_field_info();
|
||||||
|
|
||||||
@ -271,6 +270,11 @@ class FieldInfoStream : AllStatic {
|
|||||||
friend class JavaFieldStream;
|
friend class JavaFieldStream;
|
||||||
friend class FieldStreamBase;
|
friend class FieldStreamBase;
|
||||||
friend class ClassFileParser;
|
friend class ClassFileParser;
|
||||||
|
friend class FieldInfoReader;
|
||||||
|
friend class FieldInfoComparator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static int num_java_fields(const Array<u1>* fis);
|
static int num_java_fields(const Array<u1>* fis);
|
||||||
@ -278,9 +282,14 @@ class FieldInfoStream : AllStatic {
|
|||||||
static int num_total_fields(const Array<u1>* fis);
|
static int num_total_fields(const Array<u1>* fis);
|
||||||
|
|
||||||
static Array<u1>* create_FieldInfoStream(GrowableArray<FieldInfo>* fields, int java_fields, int injected_fields,
|
static Array<u1>* create_FieldInfoStream(GrowableArray<FieldInfo>* fields, int java_fields, int injected_fields,
|
||||||
ClassLoaderData* loader_data, TRAPS);
|
ClassLoaderData* loader_data, TRAPS);
|
||||||
|
static Array<u1>* create_search_table(ConstantPool* cp, const Array<u1>* fis, ClassLoaderData* loader_data, TRAPS);
|
||||||
static GrowableArray<FieldInfo>* create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count);
|
static GrowableArray<FieldInfo>* create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count);
|
||||||
static void print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp);
|
static void print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp);
|
||||||
|
|
||||||
|
DEBUG_ONLY(static void validate_search_table(ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table);)
|
||||||
|
|
||||||
|
static void print_search_table(outputStream* st, ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldStatus {
|
class FieldStatus {
|
||||||
|
@ -56,16 +56,27 @@ inline Symbol* FieldInfo::lookup_symbol(int symbol_index) const {
|
|||||||
|
|
||||||
inline int FieldInfoStream::num_injected_java_fields(const Array<u1>* fis) {
|
inline int FieldInfoStream::num_injected_java_fields(const Array<u1>* fis) {
|
||||||
FieldInfoReader fir(fis);
|
FieldInfoReader fir(fis);
|
||||||
fir.skip(1);
|
int java_fields_count;
|
||||||
return fir.next_uint();
|
int injected_fields_count;
|
||||||
|
fir.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
|
return injected_fields_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int FieldInfoStream::num_total_fields(const Array<u1>* fis) {
|
inline int FieldInfoStream::num_total_fields(const Array<u1>* fis) {
|
||||||
FieldInfoReader fir(fis);
|
FieldInfoReader fir(fis);
|
||||||
return fir.next_uint() + fir.next_uint();
|
int java_fields_count;
|
||||||
|
int injected_fields_count;
|
||||||
|
fir.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
|
return java_fields_count + injected_fields_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int FieldInfoStream::num_java_fields(const Array<u1>* fis) { return FieldInfoReader(fis).next_uint(); }
|
inline int FieldInfoStream::num_java_fields(const Array<u1>* fis) {
|
||||||
|
FieldInfoReader fir(fis);
|
||||||
|
int java_fields_count;
|
||||||
|
int injected_fields_count;
|
||||||
|
fir.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
|
return java_fields_count;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename CON>
|
template<typename CON>
|
||||||
inline void Mapper<CON>::map_field_info(const FieldInfo& fi) {
|
inline void Mapper<CON>::map_field_info(const FieldInfo& fi) {
|
||||||
@ -94,13 +105,22 @@ inline void Mapper<CON>::map_field_info(const FieldInfo& fi) {
|
|||||||
|
|
||||||
|
|
||||||
inline FieldInfoReader::FieldInfoReader(const Array<u1>* fi)
|
inline FieldInfoReader::FieldInfoReader(const Array<u1>* fi)
|
||||||
: _r(fi->data(), 0),
|
: _r(fi->data(), fi->length()),
|
||||||
_next_index(0) { }
|
_next_index(0) { }
|
||||||
|
|
||||||
|
inline void FieldInfoReader::read_field_counts(int* java_fields, int* injected_fields) {
|
||||||
|
*java_fields = next_uint();
|
||||||
|
*injected_fields = next_uint();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void FieldInfoReader::read_name_and_signature(u2* name_index, u2* signature_index) {
|
||||||
|
*name_index = checked_cast<u2>(next_uint());
|
||||||
|
*signature_index = checked_cast<u2>(next_uint());
|
||||||
|
}
|
||||||
|
|
||||||
inline void FieldInfoReader::read_field_info(FieldInfo& fi) {
|
inline void FieldInfoReader::read_field_info(FieldInfo& fi) {
|
||||||
fi._index = _next_index++;
|
fi._index = _next_index++;
|
||||||
fi._name_index = checked_cast<u2>(next_uint());
|
read_name_and_signature(&fi._name_index, &fi._signature_index);
|
||||||
fi._signature_index = checked_cast<u2>(next_uint());
|
|
||||||
fi._offset = next_uint();
|
fi._offset = next_uint();
|
||||||
fi._access_flags = AccessFlags(checked_cast<u2>(next_uint()));
|
fi._access_flags = AccessFlags(checked_cast<u2>(next_uint()));
|
||||||
fi._field_flags = FieldInfo::FieldFlags(next_uint());
|
fi._field_flags = FieldInfo::FieldFlags(next_uint());
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -56,17 +56,23 @@ class FieldStreamBase : public StackObj {
|
|||||||
|
|
||||||
inline FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit);
|
inline FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit);
|
||||||
|
|
||||||
inline FieldStreamBase(Array<u1>* fieldinfo_stream, ConstantPool* constants);
|
inline FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initialize() {
|
void initialize() {
|
||||||
int java_fields_count = _reader.next_uint();
|
int java_fields_count;
|
||||||
int injected_fields_count = _reader.next_uint();
|
int injected_fields_count;
|
||||||
assert( _limit <= java_fields_count + injected_fields_count, "Safety check");
|
_reader.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
|
if (_limit < _index) {
|
||||||
|
_limit = java_fields_count + injected_fields_count;
|
||||||
|
} else {
|
||||||
|
assert( _limit <= java_fields_count + injected_fields_count, "Safety check");
|
||||||
|
}
|
||||||
if (_limit != 0) {
|
if (_limit != 0) {
|
||||||
_reader.read_field_info(_fi_buf);
|
_reader.read_field_info(_fi_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline FieldStreamBase(InstanceKlass* klass);
|
inline FieldStreamBase(InstanceKlass* klass);
|
||||||
|
|
||||||
@ -138,8 +144,11 @@ class FieldStreamBase : public StackObj {
|
|||||||
|
|
||||||
// Iterate over only the Java fields
|
// Iterate over only the Java fields
|
||||||
class JavaFieldStream : public FieldStreamBase {
|
class JavaFieldStream : public FieldStreamBase {
|
||||||
|
Array<u1>* _search_table;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JavaFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants(), 0, k->java_fields_count()) {}
|
JavaFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants(), 0, k->java_fields_count()),
|
||||||
|
_search_table(k->fieldinfo_search_table()) {}
|
||||||
|
|
||||||
u2 name_index() const {
|
u2 name_index() const {
|
||||||
assert(!field()->field_flags().is_injected(), "regular only");
|
assert(!field()->field_flags().is_injected(), "regular only");
|
||||||
@ -149,7 +158,6 @@ class JavaFieldStream : public FieldStreamBase {
|
|||||||
u2 signature_index() const {
|
u2 signature_index() const {
|
||||||
assert(!field()->field_flags().is_injected(), "regular only");
|
assert(!field()->field_flags().is_injected(), "regular only");
|
||||||
return field()->signature_index();
|
return field()->signature_index();
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u2 generic_signature_index() const {
|
u2 generic_signature_index() const {
|
||||||
@ -164,6 +172,10 @@ class JavaFieldStream : public FieldStreamBase {
|
|||||||
assert(!field()->field_flags().is_injected(), "regular only");
|
assert(!field()->field_flags().is_injected(), "regular only");
|
||||||
return field()->initializer_index();
|
return field()->initializer_index();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Performs either a linear search or binary search through the stream
|
||||||
|
// looking for a matching name/signature combo
|
||||||
|
bool lookup(const Symbol* name, const Symbol* signature);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -176,7 +188,6 @@ class InternalFieldStream : public FieldStreamBase {
|
|||||||
|
|
||||||
class AllFieldStream : public FieldStreamBase {
|
class AllFieldStream : public FieldStreamBase {
|
||||||
public:
|
public:
|
||||||
AllFieldStream(Array<u1>* fieldinfo, ConstantPool* constants): FieldStreamBase(fieldinfo, constants) {}
|
|
||||||
AllFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants()) {}
|
AllFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants()) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -33,22 +33,18 @@
|
|||||||
FieldStreamBase::FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit) :
|
FieldStreamBase::FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit) :
|
||||||
_fieldinfo_stream(fieldinfo_stream),
|
_fieldinfo_stream(fieldinfo_stream),
|
||||||
_reader(FieldInfoReader(_fieldinfo_stream)),
|
_reader(FieldInfoReader(_fieldinfo_stream)),
|
||||||
_constants(constantPoolHandle(Thread::current(), constants)), _index(start) {
|
_constants(constantPoolHandle(Thread::current(), constants)),
|
||||||
_index = start;
|
_index(start),
|
||||||
if (limit < start) {
|
_limit(limit) {
|
||||||
_limit = FieldInfoStream::num_total_fields(_fieldinfo_stream);
|
|
||||||
} else {
|
|
||||||
_limit = limit;
|
|
||||||
}
|
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldStreamBase::FieldStreamBase(Array<u1>* fieldinfo_stream, ConstantPool* constants) :
|
FieldStreamBase::FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants) :
|
||||||
_fieldinfo_stream(fieldinfo_stream),
|
_fieldinfo_stream(fieldinfo_stream),
|
||||||
_reader(FieldInfoReader(_fieldinfo_stream)),
|
_reader(FieldInfoReader(_fieldinfo_stream)),
|
||||||
_constants(constantPoolHandle(Thread::current(), constants)),
|
_constants(constantPoolHandle(Thread::current(), constants)),
|
||||||
_index(0),
|
_index(0),
|
||||||
_limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
|
_limit(-1) {
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,9 +53,28 @@ FieldStreamBase::FieldStreamBase(InstanceKlass* klass) :
|
|||||||
_reader(FieldInfoReader(_fieldinfo_stream)),
|
_reader(FieldInfoReader(_fieldinfo_stream)),
|
||||||
_constants(constantPoolHandle(Thread::current(), klass->constants())),
|
_constants(constantPoolHandle(Thread::current(), klass->constants())),
|
||||||
_index(0),
|
_index(0),
|
||||||
_limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
|
_limit(-1) {
|
||||||
assert(klass == field_holder(), "");
|
assert(klass == field_holder(), "");
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool JavaFieldStream::lookup(const Symbol* name, const Symbol* signature) {
|
||||||
|
if (_search_table != nullptr) {
|
||||||
|
int index = _reader.search_table_lookup(_search_table, name, signature, _constants(), _limit);
|
||||||
|
if (index >= 0) {
|
||||||
|
assert(index < _limit, "must be");
|
||||||
|
_index = index;
|
||||||
|
_reader.read_field_info(_fi_buf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (; !done(); next()) {
|
||||||
|
if (this->name() == name && this->signature() == signature) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // SHARE_OOPS_FIELDSTREAMS_INLINE_HPP
|
#endif // SHARE_OOPS_FIELDSTREAMS_INLINE_HPP
|
||||||
|
@ -686,6 +686,11 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
|
|||||||
}
|
}
|
||||||
set_fieldinfo_stream(nullptr);
|
set_fieldinfo_stream(nullptr);
|
||||||
|
|
||||||
|
if (fieldinfo_search_table() != nullptr && !fieldinfo_search_table()->is_shared()) {
|
||||||
|
MetadataFactory::free_array<u1>(loader_data, fieldinfo_search_table());
|
||||||
|
}
|
||||||
|
set_fieldinfo_search_table(nullptr);
|
||||||
|
|
||||||
if (fields_status() != nullptr && !fields_status()->is_shared()) {
|
if (fields_status() != nullptr && !fields_status()->is_shared()) {
|
||||||
MetadataFactory::free_array<FieldStatus>(loader_data, fields_status());
|
MetadataFactory::free_array<FieldStatus>(loader_data, fields_status());
|
||||||
}
|
}
|
||||||
@ -1786,13 +1791,12 @@ FieldInfo InstanceKlass::field(int index) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
|
bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
|
||||||
for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
|
JavaFieldStream fs(this);
|
||||||
Symbol* f_name = fs.name();
|
if (fs.lookup(name, sig)) {
|
||||||
Symbol* f_sig = fs.signature();
|
assert(fs.name() == name, "name must match");
|
||||||
if (f_name == name && f_sig == sig) {
|
assert(fs.signature() == sig, "signature must match");
|
||||||
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.to_FieldInfo());
|
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.to_FieldInfo());
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2610,6 +2614,7 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it->push(&_fieldinfo_stream);
|
it->push(&_fieldinfo_stream);
|
||||||
|
it->push(&_fieldinfo_search_table);
|
||||||
// _fields_status might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update()
|
// _fields_status might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update()
|
||||||
it->push(&_fields_status, MetaspaceClosure::_writable);
|
it->push(&_fields_status, MetaspaceClosure::_writable);
|
||||||
|
|
||||||
@ -2710,6 +2715,8 @@ void InstanceKlass::remove_unshareable_info() {
|
|||||||
DEBUG_ONLY(_shared_class_load_count = 0);
|
DEBUG_ONLY(_shared_class_load_count = 0);
|
||||||
|
|
||||||
remove_unshareable_flags();
|
remove_unshareable_flags();
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceKlass::remove_unshareable_flags() {
|
void InstanceKlass::remove_unshareable_flags() {
|
||||||
@ -2816,6 +2823,8 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
|
|||||||
if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation() && !is_value_based()) {
|
if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation() && !is_value_based()) {
|
||||||
set_is_value_based();
|
set_is_value_based();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a class or any of its supertypes has a version older than 50.
|
// Check if a class or any of its supertypes has a version older than 50.
|
||||||
@ -3760,6 +3769,11 @@ void InstanceKlass::print_on(outputStream* st) const {
|
|||||||
map++;
|
map++;
|
||||||
}
|
}
|
||||||
st->cr();
|
st->cr();
|
||||||
|
|
||||||
|
if (fieldinfo_search_table() != nullptr) {
|
||||||
|
st->print_cr(BULLET"---- field info search table:");
|
||||||
|
FieldInfoStream::print_search_table(st, _constants, _fieldinfo_stream, _fieldinfo_search_table);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceKlass::print_value_on(outputStream* st) const {
|
void InstanceKlass::print_value_on(outputStream* st) const {
|
||||||
|
@ -276,6 +276,7 @@ class InstanceKlass: public Klass {
|
|||||||
|
|
||||||
// Fields information is stored in an UNSIGNED5 encoded stream (see fieldInfo.hpp)
|
// Fields information is stored in an UNSIGNED5 encoded stream (see fieldInfo.hpp)
|
||||||
Array<u1>* _fieldinfo_stream;
|
Array<u1>* _fieldinfo_stream;
|
||||||
|
Array<u1>* _fieldinfo_search_table;
|
||||||
Array<FieldStatus>* _fields_status;
|
Array<FieldStatus>* _fields_status;
|
||||||
|
|
||||||
// embedded Java vtable follows here
|
// embedded Java vtable follows here
|
||||||
@ -398,6 +399,9 @@ class InstanceKlass: public Klass {
|
|||||||
Array<u1>* fieldinfo_stream() const { return _fieldinfo_stream; }
|
Array<u1>* fieldinfo_stream() const { return _fieldinfo_stream; }
|
||||||
void set_fieldinfo_stream(Array<u1>* fis) { _fieldinfo_stream = fis; }
|
void set_fieldinfo_stream(Array<u1>* fis) { _fieldinfo_stream = fis; }
|
||||||
|
|
||||||
|
Array<u1>* fieldinfo_search_table() const { return _fieldinfo_search_table; }
|
||||||
|
void set_fieldinfo_search_table(Array<u1>* table) { _fieldinfo_search_table = table; }
|
||||||
|
|
||||||
Array<FieldStatus>* fields_status() const {return _fields_status; }
|
Array<FieldStatus>* fields_status() const {return _fields_status; }
|
||||||
void set_fields_status(Array<FieldStatus>* array) { _fields_status = array; }
|
void set_fields_status(Array<FieldStatus>* array) { _fields_status = array; }
|
||||||
|
|
||||||
|
@ -3550,6 +3550,13 @@ void VM_RedefineClasses::set_new_constant_pool(
|
|||||||
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, scratch_class->class_loader_data(), CHECK);
|
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, scratch_class->class_loader_data(), CHECK);
|
||||||
scratch_class->set_fieldinfo_stream(new_fis);
|
scratch_class->set_fieldinfo_stream(new_fis);
|
||||||
MetadataFactory::free_array<u1>(scratch_class->class_loader_data(), old_stream);
|
MetadataFactory::free_array<u1>(scratch_class->class_loader_data(), old_stream);
|
||||||
|
|
||||||
|
Array<u1>* old_table = scratch_class->fieldinfo_search_table();
|
||||||
|
Array<u1>* search_table = FieldInfoStream::create_search_table(scratch_class->constants(), new_fis, scratch_class->class_loader_data(), CHECK);
|
||||||
|
scratch_class->set_fieldinfo_search_table(search_table);
|
||||||
|
MetadataFactory::free_array<u1>(scratch_class->class_loader_data(), old_table);
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(scratch_class->constants(), new_fis, search_table));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update constant pool indices in the inner classes info to use
|
// Update constant pool indices in the inner classes info to use
|
||||||
|
@ -2005,6 +2005,10 @@ const int ObjectAlignmentInBytes = 8;
|
|||||||
product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC, \
|
product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC, \
|
||||||
"Use an extra lock during Thread start and exit to alleviate" \
|
"Use an extra lock during Thread start and exit to alleviate" \
|
||||||
"contention on Threads_lock.") \
|
"contention on Threads_lock.") \
|
||||||
|
\
|
||||||
|
develop(uint, BinarySearchThreshold, 16, \
|
||||||
|
"Minimal number of elements in a sorted collection to prefer" \
|
||||||
|
"binary search over simple linear search." ) \
|
||||||
|
|
||||||
// end of RUNTIME_FLAGS
|
// end of RUNTIME_FLAGS
|
||||||
|
|
||||||
|
113
src/hotspot/share/utilities/packedTable.cpp
Normal file
113
src/hotspot/share/utilities/packedTable.cpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <cstring>
|
||||||
|
|
||||||
|
#include "utilities/align.hpp"
|
||||||
|
#include "utilities/count_leading_zeros.hpp"
|
||||||
|
#include "utilities/packedTable.hpp"
|
||||||
|
|
||||||
|
// The thresholds are inclusive, and in practice the limits are rounded
|
||||||
|
// to the nearest power-of-two - 1.
|
||||||
|
// Based on the max_key and max_value we figure out the number of bits required to store
|
||||||
|
// key and value; imagine that only as bits (not aligned to byte boundary... yet).
|
||||||
|
// Then we concatenate the bits for key and value, and 'add' 1-7 padding zeroes
|
||||||
|
// (high-order bits) to align on bytes.
|
||||||
|
// In the end we have each element in the table consuming 1-8 bytes (case with 0 bits for key
|
||||||
|
// is ruled out).
|
||||||
|
PackedTableBase::PackedTableBase(uint32_t max_key, uint32_t max_value) {
|
||||||
|
unsigned int key_bits = max_key == 0 ? 0 : 32 - count_leading_zeros(max_key);
|
||||||
|
unsigned int value_bits = max_value == 0 ? 0 : 32 - count_leading_zeros(max_value);
|
||||||
|
_element_bytes = align_up(key_bits + value_bits, 8) / 8;
|
||||||
|
// shifting left by 32 is undefined behaviour, and in practice returns 1
|
||||||
|
_key_mask = key_bits >= 32 ? -1 : (1U << key_bits) - 1;
|
||||||
|
_value_shift = key_bits;
|
||||||
|
_value_mask = value_bits >= 32 ? -1 : (1U << value_bits) - 1;
|
||||||
|
guarantee(_element_bytes > 0, "wouldn't work");
|
||||||
|
assert(_element_bytes <= sizeof(uint64_t), "shouldn't happen");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: we require the supplier to provide the elements in the final order as we can't easily sort
|
||||||
|
// within this method - qsort() accepts only pure function as comparator.
|
||||||
|
void PackedTableBuilder::fill(u1* table, size_t table_length, Supplier &supplier) const {
|
||||||
|
uint32_t key, value;
|
||||||
|
size_t offset = 0;
|
||||||
|
for (; offset <= table_length && supplier.next(&key, &value); offset += _element_bytes) {
|
||||||
|
assert((key & ~_key_mask) == 0, "key out of bounds");
|
||||||
|
assert((value & ~_value_mask) == 0, "value out of bounds: %x vs. %x (%x)", value, _value_mask, ~_value_mask);
|
||||||
|
uint64_t element = static_cast<uint64_t>(key) | (static_cast<uint64_t>(value) << _value_shift);
|
||||||
|
for (unsigned int i = 0; i < _element_bytes; ++i) {
|
||||||
|
table[offset + i] = static_cast<u1>(0xFF & element);
|
||||||
|
element >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(offset == table_length, "Did not fill whole array");
|
||||||
|
assert(!supplier.next(&key, &value), "Supplier has more elements");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t PackedTableLookup::read_element(size_t offset) const {
|
||||||
|
uint64_t element = 0;
|
||||||
|
for (unsigned int i = 0; i < _element_bytes; ++i) {
|
||||||
|
element |= static_cast<uint64_t>(_table[offset + i]) << (8 * i);
|
||||||
|
}
|
||||||
|
assert((element & ~((uint64_t) _key_mask | ((uint64_t) _value_mask << _value_shift))) == 0, "read too much");
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackedTableLookup::search(Comparator& comparator, uint32_t* found_key, uint32_t* found_value) const {
|
||||||
|
unsigned int low = 0, high = checked_cast<unsigned int>(_table_length / _element_bytes);
|
||||||
|
assert(low < high, "must be");
|
||||||
|
while (low < high) {
|
||||||
|
unsigned int mid = low + (high - low) / 2;
|
||||||
|
assert(mid >= low && mid < high, "integer overflow?");
|
||||||
|
uint64_t element = read_element(_element_bytes * mid);
|
||||||
|
// Ignoring high 32 bits in element on purpose
|
||||||
|
uint32_t key = static_cast<uint32_t>(element) & _key_mask;
|
||||||
|
int cmp = comparator.compare_to(key);
|
||||||
|
if (cmp == 0) {
|
||||||
|
*found_key = key;
|
||||||
|
// Since __builtin_memcpy in read_element does not copy bits outside the element
|
||||||
|
// anything above _value_mask << _value_shift should be zero.
|
||||||
|
*found_value = checked_cast<uint32_t>(element >> _value_shift) & _value_mask;
|
||||||
|
return true;
|
||||||
|
} else if (cmp < 0) {
|
||||||
|
high = mid;
|
||||||
|
} else {
|
||||||
|
low = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
void PackedTableLookup::validate_order(Comparator &comparator) const {
|
||||||
|
auto validator = [&] (size_t offset, uint32_t key, uint32_t value) {
|
||||||
|
if (offset != 0) {
|
||||||
|
assert(comparator.compare_to(key) < 0, "not sorted");
|
||||||
|
}
|
||||||
|
comparator.reset(key);
|
||||||
|
};
|
||||||
|
iterate(validator);
|
||||||
|
}
|
||||||
|
#endif
|
123
src/hotspot/share/utilities/packedTable.hpp
Normal file
123
src/hotspot/share/utilities/packedTable.hpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 "oops/array.hpp"
|
||||||
|
#include "utilities/globalDefinitions.hpp"
|
||||||
|
|
||||||
|
// Base for space-optimized structure supporting binary search. Each element
|
||||||
|
// consists of up to 32-bit key, and up to 32-bit value; these are packed
|
||||||
|
// into a bit-record with 1-byte alignment.
|
||||||
|
// The keys are ordered according to a custom comparator.
|
||||||
|
class PackedTableBase {
|
||||||
|
protected:
|
||||||
|
unsigned int _element_bytes;
|
||||||
|
uint32_t _key_mask;
|
||||||
|
unsigned int _value_shift;
|
||||||
|
uint32_t _value_mask;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PackedTableBase(uint32_t max_key, uint32_t max_value);
|
||||||
|
|
||||||
|
// Returns number of bytes each element will occupy.
|
||||||
|
inline unsigned int element_bytes(void) const { return _element_bytes; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper class for constructing a packed table in the provided array.
|
||||||
|
class PackedTableBuilder: public PackedTableBase {
|
||||||
|
public:
|
||||||
|
class Supplier {
|
||||||
|
public:
|
||||||
|
// Returns elements with already ordered keys.
|
||||||
|
// This function should return true when the key and value was set,
|
||||||
|
// and false when there's no more elements.
|
||||||
|
// Packed table does NOT support duplicate keys.
|
||||||
|
virtual bool next(uint32_t* key, uint32_t* value) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The thresholds are inclusive, and in practice the limits are rounded
|
||||||
|
// to the nearest power-of-two - 1.
|
||||||
|
// See PackedTableBase constructor for details.
|
||||||
|
PackedTableBuilder(uint32_t max_key, uint32_t max_value): PackedTableBase(max_key, max_value) {}
|
||||||
|
|
||||||
|
// Constructs a packed table in the provided array, filling it with elements
|
||||||
|
// from the supplier. Note that no comparator is requied by this method -
|
||||||
|
// the supplier must return elements with already ordered keys.
|
||||||
|
// The table_length (in bytes) should match number of elements provided
|
||||||
|
// by the supplier (when Supplier::next() returns false the whole array should
|
||||||
|
// be filled).
|
||||||
|
void fill(u1* table, size_t table_length, Supplier &supplier) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper class for lookup in a packed table.
|
||||||
|
class PackedTableLookup: public PackedTableBase {
|
||||||
|
const u1* const _table;
|
||||||
|
const size_t _table_length;
|
||||||
|
|
||||||
|
uint64_t read_element(size_t offset) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// The comparator implementation does not have to store a key (uint32_t);
|
||||||
|
// the idea is that key can point into a different structure that hosts data
|
||||||
|
// suitable for the actual comparison. That's why PackedTableLookup::search(...)
|
||||||
|
// returns the key it found as well as the value.
|
||||||
|
class Comparator {
|
||||||
|
public:
|
||||||
|
// Returns negative/0/positive if the target referred to by this comparator
|
||||||
|
// is lower/equal/higher than the target referred to by the key.
|
||||||
|
virtual int compare_to(uint32_t key) = 0;
|
||||||
|
// Changes the target this comparator refers to.
|
||||||
|
DEBUG_ONLY(virtual void reset(uint32_t key) = 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The thresholds are inclusive, and in practice the limits are rounded
|
||||||
|
// to the nearest power-of-two - 1.
|
||||||
|
// See PackedTableBase constructor for details.
|
||||||
|
PackedTableLookup(uint32_t max_key, uint32_t max_value, const u1 *table, size_t table_length):
|
||||||
|
PackedTableBase(max_key, max_value), _table(table), _table_length(table_length) {}
|
||||||
|
|
||||||
|
PackedTableLookup(uint32_t max_key, uint32_t max_value, const Array<u1> *table):
|
||||||
|
PackedTableLookup(max_key, max_value, table->data(), static_cast<size_t>(table->length())) {}
|
||||||
|
|
||||||
|
// Performs a binary search in the packed table, looking for an element with key
|
||||||
|
// referring to a target equal according to the comparator.
|
||||||
|
// When the element is found, found_key and found_value are updated from the element
|
||||||
|
// and the function returns true.
|
||||||
|
// When the element is not found, found_key and found_value are not changed and
|
||||||
|
// the function returns false.
|
||||||
|
bool search(Comparator& comparator, uint32_t* found_key, uint32_t* found_value) const;
|
||||||
|
|
||||||
|
// Asserts that elements in the packed table follow the order defined by the comparator.
|
||||||
|
DEBUG_ONLY(void validate_order(Comparator &comparator) const);
|
||||||
|
|
||||||
|
template<typename Function>
|
||||||
|
void iterate(Function func) const {
|
||||||
|
for (size_t offset = 0; offset < _table_length; offset += _element_bytes) {
|
||||||
|
uint64_t element = read_element(offset);
|
||||||
|
uint32_t key = static_cast<uint32_t>(element) & _key_mask;
|
||||||
|
uint32_t value = checked_cast<uint32_t>(element >> _value_shift) & _value_mask;
|
||||||
|
func(offset, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -261,7 +261,7 @@ class UNSIGNED5 : AllStatic {
|
|||||||
ARR _array;
|
ARR _array;
|
||||||
OFF _limit;
|
OFF _limit;
|
||||||
OFF _position;
|
OFF _position;
|
||||||
int next_length() {
|
int next_length() const {
|
||||||
return UNSIGNED5::check_length(_array, _position, _limit, GET());
|
return UNSIGNED5::check_length(_array, _position, _limit, GET());
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
@ -270,7 +270,7 @@ class UNSIGNED5 : AllStatic {
|
|||||||
uint32_t next_uint() {
|
uint32_t next_uint() {
|
||||||
return UNSIGNED5::read_uint(_array, _position, _limit, GET());
|
return UNSIGNED5::read_uint(_array, _position, _limit, GET());
|
||||||
}
|
}
|
||||||
bool has_next() {
|
bool has_next() const {
|
||||||
return next_length() != 0;
|
return next_length() != 0;
|
||||||
}
|
}
|
||||||
// tries to skip count logical entries; returns actual number skipped
|
// tries to skip count logical entries; returns actual number skipped
|
||||||
@ -284,8 +284,9 @@ class UNSIGNED5 : AllStatic {
|
|||||||
return actual;
|
return actual;
|
||||||
}
|
}
|
||||||
ARR array() { return _array; }
|
ARR array() { return _array; }
|
||||||
OFF limit() { return _limit; }
|
OFF limit() const { return _limit; }
|
||||||
OFF position() { return _position; }
|
OFF position() const { return _position; }
|
||||||
|
void set_limit(OFF limit) { _limit = limit; }
|
||||||
void set_position(OFF position) { _position = position; }
|
void set_position(OFF position) { _position = position; }
|
||||||
|
|
||||||
// For debugging, even in product builds (see debug.cpp).
|
// For debugging, even in product builds (see debug.cpp).
|
||||||
|
158
test/hotspot/gtest/utilities/test_packedTable.cpp
Normal file
158
test/hotspot/gtest/utilities/test_packedTable.cpp
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 "utilities/packedTable.hpp"
|
||||||
|
#include "unittest.hpp"
|
||||||
|
|
||||||
|
class Supplier: public PackedTableBuilder::Supplier {
|
||||||
|
uint32_t* _keys;
|
||||||
|
uint32_t* _values;
|
||||||
|
size_t _num_keys;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Supplier(uint32_t* keys, uint32_t* values, size_t num_keys):
|
||||||
|
_keys(keys), _values(values), _num_keys(num_keys) {}
|
||||||
|
|
||||||
|
bool next(uint32_t* key, uint32_t* value) override {
|
||||||
|
if (_num_keys == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*key = *_keys;
|
||||||
|
++_keys;
|
||||||
|
if (_values != nullptr) {
|
||||||
|
*value = *_values;
|
||||||
|
++_values;
|
||||||
|
} else {
|
||||||
|
*value = 0;
|
||||||
|
}
|
||||||
|
--_num_keys;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Comparator: public PackedTableLookup::Comparator {
|
||||||
|
uint32_t _current;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int compare_to(uint32_t key) override {
|
||||||
|
return _current < key ? -1 : (_current > key ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(uint32_t key) DEBUG_ONLY(override) {
|
||||||
|
_current = key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test(uint32_t max_key, uint32_t max_value, unsigned int length) {
|
||||||
|
if (length > max_key + 1) {
|
||||||
|
// can't generate more keys, as keys must be unique
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PackedTableBuilder builder(max_key, max_value);
|
||||||
|
size_t table_length = length * builder.element_bytes();
|
||||||
|
u1* table = new u1[table_length];
|
||||||
|
|
||||||
|
uint32_t* keys = new uint32_t[length];
|
||||||
|
uint32_t* values = max_value != 0 ? new uint32_t[length] : nullptr;
|
||||||
|
for (unsigned int i = 0; i < length; ++i) {
|
||||||
|
keys[i] = i;
|
||||||
|
if (values != nullptr) {
|
||||||
|
values[i] = i % max_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Supplier sup(keys, values, length);
|
||||||
|
builder.fill(table, table_length, sup);
|
||||||
|
|
||||||
|
Comparator comparator;
|
||||||
|
PackedTableLookup lookup(max_key, max_value, table, table_length);
|
||||||
|
#ifdef ASSERT
|
||||||
|
lookup.validate_order(comparator);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < length; ++i) {
|
||||||
|
uint32_t key, value;
|
||||||
|
comparator.reset(keys[i]);
|
||||||
|
EXPECT_TRUE(lookup.search(comparator, &key, &value));
|
||||||
|
EXPECT_EQ(key, keys[i]);
|
||||||
|
if (values != nullptr) {
|
||||||
|
EXPECT_EQ(value, values[i]);
|
||||||
|
} else {
|
||||||
|
EXPECT_EQ(value, 0U);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] keys;
|
||||||
|
delete[] values;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_with_bits(uint32_t max_key, uint32_t max_value) {
|
||||||
|
// Some small sizes
|
||||||
|
for (unsigned int i = 0; i <= 100; ++i) {
|
||||||
|
test(max_key, max_value, i);
|
||||||
|
}
|
||||||
|
test(max_key, max_value, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PackedTableLookup, lookup) {
|
||||||
|
for (int key_bits = 1; key_bits <= 32; ++key_bits) {
|
||||||
|
for (int value_bits = 0; value_bits <= 32; ++value_bits) {
|
||||||
|
test_with_bits(static_cast<uint32_t>((1ULL << key_bits) - 1),
|
||||||
|
static_cast<uint32_t>((1ULL << value_bits) - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PackedTableBase, element_bytes) {
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(1, 0);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 1U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(15, 15);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 1U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(15, 16);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 2U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(31, 7);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 1U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(32, 7);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 2U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(-1, 0);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 4U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(-1, 1);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 5U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(-1, -1);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 8U);
|
||||||
|
}
|
||||||
|
}
|
140
test/hotspot/jtreg/runtime/FieldStream/LocalFieldLookupTest.java
Normal file
140
test/hotspot/jtreg/runtime/FieldStream/LocalFieldLookupTest.java
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||||
|
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
|
||||||
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test id=defaults
|
||||||
|
* @bug 8352075
|
||||||
|
* @library /test/lib
|
||||||
|
* @library /testlibrary/asm
|
||||||
|
* @run main/othervm LocalFieldLookupTest
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* @test id=custom-threshold
|
||||||
|
* @bug 8352075
|
||||||
|
* @library /test/lib
|
||||||
|
* @library /testlibrary/asm
|
||||||
|
* @requires vm.debug == true
|
||||||
|
* @run main/othervm LocalFieldLookupTest
|
||||||
|
* @run main/othervm -XX:BinarySearchThreshold=0 LocalFieldLookupTest
|
||||||
|
* @run main/othervm -XX:BinarySearchThreshold=1 LocalFieldLookupTest
|
||||||
|
* @run main/othervm -XX:BinarySearchThreshold=15 LocalFieldLookupTest
|
||||||
|
* @run main/othervm -XX:BinarySearchThreshold=100000 LocalFieldLookupTest
|
||||||
|
*/
|
||||||
|
public class LocalFieldLookupTest {
|
||||||
|
private static final String TEST_CLASS_NAME = "Test";
|
||||||
|
private static final int MAX_FIELDS_IN_METHOD = 10000;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||||
|
// Test small classes, covering the tested thresholds
|
||||||
|
for (int i = 0; i <= 33; ++i) {
|
||||||
|
makeClass(i).newInstance();
|
||||||
|
}
|
||||||
|
// Test classes around 256 fields (index encoding 1/2 bytes) to check off-by-one errors
|
||||||
|
for (int i = 254; i <= 259; ++i) {
|
||||||
|
makeClass(255).newInstance();
|
||||||
|
}
|
||||||
|
// We would like to test #fields that create have the stream about 65536 bytes long;
|
||||||
|
// this value is not exposed, though, so these are rather experimentally found values,
|
||||||
|
// hence fragile. Moreover, since the stream length is incremented by about 8 bytes
|
||||||
|
// for each field we cannot test for off-by-one errors reliably.
|
||||||
|
for (int i = 8433; i <= 8437; ++i) {
|
||||||
|
makeClass(i).newInstance();
|
||||||
|
}
|
||||||
|
// The largest class we can create - this one has 65533 entries in the constant pool
|
||||||
|
makeClass(26205).newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> makeClass(int fields) throws ClassNotFoundException {
|
||||||
|
ClassWriter writer = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||||
|
writer.visit(49, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, TEST_CLASS_NAME,null, "java/lang/Object", null);
|
||||||
|
|
||||||
|
for (int i = 0; i < fields; i += 2) {
|
||||||
|
writer.visitField(ACC_PUBLIC, "f" + i, "I", null, null);
|
||||||
|
// Let's use duplicate names to confirm search takes signatures into account
|
||||||
|
if (i + 1 < fields) {
|
||||||
|
writer.visitField(ACC_PUBLIC, "f" + i, "J", null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We initialize fields in multiple methods to avoid running into bytecode limit per method
|
||||||
|
MethodVisitor fi = null;
|
||||||
|
for (int i = 0; i < fields; i+= 2) {
|
||||||
|
if (fi == null) {
|
||||||
|
fi = writer.visitMethod(ACC_PRIVATE, "init" + i, "()V", null, null);
|
||||||
|
fi.visitCode();
|
||||||
|
}
|
||||||
|
fi.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
|
fi.visitInsn(Opcodes.ICONST_2);
|
||||||
|
fi.visitFieldInsn(PUTFIELD, TEST_CLASS_NAME, "f" + i, "I");
|
||||||
|
if (i + 1 < fields) {
|
||||||
|
fi.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
|
fi.visitInsn(Opcodes.LCONST_1);
|
||||||
|
fi.visitFieldInsn(PUTFIELD, TEST_CLASS_NAME, "f" + i, "J");
|
||||||
|
}
|
||||||
|
if (i % MAX_FIELDS_IN_METHOD == MAX_FIELDS_IN_METHOD - 2) {
|
||||||
|
fi.visitInsn(Opcodes.RETURN);
|
||||||
|
fi.visitMaxs(0, 0);
|
||||||
|
fi.visitEnd();
|
||||||
|
fi = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fi != null) {
|
||||||
|
fi.visitInsn(Opcodes.RETURN);
|
||||||
|
fi.visitMaxs(0, 0);
|
||||||
|
fi.visitEnd();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
MethodVisitor mv = writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||||
|
mv.visitCode();
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||||
|
for (int i = 0; i < fields; i += MAX_FIELDS_IN_METHOD) {
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitMethodInsn(INVOKESPECIAL, TEST_CLASS_NAME, "init" + i, "()V", false);
|
||||||
|
}
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(0, 0);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
writer.visitEnd();
|
||||||
|
|
||||||
|
byte[] bytecode = writer.toByteArray();
|
||||||
|
ClassLoader cl = new ClassLoader() {
|
||||||
|
@Override
|
||||||
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
|
if (!TEST_CLASS_NAME.equals(name)) {
|
||||||
|
throw new ClassNotFoundException();
|
||||||
|
}
|
||||||
|
return defineClass(TEST_CLASS_NAME, bytecode, 0, bytecode.length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return cl.loadClass(TEST_CLASS_NAME);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user