mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8334898: Resolve static field/method references at CDS dump time
Reviewed-by: iklam, adinn
This commit is contained in:
parent
5c5367c312
commit
5dd8a33396
@ -224,6 +224,13 @@ void AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(JavaThread*
|
||||
bcs.next();
|
||||
Bytecodes::Code raw_bc = bcs.raw_code();
|
||||
switch (raw_bc) {
|
||||
case Bytecodes::_getstatic:
|
||||
case Bytecodes::_putstatic:
|
||||
maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD);
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
CLEAR_PENDING_EXCEPTION; // just ignore
|
||||
}
|
||||
break;
|
||||
case Bytecodes::_getfield:
|
||||
// no-fast bytecode
|
||||
case Bytecodes::_nofast_getfield:
|
||||
@ -266,6 +273,7 @@ void AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(JavaThread*
|
||||
case Bytecodes::_invokespecial:
|
||||
case Bytecodes::_invokevirtual:
|
||||
case Bytecodes::_invokeinterface:
|
||||
case Bytecodes::_invokestatic:
|
||||
maybe_resolve_fmi_ref(ik, m, raw_bc, bcs.get_index_u2(), preresolve_list, THREAD);
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
CLEAR_PENDING_EXCEPTION; // just ignore
|
||||
@ -302,13 +310,31 @@ void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m
|
||||
}
|
||||
|
||||
Klass* resolved_klass = cp->klass_ref_at(raw_index, bc, CHECK);
|
||||
const char* is_static = "";
|
||||
|
||||
switch (bc) {
|
||||
case Bytecodes::_getstatic:
|
||||
case Bytecodes::_putstatic:
|
||||
if (!VM_Version::supports_fast_class_init_checks()) {
|
||||
return; // Do not resolve since interpreter lacks fast clinit barriers support
|
||||
}
|
||||
InterpreterRuntime::resolve_get_put(bc, raw_index, mh, cp, false /*initialize_holder*/, CHECK);
|
||||
is_static = " *** static";
|
||||
break;
|
||||
|
||||
case Bytecodes::_getfield:
|
||||
case Bytecodes::_putfield:
|
||||
InterpreterRuntime::resolve_get_put(bc, raw_index, mh, cp, false /*initialize_holder*/, CHECK);
|
||||
break;
|
||||
|
||||
case Bytecodes::_invokestatic:
|
||||
if (!VM_Version::supports_fast_class_init_checks()) {
|
||||
return; // Do not resolve since interpreter lacks fast clinit barriers support
|
||||
}
|
||||
InterpreterRuntime::cds_resolve_invoke(bc, raw_index, cp, CHECK);
|
||||
is_static = " *** static";
|
||||
break;
|
||||
|
||||
case Bytecodes::_invokevirtual:
|
||||
case Bytecodes::_invokespecial:
|
||||
case Bytecodes::_invokeinterface:
|
||||
@ -328,11 +354,11 @@ void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m
|
||||
bool resolved = cp->is_resolved(raw_index, bc);
|
||||
Symbol* name = cp->name_ref_at(raw_index, bc);
|
||||
Symbol* signature = cp->signature_ref_at(raw_index, bc);
|
||||
log_trace(aot, resolve)("%s %s [%3d] %s -> %s.%s:%s",
|
||||
log_trace(aot, resolve)("%s %s [%3d] %s -> %s.%s:%s%s",
|
||||
(resolved ? "Resolved" : "Failed to resolve"),
|
||||
Bytecodes::name(bc), cp_index, ik->external_name(),
|
||||
resolved_klass->external_name(),
|
||||
name->as_C_string(), signature->as_C_string());
|
||||
name->as_C_string(), signature->as_C_string(), is_static);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -277,7 +277,9 @@ void ClassListWriter::write_resolved_constants_for(InstanceKlass* ik) {
|
||||
if (field_entries != nullptr) {
|
||||
for (int i = 0; i < field_entries->length(); i++) {
|
||||
ResolvedFieldEntry* rfe = field_entries->adr_at(i);
|
||||
if (rfe->is_resolved(Bytecodes::_getfield) ||
|
||||
if (rfe->is_resolved(Bytecodes::_getstatic) ||
|
||||
rfe->is_resolved(Bytecodes::_putstatic) ||
|
||||
rfe->is_resolved(Bytecodes::_getfield) ||
|
||||
rfe->is_resolved(Bytecodes::_putfield)) {
|
||||
list.at_put(rfe->constant_pool_index(), true);
|
||||
print = true;
|
||||
@ -292,6 +294,7 @@ void ClassListWriter::write_resolved_constants_for(InstanceKlass* ik) {
|
||||
if (rme->is_resolved(Bytecodes::_invokevirtual) ||
|
||||
rme->is_resolved(Bytecodes::_invokespecial) ||
|
||||
rme->is_resolved(Bytecodes::_invokeinterface) ||
|
||||
rme->is_resolved(Bytecodes::_invokestatic) ||
|
||||
rme->is_resolved(Bytecodes::_invokehandle)) {
|
||||
list.at_put(rme->constant_pool_index(), true);
|
||||
print = true;
|
||||
|
||||
@ -89,7 +89,9 @@ void FinalImageRecipes::record_recipes_for_constantpool() {
|
||||
if (field_entries != nullptr) {
|
||||
for (int i = 0; i < field_entries->length(); i++) {
|
||||
ResolvedFieldEntry* rfe = field_entries->adr_at(i);
|
||||
if (rfe->is_resolved(Bytecodes::_getfield) ||
|
||||
if (rfe->is_resolved(Bytecodes::_getstatic) ||
|
||||
rfe->is_resolved(Bytecodes::_putstatic) ||
|
||||
rfe->is_resolved(Bytecodes::_getfield) ||
|
||||
rfe->is_resolved(Bytecodes::_putfield)) {
|
||||
cp_indices.append(rfe->constant_pool_index());
|
||||
flags |= CP_RESOLVE_FIELD_AND_METHOD;
|
||||
|
||||
@ -912,6 +912,7 @@ void InterpreterRuntime::cds_resolve_invoke(Bytecodes::Code bytecode, int method
|
||||
switch (bytecode) {
|
||||
case Bytecodes::_invokevirtual: LinkResolver::cds_resolve_virtual_call (call_info, link_info, CHECK); break;
|
||||
case Bytecodes::_invokeinterface: LinkResolver::cds_resolve_interface_call(call_info, link_info, CHECK); break;
|
||||
case Bytecodes::_invokestatic: LinkResolver::cds_resolve_static_call (call_info, link_info, CHECK); break;
|
||||
case Bytecodes::_invokespecial: LinkResolver::cds_resolve_special_call (call_info, link_info, CHECK); break;
|
||||
|
||||
default: fatal("Unimplemented: %s", Bytecodes::name(bytecode));
|
||||
|
||||
@ -1126,6 +1126,10 @@ void LinkResolver::resolve_static_call(CallInfo& result,
|
||||
JFR_ONLY(Jfr::on_resolution(result, CHECK);)
|
||||
}
|
||||
|
||||
void LinkResolver::cds_resolve_static_call(CallInfo& result, const LinkInfo& link_info, TRAPS) {
|
||||
resolve_static_call(result, link_info, /*initialize_class*/false, CHECK);
|
||||
}
|
||||
|
||||
// throws linktime exceptions
|
||||
Method* LinkResolver::linktime_resolve_static_method(const LinkInfo& link_info, TRAPS) {
|
||||
|
||||
|
||||
@ -328,6 +328,7 @@ class LinkResolver: AllStatic {
|
||||
|
||||
static void cds_resolve_virtual_call (CallInfo& result, const LinkInfo& link_info, TRAPS);
|
||||
static void cds_resolve_interface_call(CallInfo& result, const LinkInfo& link_info, TRAPS);
|
||||
static void cds_resolve_static_call(CallInfo& result, const LinkInfo& link_info, TRAPS);
|
||||
static void cds_resolve_special_call (CallInfo& result, const LinkInfo& link_info, TRAPS);
|
||||
|
||||
// same as above for compile-time resolution; but returns null handle instead of throwing
|
||||
|
||||
@ -175,7 +175,8 @@ void ConstantPoolCache::set_direct_or_vtable_call(Bytecodes::Code invoke_code,
|
||||
}
|
||||
if (invoke_code == Bytecodes::_invokestatic) {
|
||||
assert(method->method_holder()->is_initialized() ||
|
||||
method->method_holder()->is_reentrant_initialization(JavaThread::current()),
|
||||
method->method_holder()->is_reentrant_initialization(JavaThread::current()) ||
|
||||
(CDSConfig::is_dumping_archive() && VM_Version::supports_fast_class_init_checks()),
|
||||
"invalid class initialization state for invoke_static");
|
||||
|
||||
if (!VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) {
|
||||
@ -428,8 +429,13 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() {
|
||||
ResolvedFieldEntry* rfi = _resolved_field_entries->adr_at(i);
|
||||
int cp_index = rfi->constant_pool_index();
|
||||
bool archived = false;
|
||||
bool resolved = rfi->is_resolved(Bytecodes::_getfield) ||
|
||||
rfi->is_resolved(Bytecodes::_putfield);
|
||||
bool resolved = false;
|
||||
|
||||
if (rfi->is_resolved(Bytecodes::_getfield) || rfi->is_resolved(Bytecodes::_putfield) ||
|
||||
((rfi->is_resolved(Bytecodes::_getstatic) || rfi->is_resolved(Bytecodes::_putstatic)) && VM_Version::supports_fast_class_init_checks())) {
|
||||
resolved = true;
|
||||
}
|
||||
|
||||
if (resolved && !CDSConfig::is_dumping_preimage_static_archive()
|
||||
&& AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) {
|
||||
rfi->mark_and_relocate();
|
||||
@ -444,11 +450,12 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() {
|
||||
Symbol* klass_name = cp->klass_name_at(klass_cp_index);
|
||||
Symbol* name = cp->uncached_name_ref_at(cp_index);
|
||||
Symbol* signature = cp->uncached_signature_ref_at(cp_index);
|
||||
log.print("%s field CP entry [%3d]: %s => %s.%s:%s",
|
||||
log.print("%s field CP entry [%3d]: %s => %s.%s:%s%s",
|
||||
(archived ? "archived" : "reverted"),
|
||||
cp_index,
|
||||
cp->pool_holder()->name()->as_C_string(),
|
||||
klass_name->as_C_string(), name->as_C_string(), signature->as_C_string());
|
||||
klass_name->as_C_string(), name->as_C_string(), signature->as_C_string(),
|
||||
rfi->is_resolved(Bytecodes::_getstatic) || rfi->is_resolved(Bytecodes::_putstatic) ? " *** static" : "");
|
||||
}
|
||||
ArchiveBuilder::alloc_stats()->record_field_cp_entry(archived, resolved && !archived);
|
||||
}
|
||||
@ -464,10 +471,8 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() {
|
||||
bool resolved = rme->is_resolved(Bytecodes::_invokevirtual) ||
|
||||
rme->is_resolved(Bytecodes::_invokespecial) ||
|
||||
rme->is_resolved(Bytecodes::_invokeinterface) ||
|
||||
rme->is_resolved(Bytecodes::_invokehandle);
|
||||
|
||||
// Just for safety -- this should not happen, but do not archive if we ever see this.
|
||||
resolved &= !(rme->is_resolved(Bytecodes::_invokestatic));
|
||||
rme->is_resolved(Bytecodes::_invokehandle) ||
|
||||
(rme->is_resolved(Bytecodes::_invokestatic) && VM_Version::supports_fast_class_init_checks());
|
||||
|
||||
if (resolved && !CDSConfig::is_dumping_preimage_static_archive()
|
||||
&& can_archive_resolved_method(src_cp, rme)) {
|
||||
@ -495,8 +500,8 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() {
|
||||
resolved_klass->name()->as_C_string(),
|
||||
(rme->is_resolved(Bytecodes::_invokestatic) ? " *** static" : ""));
|
||||
}
|
||||
ArchiveBuilder::alloc_stats()->record_method_cp_entry(archived, resolved && !archived);
|
||||
}
|
||||
ArchiveBuilder::alloc_stats()->record_method_cp_entry(archived, resolved && !archived);
|
||||
}
|
||||
}
|
||||
|
||||
@ -534,6 +539,7 @@ void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() {
|
||||
}
|
||||
|
||||
bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry) {
|
||||
LogStreamHandle(Trace, aot, resolve) log;
|
||||
InstanceKlass* pool_holder = constant_pool()->pool_holder();
|
||||
if (pool_holder->defined_by_other_loaders()) {
|
||||
// Archiving resolved cp entries for classes from non-builtin loaders
|
||||
@ -554,6 +560,12 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv
|
||||
if (method_entry->method()->is_continuation_native_intrinsic()) {
|
||||
return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call).
|
||||
}
|
||||
if (method_entry->is_resolved(Bytecodes::_invokehandle) && !CDSConfig::is_dumping_method_handles()) {
|
||||
return false;
|
||||
}
|
||||
if (method_entry->method()->is_method_handle_intrinsic() && !CDSConfig::is_dumping_method_handles()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int cp_index = method_entry->constant_pool_index();
|
||||
@ -562,21 +574,7 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv
|
||||
if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method_entry->is_resolved(Bytecodes::_invokeinterface) ||
|
||||
method_entry->is_resolved(Bytecodes::_invokevirtual) ||
|
||||
method_entry->is_resolved(Bytecodes::_invokespecial)) {
|
||||
return true;
|
||||
} else if (method_entry->is_resolved(Bytecodes::_invokehandle)) {
|
||||
if (CDSConfig::is_dumping_method_handles()) {
|
||||
// invokehandle depends on archived MethodType and LambdaForms.
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif // INCLUDE_CDS
|
||||
|
||||
|
||||
@ -58,6 +58,22 @@
|
||||
* @run main/othervm -Dcds.app.tester.workflow=DYNAMIC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. ResolvedConstants DYNAMIC
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=aot
|
||||
* @summary Dump time resolution of constant pool entries (AOT workflow).
|
||||
* @requires vm.cds
|
||||
* @requires vm.cds.supports.aot.class.linking
|
||||
* @requires vm.compMode != "Xcomp"
|
||||
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/
|
||||
* @build OldProvider OldClass OldConsumer StringConcatTestOld
|
||||
* @build ResolvedConstants
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
|
||||
* ResolvedConstantsApp ResolvedConstantsFoo ResolvedConstantsBar
|
||||
* MyInterface InterfaceWithClinit NormalClass
|
||||
* OldProvider OldClass OldConsumer SubOfOldClass
|
||||
* StringConcatTest StringConcatTestOld
|
||||
* @run driver ResolvedConstants AOT --two-step-training
|
||||
*/
|
||||
import java.util.function.Consumer;
|
||||
import jdk.test.lib.cds.CDSOptions;
|
||||
import jdk.test.lib.cds.CDSTestUtils;
|
||||
@ -122,12 +138,15 @@ public class ResolvedConstants {
|
||||
.shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsBar.a:I"))
|
||||
.shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsFoo.a:I"))
|
||||
.shouldMatch(ALWAYS("field.* ResolvedConstantsFoo => ResolvedConstantsFoo.a:I"))
|
||||
.shouldMatch(ALWAYS("field.* ResolvedConstantsApp => ResolvedConstantsApp.static_i:I"))
|
||||
|
||||
// Resolve field references to child classes ONLY when using -XX:+AOTClassLinking
|
||||
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.static_b:I"))
|
||||
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.a:I"))
|
||||
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.b:I"))
|
||||
|
||||
// Resolve field references to unrelated classes ONLY when using -XX:+AOTClassLinking
|
||||
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.static_b:I"))
|
||||
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.a:I"))
|
||||
.shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.b:I"));
|
||||
|
||||
@ -150,8 +169,8 @@ public class ResolvedConstants {
|
||||
.shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.privateInstanceCall:"))
|
||||
.shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.publicInstanceCall:"))
|
||||
|
||||
// Should not resolve references to static method
|
||||
.shouldNotMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.staticCall:"))
|
||||
// Should resolve references to static method
|
||||
.shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.staticCall:"))
|
||||
|
||||
// Should resolve references to method in super type
|
||||
.shouldMatch(ALWAYS("method.*: ResolvedConstantsBar ResolvedConstantsFoo.doBar:"))
|
||||
@ -164,7 +183,12 @@ public class ResolvedConstants {
|
||||
.shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp java/io/PrintStream.println:"))
|
||||
.shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsBar java/lang/Class.getName:"))
|
||||
|
||||
// Resole resolve methods in unrelated classes ONLY when using -XX:+AOTClassLinking
|
||||
// Resolve method references to child classes ONLY when using -XX:+AOTClassLinking
|
||||
.shouldMatch(AOTLINK_ONLY("method.* ResolvedConstantsFoo ResolvedConstantsBar.static_doit"))
|
||||
.shouldMatch(AOTLINK_ONLY("method.* ResolvedConstantsFoo ResolvedConstantsBar.doit2"))
|
||||
|
||||
// Resolve methods in unrelated classes ONLY when using -XX:+AOTClassLinking
|
||||
.shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp ResolvedConstantsBar.static_doit:"))
|
||||
.shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp ResolvedConstantsBar.doit:"))
|
||||
|
||||
// End ---
|
||||
@ -203,11 +227,14 @@ class ResolvedConstantsApp implements Runnable {
|
||||
System.out.println("Hello ResolvedConstantsApp");
|
||||
ResolvedConstantsApp app = new ResolvedConstantsApp();
|
||||
ResolvedConstantsApp.staticCall();
|
||||
ResolvedConstantsApp.static_i ++;
|
||||
app.privateInstanceCall();
|
||||
app.publicInstanceCall();
|
||||
Object a = app;
|
||||
((Runnable)a).run();
|
||||
|
||||
ResolvedConstantsBar.static_b += 10;
|
||||
ResolvedConstantsBar.static_doit();
|
||||
ResolvedConstantsFoo foo = new ResolvedConstantsFoo();
|
||||
ResolvedConstantsBar bar = new ResolvedConstantsBar();
|
||||
bar.a ++;
|
||||
@ -218,6 +245,7 @@ class ResolvedConstantsApp implements Runnable {
|
||||
StringConcatTest.test();
|
||||
StringConcatTestOld.main(null);
|
||||
}
|
||||
private static int static_i = 10;
|
||||
private static void staticCall() {}
|
||||
private void privateInstanceCall() {}
|
||||
public void publicInstanceCall() {}
|
||||
@ -313,13 +341,21 @@ class ResolvedConstantsFoo {
|
||||
}
|
||||
|
||||
void doBar(ResolvedConstantsBar bar) {
|
||||
ResolvedConstantsBar.static_b += 1;
|
||||
ResolvedConstantsBar.static_doit();
|
||||
|
||||
bar.a ++;
|
||||
bar.b ++;
|
||||
bar.doit2();
|
||||
}
|
||||
}
|
||||
|
||||
class ResolvedConstantsBar extends ResolvedConstantsFoo {
|
||||
public static int static_b = 10;
|
||||
int b = 2;
|
||||
public static void static_doit() {
|
||||
}
|
||||
|
||||
void doit() {
|
||||
System.out.println("Hello ResolvedConstantsBar and " + ResolvedConstantsFoo.class.getName());
|
||||
System.out.println("a = " + a);
|
||||
@ -330,4 +366,8 @@ class ResolvedConstantsBar extends ResolvedConstantsFoo {
|
||||
|
||||
((ResolvedConstantsFoo)this).doBar(this);
|
||||
}
|
||||
|
||||
void doit2() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user