8361569: [JVMCI] Further refine JVMCI-compiled nmethod that should not collect deoptimization profile

Reviewed-by: dnsimon, gdub
This commit is contained in:
Yudi Zheng 2025-07-09 19:12:37 +00:00
parent 7282f68cee
commit 6681fc72d3
18 changed files with 81 additions and 47 deletions

View File

@ -1936,10 +1936,7 @@ void nmethod::inc_decompile_count() {
if (!is_compiled_by_c2() && !is_compiled_by_jvmci()) return;
// Could be gated by ProfileTraps, but do not bother...
#if INCLUDE_JVMCI
// Deoptimization count is used by the CompileBroker to reason about compilations
// it requests so do not pollute the count for deoptimizations in non-default (i.e.
// non-CompilerBroker) compilations.
if (is_jvmci_hosted()) {
if (jvmci_skip_profile_deopt()) {
return;
}
#endif
@ -4066,7 +4063,7 @@ const char* nmethod::jvmci_name() {
return nullptr;
}
bool nmethod::is_jvmci_hosted() const {
return jvmci_nmethod_data() != nullptr && !jvmci_nmethod_data()->is_default();
bool nmethod::jvmci_skip_profile_deopt() const {
return jvmci_nmethod_data() != nullptr && !jvmci_nmethod_data()->profile_deopt();
}
#endif

View File

@ -914,9 +914,9 @@ public:
return jvmci_data_size() == 0 ? nullptr : (JVMCINMethodData*) jvmci_data_begin();
}
// Returns true if a JVMCI compiled method is non-default,
// i.e., not triggered by CompilerBroker
bool is_jvmci_hosted() const;
// Returns true if the runtime should NOT collect deoptimization profile for a JVMCI
// compiled method
bool jvmci_skip_profile_deopt() const;
#endif
void oops_do(OopClosure* f) { oops_do(f, false); }

View File

@ -2890,11 +2890,12 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle, jbool
JVMCIObject methodObject = thisEnv->get_HotSpotNmethod_method(obj);
methodHandle mh(THREAD, thisEnv->asMethod(methodObject));
jboolean isDefault = thisEnv->get_HotSpotNmethod_isDefault(obj);
jboolean profileDeopt = thisEnv->get_HotSpotNmethod_profileDeopt(obj);
jlong compileIdSnapshot = thisEnv->get_HotSpotNmethod_compileIdSnapshot(obj);
JVMCIObject name_string = thisEnv->get_InstalledCode_name(obj);
const char* cstring = name_string.is_null() ? nullptr : thisEnv->as_utf8_string(name_string);
// Create a new HotSpotNmethod instance in the peer runtime
result = PEER_JVMCIENV->new_HotSpotNmethod(mh, cstring, isDefault, compileIdSnapshot, JVMCI_CHECK_0);
result = PEER_JVMCIENV->new_HotSpotNmethod(mh, cstring, isDefault, profileDeopt, compileIdSnapshot, JVMCI_CHECK_0);
JVMCINMethodHandle nmethod_handle(THREAD);
nmethod* nm = JVMCIENV->get_nmethod(obj, nmethod_handle);
if (result.is_null()) {

View File

@ -1210,7 +1210,7 @@ JVMCIObject JVMCIEnv::new_StackTraceElement(const methodHandle& method, int bci,
}
}
JVMCIObject JVMCIEnv::new_HotSpotNmethod(const methodHandle& method, const char* name, jboolean isDefault, jlong compileId, JVMCI_TRAPS) {
JVMCIObject JVMCIEnv::new_HotSpotNmethod(const methodHandle& method, const char* name, jboolean isDefault, jboolean profileDeopt, jlong compileId, JVMCI_TRAPS) {
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros.
JVMCIObject methodObject = get_jvmci_method(method, JVMCI_CHECK_(JVMCIObject()));
@ -1230,11 +1230,12 @@ JVMCIObject JVMCIEnv::new_HotSpotNmethod(const methodHandle& method, const char*
jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(methodObject)));
jargs.push_oop(nameStr);
jargs.push_int(isDefault);
jargs.push_int(profileDeopt);
jargs.push_long(compileId);
JavaValue result(T_VOID);
JavaCalls::call_special(&result, ik,
vmSymbols::object_initializer_name(),
vmSymbols::method_string_bool_long_signature(),
vmSymbols::method_string_bool_bool_long_signature(),
&jargs, CHECK_(JVMCIObject()));
return wrap(obj_h());
} else {

View File

@ -426,7 +426,7 @@ public:
JVMCIObjectArray new_byte_array_array(int length, JVMCI_TRAPS);
JVMCIObject new_StackTraceElement(const methodHandle& method, int bci, JVMCI_TRAPS);
JVMCIObject new_HotSpotNmethod(const methodHandle& method, const char* name, jboolean isDefault, jlong compileId, JVMCI_TRAPS);
JVMCIObject new_HotSpotNmethod(const methodHandle& method, const char* name, jboolean isDefault, jboolean profileDeopt, jlong compileId, JVMCI_TRAPS);
JVMCIObject new_VMField(JVMCIObject name, JVMCIObject type, jlong offset, jlong address, JVMCIObject value, JVMCI_TRAPS);
JVMCIObject new_VMFlag(JVMCIObject name, JVMCIObject type, JVMCIObject value, JVMCI_TRAPS);
JVMCIObject new_VMIntrinsicMethod(JVMCIObject declaringClass, JVMCIObject name, JVMCIObject descriptor, int id, jboolean isAvailable, jboolean c1Supported, jboolean c2Supported, JVMCI_TRAPS);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2024, 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.
*
* This code is free software; you can redistribute it and/or modify it
@ -100,10 +100,11 @@
end_class \
start_class(HotSpotNmethod, jdk_vm_ci_hotspot_HotSpotNmethod) \
boolean_field(HotSpotNmethod, isDefault) \
boolean_field(HotSpotNmethod, profileDeopt) \
long_field(HotSpotNmethod, compileIdSnapshot) \
object_field(HotSpotNmethod, method, "Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;") \
int_field(HotSpotNmethod, invalidationReason) \
jvmci_constructor(HotSpotNmethod, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;Ljava/lang/String;ZJ)V") \
jvmci_constructor(HotSpotNmethod, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;Ljava/lang/String;ZZJ)V") \
end_class \
start_class(HotSpotCompiledCode, jdk_vm_ci_hotspot_HotSpotCompiledCode) \
primarray_field(HotSpotCompiledCode, targetCode, "[B") \

View File

@ -747,6 +747,7 @@ void JVMCINMethodData::initialize(int nmethod_mirror_index,
int nmethod_entry_patch_offset,
const char* nmethod_mirror_name,
bool is_default,
bool profile_deopt,
FailedSpeculation** failed_speculations)
{
_failed_speculations = failed_speculations;
@ -761,10 +762,12 @@ void JVMCINMethodData::initialize(int nmethod_mirror_index,
_properties.bits._has_name = 0;
}
_properties.bits._is_default = is_default;
_properties.bits._profile_deopt = profile_deopt;
}
void JVMCINMethodData::copy(JVMCINMethodData* data) {
initialize(data->_nmethod_mirror_index, data->_nmethod_entry_patch_offset, data->name(), data->_properties.bits._is_default, data->_failed_speculations);
initialize(data->_nmethod_mirror_index, data->_nmethod_entry_patch_offset, data->name(), data->_properties.bits._is_default,
data->_properties.bits._profile_deopt, data->_failed_speculations);
}
void JVMCINMethodData::add_failed_speculation(nmethod* nm, jlong speculation) {
@ -2086,6 +2089,7 @@ JVMCI::CodeInstallResult JVMCIRuntime::register_method(JVMCIEnv* JVMCIENV,
char* failure_detail = nullptr;
bool install_default = JVMCIENV->get_HotSpotNmethod_isDefault(nmethod_mirror) != 0;
bool profile_deopt = JVMCIENV->get_HotSpotNmethod_profileDeopt(nmethod_mirror) != 0;
assert(JVMCIENV->isa_HotSpotNmethod(nmethod_mirror), "must be");
JVMCIObject name = JVMCIENV->get_InstalledCode_name(nmethod_mirror);
const char* nmethod_mirror_name = name.is_null() ? nullptr : JVMCIENV->as_utf8_string(name);
@ -2154,6 +2158,7 @@ JVMCI::CodeInstallResult JVMCIRuntime::register_method(JVMCIEnv* JVMCIENV,
nmethod_entry_patch_offset,
nmethod_mirror_name,
install_default,
profile_deopt,
failed_speculations);
nm = nmethod::new_nmethod(method,
compile_id,

View File

@ -53,10 +53,12 @@ class JVMCINMethodData : public ResourceObj {
struct {
// Is HotSpotNmethod.name non-null? If so, the value is
// embedded in the end of this object.
uint8_t _has_name : 1,
uint8_t _has_name : 1,
// HotSpotNmethod.isDefault (e.g., compilation scheduled by CompileBroker)
_is_default : 1,
: 6;
_is_default : 1,
// HotSpotNmethod.profileDeopt
_profile_deopt : 1,
: 5;
} bits;
};
@ -87,6 +89,7 @@ class JVMCINMethodData : public ResourceObj {
int nmethod_entry_patch_offset,
const char* nmethod_mirror_name,
bool is_default,
bool profile_deopt,
FailedSpeculation** failed_speculations);
void* operator new(size_t size, const char* nmethod_mirror_name) {
@ -100,12 +103,14 @@ public:
int nmethod_entry_patch_offset,
const char* nmethod_mirror_name,
bool is_default,
bool profile_deopt,
FailedSpeculation** failed_speculations) {
JVMCINMethodData* result = new (nmethod_mirror_name) JVMCINMethodData();
result->initialize(nmethod_mirror_index,
nmethod_entry_patch_offset,
nmethod_mirror_name,
is_default,
profile_deopt,
failed_speculations);
return result;
}
@ -153,6 +158,10 @@ public:
bool is_default() {
return _properties.bits._is_default;
}
bool profile_deopt() {
return _properties.bits._profile_deopt;
}
};
// A top level class that represents an initialized JVMCI runtime.

View File

@ -102,7 +102,7 @@
template(bootstrapFinished_name, "bootstrapFinished") \
template(forPrimitive_name, "forPrimitive") \
template(forPrimitive_signature, "(CJ)Ljdk/vm/ci/meta/PrimitiveConstant;") \
template(method_string_bool_long_signature, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;Ljava/lang/String;ZJ)V") \
template(method_string_bool_bool_long_signature, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;Ljava/lang/String;ZZJ)V") \
#endif

View File

@ -2367,7 +2367,7 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr
// Deoptimization count is used by the CompileBroker to reason about compilations
// it requests so do not pollute the count for deoptimizations in non-default (i.e.
// non-CompilerBroker) compilations.
if (nm->is_jvmci_hosted()) {
if (nm->jvmci_skip_profile_deopt()) {
update_trap_state = false;
}
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 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
@ -42,13 +42,15 @@ public interface CodeCacheProvider {
* @param installedCode a predefined {@link InstalledCode} object to use as a reference to the
* installed code. If {@code null}, a new {@link InstalledCode} object will be
* created.
* @param profileDeopt specifies if HotSpot should profile deoptimizations for the
* {@code nmethod} associated with this object.
* @return a reference to the ready-to-run code
* @throws BailoutException if the code installation failed
* @throws IllegalArgumentException if {@code installedCode != null} and this object does not
* support a predefined {@link InstalledCode} object
*/
default InstalledCode addCode(ResolvedJavaMethod method, CompiledCode compiledCode, SpeculationLog log, InstalledCode installedCode) {
return installCode(method, compiledCode, installedCode, log, false);
default InstalledCode addCode(ResolvedJavaMethod method, CompiledCode compiledCode, SpeculationLog log, InstalledCode installedCode, boolean profileDeopt) {
return installCode(method, compiledCode, installedCode, log, false, profileDeopt);
}
/**
@ -64,7 +66,7 @@ public interface CodeCacheProvider {
* support a predefined {@link InstalledCode} object
*/
default InstalledCode setDefaultCode(ResolvedJavaMethod method, CompiledCode compiledCode) {
return installCode(method, compiledCode, null, null, true);
return installCode(method, compiledCode, null, null, true, true);
}
/**
@ -81,10 +83,12 @@ public interface CodeCacheProvider {
* {@code compRequest.getMethod()}. The default implementation for a method is the
* code executed for standard calls to the method. This argument is ignored if
* {@code compRequest == null}.
* @param profileDeopt specifies if HotSpot should profile deoptimizations for the
* {@code nmethod} associated with this object.
* @return a reference to the compiled and ready-to-run installed code
* @throws BailoutException if the code installation failed
*/
InstalledCode installCode(ResolvedJavaMethod method, CompiledCode compiledCode, InstalledCode installedCode, SpeculationLog log, boolean isDefault);
InstalledCode installCode(ResolvedJavaMethod method, CompiledCode compiledCode, InstalledCode installedCode, SpeculationLog log, boolean isDefault, boolean profileDeopt);
/**
* Invalidates {@code installedCode} such that {@link InvalidInstalledCodeException} will be

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 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
@ -23,8 +23,6 @@
/**
* Package that defines the interface between a Java application that wants to install code and the
* runtime. The runtime provides in implementation of the {@link jdk.vm.ci.code.CodeCacheProvider}
* interface. The method
* {@link jdk.vm.ci.code.CodeCacheProvider#addCode(jdk.vm.ci.meta.ResolvedJavaMethod, CompiledCode, jdk.vm.ci.meta.SpeculationLog, InstalledCode)}
* can be used to install code.
* interface. The method {@link jdk.vm.ci.code.CodeCacheProvider#addCode} can be used to install code.
*/
package jdk.vm.ci.code;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 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
@ -101,7 +101,7 @@ public class HotSpotCodeCacheProvider implements CodeCacheProvider {
}
@Override
public InstalledCode installCode(ResolvedJavaMethod method, CompiledCode compiledCode, InstalledCode installedCode, SpeculationLog log, boolean isDefault) {
public InstalledCode installCode(ResolvedJavaMethod method, CompiledCode compiledCode, InstalledCode installedCode, SpeculationLog log, boolean isDefault, boolean profileDeopt) {
InstalledCode resultInstalledCode;
if (installedCode != null) {
throw new IllegalArgumentException("InstalledCode argument must be null");
@ -131,7 +131,7 @@ public class HotSpotCodeCacheProvider implements CodeCacheProvider {
} else {
hsCompiledNmethod = (HotSpotCompiledNmethod) hsCompiledCode;
HotSpotResolvedJavaMethodImpl hsMethod = (HotSpotResolvedJavaMethodImpl) method;
HotSpotNmethod nmethod = new HotSpotNmethod(hsMethod, name, isDefault, hsCompiledNmethod.id);
HotSpotNmethod nmethod = new HotSpotNmethod(hsMethod, name, isDefault, profileDeopt, hsCompiledNmethod.id);
nmethod.setSpeculationLog(speculationLog);
resultInstalledCode = nmethod;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2019, 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.
*
* This code is free software; you can redistribute it and/or modify it
@ -54,6 +54,13 @@ public class HotSpotNmethod extends HotSpotInstalledCode {
*/
private final boolean isDefault;
/**
* Specifies whether HotSpot should profile deoptimizations for the {@code nmethod} associated
* with this object. This is particularly useful for whitebox testing scenarios that involve
* deoptimization.
*/
private final boolean profileDeopt;
/**
* Determines whether this object is in the oops table of the nmethod.
* <p>
@ -83,10 +90,11 @@ public class HotSpotNmethod extends HotSpotInstalledCode {
*/
private int invalidationReason;
HotSpotNmethod(HotSpotResolvedJavaMethodImpl method, String name, boolean isDefault, long compileId) {
HotSpotNmethod(HotSpotResolvedJavaMethodImpl method, String name, boolean isDefault, boolean profileDeopt, long compileId) {
super(name);
this.method = method;
this.isDefault = isDefault;
this.profileDeopt = profileDeopt;
boolean inOopsTable = !IS_IN_NATIVE_IMAGE && !isDefault;
this.compileIdSnapshot = inOopsTable ? 0L : compileId;
this.invalidationReason = -1;
@ -103,12 +111,13 @@ public class HotSpotNmethod extends HotSpotInstalledCode {
/**
* The speculation log containing speculations embedded in the nmethod.
*
* <p>
* If {@code speculationLog.managesFailedSpeculations() == true}, this field ensures the failed
* speculation list lives at least as long as this object. This prevents deoptimization from
* appending to an already freed list.
*/
@SuppressWarnings("unused") private HotSpotSpeculationLog speculationLog;
@SuppressWarnings("unused")
private HotSpotSpeculationLog speculationLog;
/**
* Determines if the nmethod associated with this object is the compiled entry point for
@ -118,6 +127,14 @@ public class HotSpotNmethod extends HotSpotInstalledCode {
return isDefault;
}
/**
* Determines if HotSpot should profile deoptimization for the {@code nmethod} associated
* with this object.
*/
public boolean profileDeopt() {
return profileDeopt;
}
@Override
public boolean isValid() {
if (compileIdSnapshot != 0L) {
@ -133,7 +150,8 @@ public class HotSpotNmethod extends HotSpotInstalledCode {
/**
* Invalidate this nmethod using the reason specified in {@code invalidationReason} and
* optionally deoptimize the method if {@code deoptimize} is set.
* @param deoptimize whether or not to deoptimize the method.
*
* @param deoptimize whether or not to deoptimize the method.
* @param invalidationReason invalidation reason code.
*/
public void invalidate(boolean deoptimize, int invalidationReason) {
@ -164,7 +182,7 @@ public class HotSpotNmethod extends HotSpotInstalledCode {
@Override
public String toString() {
return String.format("HotSpotNmethod[method=%s, codeBlob=0x%x, isDefault=%b, name=%s, inOopsTable=%s]",
method, getAddress(), isDefault, name, inOopsTable());
method, getAddress(), isDefault, name, inOopsTable());
}
private boolean checkArgs(Object... args) {
@ -183,7 +201,7 @@ public class HotSpotNmethod extends HotSpotInstalledCode {
/**
* {@inheritDoc}
*
* <p>
* It's possible for the HotSpot runtime to sweep nmethods at any point in time. As a result,
* there is no guarantee that calling this method will execute the wrapped nmethod. Instead, it
* may end up executing the bytecode of the associated {@link #getMethod() Java method}. Only if

View File

@ -112,7 +112,7 @@ public class CodeInstallerTest {
hasUnsafeAccess);
SpeculationLog log = null;
InstalledCode installedCode = null;
return codeCache.addCode(dummyMethod, code, log, installedCode);
return codeCache.addCode(dummyMethod, code, log, installedCode, true);
}
protected Register getRegister(PlatformKind kind, int index) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -306,7 +306,7 @@ public class CompilerToVMHelper {
}
private static class InstalledCodeStub extends HotSpotNmethod {
private InstalledCodeStub(HotSpotResolvedJavaMethodImpl method, String name, long address, long entryPoint) {
super(method, name, false, 0);
super(method, name, false, true, 0);
this.address = address;
this.entryPoint = entryPoint;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -106,7 +106,7 @@ public class CodeInstallationTest {
asm.emitEpilogue();
HotSpotCompiledCode code = asm.finish(resolvedMethod);
InstalledCode installed = codeCache.addCode(resolvedMethod, code, null, null);
InstalledCode installed = codeCache.addCode(resolvedMethod, code, null, null, true);
if (DEBUG) {
String str = ((HotSpotCodeCacheProvider) codeCache).disassemble(installed);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 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
@ -76,7 +76,7 @@ public class RuntimeStubAllocFailTest {
/* totalFrameSize */ 0,
/* deoptRescueSlot */ null);
try {
codeCache.installCode(null, stub, null, null, true);
codeCache.installCode(null, stub, null, null, true, true);
throw new AssertionError("Didn't get expected " + BailoutException.class.getName());
} catch (BailoutException e) {
Asserts.assertEQ(e.getMessage(), "Error installing " + stubToFail + ": code cache is full");