8243996: Remove hardcoded field offsets from HotSpot
Reviewed-by: jrose, coleenp, dholmes, fparain
This commit is contained in:
parent
c3e3459027
commit
6508c5a38d
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user