8243996: Remove hardcoded field offsets from HotSpot

Reviewed-by: jrose, coleenp, dholmes, fparain
This commit is contained in:
Erik Österlund 2020-05-20 09:31:38 +00:00
parent c3e3459027
commit 6508c5a38d
14 changed files with 109 additions and 114 deletions

View File

@ -4200,23 +4200,6 @@ void ClassFileParser::layout_fields(ConstantPool* cp,
bool compact_fields = true;
bool allocate_oops_first = false;
// The next classes have predefined hard-coded fields offsets
// (see in JavaClasses::compute_hard_coded_offsets()).
// Use default fields allocation order for them.
if (_loader_data->class_loader() == NULL &&
(_class_name == vmSymbols::java_lang_ref_Reference() ||
_class_name == vmSymbols::java_lang_Boolean() ||
_class_name == vmSymbols::java_lang_Character() ||
_class_name == vmSymbols::java_lang_Float() ||
_class_name == vmSymbols::java_lang_Double() ||
_class_name == vmSymbols::java_lang_Byte() ||
_class_name == vmSymbols::java_lang_Short() ||
_class_name == vmSymbols::java_lang_Integer() ||
_class_name == vmSymbols::java_lang_Long())) {
allocate_oops_first = true; // Allocate oops first
compact_fields = false; // Don't compact fields
}
int next_nonstatic_oop_offset = 0;
int next_nonstatic_double_offset = 0;

View File

@ -3578,6 +3578,44 @@ bool java_lang_ref_Reference::is_referent_field(oop obj, ptrdiff_t offset) {
return is_reference;
}
#define REFERENCE_FIELDS_DO(macro) \
macro(referent_offset, k, "referent", object_signature, false); \
macro(queue_offset, k, "queue", referencequeue_signature, false); \
macro(next_offset, k, "next", reference_signature, false); \
macro(discovered_offset, k, "discovered", reference_signature, false);
void java_lang_ref_Reference::compute_offsets() {
if (_offsets_initialized) {
return;
}
_offsets_initialized = true;
InstanceKlass* k = SystemDictionary::Reference_klass();
REFERENCE_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
#if INCLUDE_CDS
void java_lang_ref_Reference::serialize_offsets(SerializeClosure* f) {
f->do_bool(&_offsets_initialized);
REFERENCE_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
}
#endif
#define BOXING_FIELDS_DO(macro) \
macro(value_offset, integerKlass, "value", int_signature, false); \
macro(long_value_offset, longKlass, "value", long_signature, false);
void java_lang_boxing_object::compute_offsets() {
InstanceKlass* integerKlass = SystemDictionary::Integer_klass();
InstanceKlass* longKlass = SystemDictionary::Long_klass();
BOXING_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
#if INCLUDE_CDS
void java_lang_boxing_object::serialize_offsets(SerializeClosure* f) {
BOXING_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
}
#endif
// Support for java_lang_ref_SoftReference
//
@ -4342,6 +4380,7 @@ int java_lang_reflect_Parameter::index_offset;
int java_lang_reflect_Parameter::executable_offset;
int java_lang_boxing_object::value_offset;
int java_lang_boxing_object::long_value_offset;
bool java_lang_ref_Reference::_offsets_initialized;
int java_lang_ref_Reference::referent_offset;
int java_lang_ref_Reference::queue_offset;
int java_lang_ref_Reference::next_offset;
@ -4738,12 +4777,6 @@ jboolean java_lang_Boolean::value(oop obj) {
return v.z;
}
// Use with care. This function makes a lot of assumptions about the contents of the object.
// So naturally, only hardcode offsets if you know what you are doing.
static int member_offset(int hardcoded_offset, int elementSize) {
return align_up((hardcoded_offset * elementSize) + instanceOopDesc::base_offset_in_bytes(), elementSize);
}
#define RECORDCOMPONENT_FIELDS_DO(macro) \
macro(clazz_offset, k, "clazz", class_signature, false); \
macro(name_offset, k, "name", string_signature, false); \
@ -4793,22 +4826,6 @@ void java_lang_reflect_RecordComponent::set_typeAnnotations(oop element, oop val
element->obj_field_put(typeAnnotations_offset, value);
}
// Compute hard-coded offsets
// Invoked before SystemDictionary::initialize, so pre-loaded classes
// are not available to determine the offset_of_static_fields.
void JavaClasses::compute_hard_coded_offsets() {
// java_lang_boxing_object
java_lang_boxing_object::value_offset = member_offset(java_lang_boxing_object::hc_value_offset, BytesPerInt);
java_lang_boxing_object::long_value_offset = member_offset(java_lang_boxing_object::hc_value_offset, BytesPerLong);
// java_lang_ref_Reference
java_lang_ref_Reference::referent_offset = member_offset(java_lang_ref_Reference::hc_referent_offset, heapOopSize);
java_lang_ref_Reference::queue_offset = member_offset(java_lang_ref_Reference::hc_queue_offset, heapOopSize);
java_lang_ref_Reference::next_offset = member_offset(java_lang_ref_Reference::hc_next_offset, heapOopSize);
java_lang_ref_Reference::discovered_offset = member_offset(java_lang_ref_Reference::hc_discovered_offset, heapOopSize);
}
#define DO_COMPUTE_OFFSETS(k) k::compute_offsets();
// Compute non-hard-coded field offsets of all the classes in this file
@ -4825,8 +4842,8 @@ void JavaClasses::compute_offsets() {
}
// We have already called the compute_offsets() of the
// BASIC_JAVA_CLASSES_DO_PART1 classes (java_lang_String and java_lang_Class)
// earlier inside SystemDictionary::resolve_well_known_classes()
// BASIC_JAVA_CLASSES_DO_PART1 classes (java_lang_String, java_lang_Class and
// java_lang_ref_Reference) earlier inside SystemDictionary::resolve_well_known_classes()
BASIC_JAVA_CLASSES_DO_PART2(DO_COMPUTE_OFFSETS);
}
@ -4913,14 +4930,6 @@ void JavaClasses::check_offsets() {
CHECK_OFFSET("java/lang/Integer", java_lang_boxing_object, value, "I");
CHECK_LONG_OFFSET("java/lang/Long", java_lang_boxing_object, value, "J");
// java.lang.ref.Reference
CHECK_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, referent, "Ljava/lang/Object;");
CHECK_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, queue, "Ljava/lang/ref/ReferenceQueue;");
CHECK_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, next, "Ljava/lang/ref/Reference;");
// Fake field
//CHECK_OFFSET("java/lang/ref/Reference", java_lang_ref_Reference, discovered, "Ljava/lang/ref/Reference;");
if (!valid) vm_exit_during_initialization("Hard-coded field offset verification failed");
}

View File

@ -33,24 +33,11 @@
class RecordComponent;
// Interface for manipulating the basic Java classes.
//
// All dependencies on layout of actual Java classes should be kept here.
// If the layout of any of the classes above changes the offsets must be adjusted.
//
// For most classes we hardwire the offsets for performance reasons. In certain
// cases (e.g. java.security.AccessControlContext) we compute the offsets at
// startup since the layout here differs between JDK1.2 and JDK1.3.
//
// Note that fields (static and non-static) are arranged with oops before non-oops
// on a per class basis. The offsets below have to reflect this ordering.
//
// When editing the layouts please update the check_offset verification code
// correspondingly. The names in the enums must be identical to the actual field
// names in order for the verification code to work.
#define BASIC_JAVA_CLASSES_DO_PART1(f) \
f(java_lang_Class) \
f(java_lang_String) \
f(java_lang_ref_Reference) \
//end
#define BASIC_JAVA_CLASSES_DO_PART2(f) \
@ -86,6 +73,7 @@ class RecordComponent;
f(java_lang_LiveStackFrameInfo) \
f(java_util_concurrent_locks_AbstractOwnableSynchronizer) \
f(jdk_internal_misc_UnsafeConstants) \
f(java_lang_boxing_object) \
//end
#define BASIC_JAVA_CLASSES_DO(f) \
@ -529,13 +517,6 @@ class java_lang_Throwable: AllStatic {
friend class BacktraceIterator;
private:
// Offsets
enum {
hc_backtrace_offset = 0,
hc_detailMessage_offset = 1,
hc_cause_offset = 2, // New since 1.4
hc_stackTrace_offset = 3 // New since 1.4
};
// Trace constants
enum {
trace_methods_offset = 0,
@ -886,9 +867,6 @@ class reflect_UnsafeStaticFieldAccessorImpl {
class java_lang_boxing_object: AllStatic {
private:
enum {
hc_value_offset = 0
};
static int value_offset;
static int long_value_offset;
@ -910,6 +888,9 @@ class java_lang_boxing_object: AllStatic {
value_offset;
}
static void compute_offsets();
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
// Debugging
friend class JavaClasses;
};
@ -919,14 +900,9 @@ class java_lang_boxing_object: AllStatic {
// Interface to java.lang.ref.Reference objects
class java_lang_ref_Reference: AllStatic {
public:
enum {
hc_referent_offset = 0,
hc_queue_offset = 1,
hc_next_offset = 2,
hc_discovered_offset = 3 // Is not last, see SoftRefs.
};
static bool _offsets_initialized;
public:
static int referent_offset;
static int queue_offset;
static int next_offset;
@ -950,6 +926,9 @@ class java_lang_ref_Reference: AllStatic {
static bool is_referent_field(oop obj, ptrdiff_t offset);
static inline bool is_final(oop ref);
static inline bool is_phantom(oop ref);
static void compute_offsets();
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
};
@ -1731,7 +1710,6 @@ class JavaClasses : AllStatic {
static int compute_injected_offset(InjectedFieldID id);
static void compute_hard_coded_offsets();
static void compute_offsets();
static void check_offsets() PRODUCT_RETURN;
static void serialize_offsets(SerializeClosure* soc) NOT_CDS_RETURN;

View File

@ -2095,6 +2095,13 @@ void SystemDictionary::resolve_well_known_classes(TRAPS) {
// do a bunch more:
resolve_wk_klasses_through(WK_KLASS_ENUM_NAME(Reference_klass), scan, CHECK);
// The offsets for jlr.Reference must be computed before
// InstanceRefKlass::update_nonstatic_oop_maps is called. That function uses
// the offsets to remove the referent and discovered fields from the oop maps,
// as they are treated in a special way by the GC. Removing these oops from the
// oop maps must be done before the usual subclasses of jlr.Reference are loaded.
java_lang_ref_Reference::compute_offsets();
// Preload ref klasses and set reference types
WK_KLASS(Reference_klass)->set_reference_type(REF_OTHER);
InstanceRefKlass::update_nonstatic_oop_maps(WK_KLASS(Reference_klass));

View File

@ -552,6 +552,7 @@
template(string_signature, "Ljava/lang/String;") \
template(string_array_signature, "[Ljava/lang/String;") \
template(reference_signature, "Ljava/lang/ref/Reference;") \
template(referencequeue_signature, "Ljava/lang/ref/ReferenceQueue;") \
template(executable_signature, "Ljava/lang/reflect/Executable;") \
template(module_signature, "Ljava/lang/Module;") \
template(concurrenthashmap_signature, "Ljava/util/concurrent/ConcurrentHashMap;") \
@ -1535,7 +1536,7 @@ class vmSymbols: AllStatic {
FIRST_SID = NO_SID + 1
};
enum {
log2_SID_LIMIT = 10 // checked by an assert at start-up
log2_SID_LIMIT = 11 // checked by an assert at start-up
};
private:

View File

@ -56,8 +56,6 @@
// Implementation of platform independent aspects of Interpreter
void AbstractInterpreter::initialize() {
assert(_code == NULL, "must only initialize once");
// make sure 'imported' classes are initialized
if (CountBytecodes || TraceBytecodes || StopInterpreterAt) BytecodeCounter::reset();
if (PrintBytecodeHistogram) BytecodeHistogram::reset();

View File

@ -38,17 +38,22 @@
#error "Only Zero CppInterpreter is supported"
#endif
void CppInterpreter::initialize() {
void CppInterpreter::initialize_stub() {
if (_code != NULL) return;
// generate interpreter
int code_size = InterpreterCodeSize;
NOT_PRODUCT(code_size *= 4;) // debug uses extra interpreter code space
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,
"Interpreter");
}
void CppInterpreter::initialize_code() {
AbstractInterpreter::initialize();
// generate interpreter
{ ResourceMark rm;
TraceTime timer("Interpreter generation", TRACETIME_LOG(Info, startuptime));
int code_size = InterpreterCodeSize;
NOT_PRODUCT(code_size *= 4;) // debug uses extra interpreter code space
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,
"Interpreter");
CppInterpreterGenerator g(_code);
if (PrintInterpreter) print();
}
@ -61,7 +66,6 @@ void CppInterpreter::initialize() {
BytecodeInterpreter::run(&start_msg);
}
void CppInterpreter::invoke_method(Method* method, address entry_point, TRAPS) {
((ZeroEntry *) entry_point)->invoke(method, THREAD);
}

View File

@ -39,7 +39,8 @@ class CppInterpreter: public AbstractInterpreter {
friend class VMStructs;
public:
// Initialization/debugging
static void initialize();
static void initialize_stub();
static void initialize_code();
// this only returns whether a pc is within generated code for the interpreter.
// These are moderately dubious interfaces for the c++ interpreter. Only

View File

@ -113,9 +113,22 @@ CodeletMark::~CodeletMark() {
*_masm = NULL;
}
// The reason that interpreter initialization is split into two parts is that the first part
// needs to run before methods are loaded (which with CDS implies linked also), and the other
// part needs to run after. The reason is that when methods are loaded (with CDS) or linked
// (without CDS), the i2c adapters are generated that assert we are currently in the interpreter.
// Asserting that requires knowledge about where the interpreter is in memory. Therefore,
// establishing the interpreter address must be done before methods are loaded. However,
// we would like to actually generate the interpreter after methods are loaded. That allows
// us to remove otherwise hardcoded offsets regarding fields that are needed in the interpreter
// code. This leads to a split if 1. reserving the memory for the interpreter, 2. loading methods
// and 3. generating the interpreter.
void interpreter_init_stub() {
Interpreter::initialize_stub();
}
void interpreter_init() {
Interpreter::initialize();
void interpreter_init_code() {
Interpreter::initialize_code();
#ifndef PRODUCT
if (TraceBytecodes) BytecodeTracer::set_closure(BytecodeTracer::std_closure());
#endif // PRODUCT

View File

@ -39,12 +39,20 @@
# define __ _masm->
void TemplateInterpreter::initialize() {
void TemplateInterpreter::initialize_stub() {
// assertions
assert(_code == NULL, "must only initialize once");
assert((int)Bytecodes::number_of_codes <= (int)DispatchTable::length,
"dispatch table too small");
// allocate interpreter
int code_size = InterpreterCodeSize;
NOT_PRODUCT(code_size *= 4;) // debug uses extra interpreter code space
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,
"Interpreter");
}
void TemplateInterpreter::initialize_code() {
AbstractInterpreter::initialize();
TemplateTable::initialize();
@ -52,10 +60,6 @@ void TemplateInterpreter::initialize() {
// generate interpreter
{ ResourceMark rm;
TraceTime timer("Interpreter generation", TRACETIME_LOG(Info, startuptime));
int code_size = InterpreterCodeSize;
NOT_PRODUCT(code_size *= 4;) // debug uses extra interpreter code space
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,
"Interpreter");
TemplateInterpreterGenerator g(_code);
// Free the unused memory not occupied by the interpreter and the stubs
_code->deallocate_unused_tail();

View File

@ -134,7 +134,8 @@ class TemplateInterpreter: public AbstractInterpreter {
public:
// Initialization/debugging
static void initialize();
static void initialize_stub();
static void initialize_code();
// this only returns whether a pc is within generated code for the interpreter.
static bool contains(address pc) { return _code != NULL && _code->contains(pc); }
// Debugging/printing

View File

@ -649,8 +649,6 @@ jint universe_init() {
TraceTime timer("Genesis", TRACETIME_LOG(Info, startuptime));
JavaClasses::compute_hard_coded_offsets();
initialize_global_behaviours();
GCConfig::arguments()->initialize_heap_sizes();

View File

@ -1176,16 +1176,12 @@ void Method::link_method(const methodHandle& h_method, TRAPS) {
// If the code cache is full, we may reenter this function for the
// leftover methods that weren't linked.
if (is_shared()) {
#ifdef ASSERT
address entry = Interpreter::entry_for_cds_method(h_method);
assert(entry != NULL && entry == _i2i_entry,
"should be correctly set during dump time");
#endif
// Can't assert that the adapters are sane, because methods get linked before
// the interpreter is generated, and hence before its adapters are generated.
// If you messed them up you will notice soon enough though, don't you worry.
if (adapter() != NULL) {
return;
}
assert(entry == _from_interpreted_entry,
"should be correctly set during dump time");
} else if (_i2i_entry != NULL) {
return;
}

View File

@ -66,8 +66,9 @@ void stubRoutines_init1();
jint universe_init(); // depends on codeCache_init and stubRoutines_init
// depends on universe_init, must be before interpreter_init (currently only on SPARC)
void gc_barrier_stubs_init();
void interpreter_init(); // before any methods loaded
void invocationCounter_init(); // before any methods loaded
void interpreter_init_stub(); // before any methods loaded
void interpreter_init_code(); // after methods loaded, but before they are linked
void invocationCounter_init(); // after methods loaded, but before they are linked
void accessFlags_init();
void InterfaceSupport_init();
void universe2_init(); // dependent on codeCache_init and stubRoutines_init, loads primordial classes
@ -118,15 +119,16 @@ jint init_globals() {
if (status != JNI_OK)
return status;
gc_barrier_stubs_init(); // depends on universe_init, must be before interpreter_init
interpreter_init(); // before any methods loaded
invocationCounter_init(); // before any methods loaded
gc_barrier_stubs_init(); // depends on universe_init, must be before interpreter_init
interpreter_init_stub(); // before methods get loaded
accessFlags_init();
InterfaceSupport_init();
VMRegImpl::set_regName(); // need this before generate_stubs (for printing oop maps).
VMRegImpl::set_regName(); // need this before generate_stubs (for printing oop maps).
SharedRuntime::generate_stubs();
universe2_init(); // dependent on codeCache_init and stubRoutines_init1
javaClasses_init();// must happen after vtable initialization, before referenceProcessor_init
interpreter_init_code(); // after javaClasses_init and before any method gets linked
invocationCounter_init(); // after javaClasses_init and before any method gets linked
referenceProcessor_init();
jni_handles_init();
#if INCLUDE_VM_STRUCTS