OpenJDK / jdk / jdk
changeset 59360:5ff07f5ad00e
8243996: Remove hardcoded field offsets from HotSpot
Reviewed-by: jrose, coleenp, dholmes, fparain
line wrap: on
line diff
--- a/src/hotspot/share/classfile/classFileParser.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/classfile/classFileParser.cpp Wed May 20 09:31:38 2020 +0000 @@ -4200,23 +4200,6 @@ 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;
--- a/src/hotspot/share/classfile/javaClasses.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/classfile/javaClasses.cpp Wed May 20 09:31:38 2020 +0000 @@ -3578,6 +3578,44 @@ 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::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 @@ 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 @@ 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 @@ } // 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 @@ 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"); }
--- a/src/hotspot/share/classfile/javaClasses.hpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/classfile/javaClasses.hpp Wed May 20 09:31:38 2020 +0000 @@ -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 @@ 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 @@ 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 java_lang_boxing_object: AllStatic { private: - enum { - hc_value_offset = 0 - }; static int value_offset; static int long_value_offset; @@ -910,6 +888,9 @@ value_offset; } + static void compute_offsets(); + static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; + // Debugging friend class JavaClasses; }; @@ -919,14 +900,9 @@ // Interface to java.lang.ref.Reference objects class java_lang_ref_Reference: AllStatic { + static bool _offsets_initialized; + public: - enum { - hc_referent_offset = 0, - hc_queue_offset = 1, - hc_next_offset = 2, - hc_discovered_offset = 3 // Is not last, see SoftRefs. - }; - static int referent_offset; static int queue_offset; static int next_offset; @@ -950,6 +926,9 @@ 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 @@ 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;
--- a/src/hotspot/share/classfile/systemDictionary.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/classfile/systemDictionary.cpp Wed May 20 09:31:38 2020 +0000 @@ -2095,6 +2095,13 @@ // 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));
--- a/src/hotspot/share/classfile/vmSymbols.hpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/classfile/vmSymbols.hpp Wed May 20 09:31:38 2020 +0000 @@ -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 @@ 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:
--- a/src/hotspot/share/interpreter/abstractInterpreter.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp Wed May 20 09:31:38 2020 +0000 @@ -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();
--- a/src/hotspot/share/interpreter/cppInterpreter.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/interpreter/cppInterpreter.cpp Wed May 20 09:31:38 2020 +0000 @@ -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 @@ BytecodeInterpreter::run(&start_msg); } - void CppInterpreter::invoke_method(Method* method, address entry_point, TRAPS) { ((ZeroEntry *) entry_point)->invoke(method, THREAD); }
--- a/src/hotspot/share/interpreter/cppInterpreter.hpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/interpreter/cppInterpreter.hpp Wed May 20 09:31:38 2020 +0000 @@ -39,7 +39,8 @@ 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
--- a/src/hotspot/share/interpreter/interpreter.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/interpreter/interpreter.cpp Wed May 20 09:31:38 2020 +0000 @@ -113,9 +113,22 @@ *_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
--- a/src/hotspot/share/interpreter/templateInterpreter.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/interpreter/templateInterpreter.cpp Wed May 20 09:31:38 2020 +0000 @@ -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 @@ // 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();
--- a/src/hotspot/share/interpreter/templateInterpreter.hpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/interpreter/templateInterpreter.hpp Wed May 20 09:31:38 2020 +0000 @@ -134,7 +134,8 @@ 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
--- a/src/hotspot/share/memory/universe.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/memory/universe.cpp Wed May 20 09:31:38 2020 +0000 @@ -649,8 +649,6 @@ TraceTime timer("Genesis", TRACETIME_LOG(Info, startuptime)); - JavaClasses::compute_hard_coded_offsets(); - initialize_global_behaviours(); GCConfig::arguments()->initialize_heap_sizes();
--- a/src/hotspot/share/oops/method.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/oops/method.cpp Wed May 20 09:31:38 2020 +0000 @@ -1176,16 +1176,12 @@ // 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; }
--- a/src/hotspot/share/runtime/init.cpp Wed May 06 17:24:47 2020 +0200 +++ b/src/hotspot/share/runtime/init.cpp Wed May 20 09:31:38 2020 +0000 @@ -66,8 +66,9 @@ 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 @@ 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