Skip to content
Draft
1 change: 1 addition & 0 deletions src/hotspot/share/logging/logTag.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class outputStream;
// (The tags 'all', 'disable' and 'help' are special tags that can
// not be used in log calls, and should not be listed below.)
#define LOG_TAG_LIST \
LOG_TAG(adapters) \
LOG_TAG(add) \
LOG_TAG(age) \
LOG_TAG(alloc) \
Expand Down
87 changes: 87 additions & 0 deletions src/hotspot/share/runtime/sharedRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2896,6 +2896,33 @@ GrowableArray<Method*>* CompiledEntrySignature::get_supers() {
return _supers;
}

bool AdapterHandlerEntry::check_interface_calling_conventions(Method* super_method, Array<InstanceKlass*>* interfaces) {
bool has_scalarized = super_method->has_scalarized_args();
for (int i = 0; i < interfaces->length(); i++) {
Method* interface_method = interfaces->at(i)->lookup_method(super_method->name(), super_method->signature());
if (interface_method != nullptr) {
// Check for scalarized/non-scalarized mismatch
if ((interface_method->has_scalarized_args() && !has_scalarized) || (!interface_method->has_scalarized_args() && has_scalarized)) {
log_info(adapters)("Super method %s has scalarized/non-scalarized calling convention mismatch with interface method %s",
super_method->external_name(), interface_method->external_name());
return false;
}

// Check that calling conventions are consistent
if (interface_method->adapter() != nullptr && interface_method->has_scalarized_args()) {
assert(interfaces->at(i)->is_abstract() || interface_method->adapter() != nullptr, "must be");
AdapterHandlerEntry* adapter = interface_method->adapter();
if (*adapter->get_sig_cc() != *_sig_cc || *adapter->get_sig_cc_ro() != *_sig_cc_ro) {
log_info(adapters)("Super method %s has calling convention mismatch with interface method %s",
super_method->external_name(), interface_method->external_name());
return false;
}
}
}
}
return true;
}

// Iterate over arguments and compute scalarized and non-scalarized signatures
void CompiledEntrySignature::compute_calling_conventions(bool init) {
bool has_scalarized = false;
Expand Down Expand Up @@ -3205,6 +3232,29 @@ void AdapterHandlerLibrary::verify_adapter_sharing(CompiledEntrySignature& ces,
}
#endif /* ASSERT*/

Method* AdapterHandlerLibrary::find_super_method(InstanceKlass* holder, const methodHandle& method) {
Method* super_method;
Symbol* holder_name = holder->name();
JavaThread* current = JavaThread::current();
Handle loader(current, method->method_holder()->class_loader());

// Walk up the class hierarchy and search for super methods
InstanceKlass* super_klass = holder->super();
while (super_klass != nullptr) {
super_method = super_klass->lookup_method(method->name(), method->signature());
if (super_method == nullptr) {
return nullptr;
}
if (!super_method->is_static() && !super_method->is_private() &&
(!super_method->is_package_private() ||
super_method->method_holder()->is_same_class_package(loader(), holder_name))) {
return super_method;
}
super_klass = super_method->method_holder()->super();
}
return nullptr;
}

AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& method) {
assert(!method->is_abstract() || InlineTypePassFieldsAsArgs, "abstract methods do not have adapters");
// Use customized signature handler. Need to lock around updates to
Expand All @@ -3221,6 +3271,43 @@ AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& meth
ResourceMark rm;
bool new_entry = false;

// Inherit adapter from super method as long as it is compatible with adapters from local interfaces
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we wrap this in a if (!method->is_static()) {...} block?

InstanceKlass* holder = method->method_holder();
InstanceKlass* super_klass = holder->super();

if (super_klass != nullptr) {
Method* super_method = find_super_method(holder, method);
if (super_method != nullptr && super_method->adapter() != nullptr) {
log_info(adapters)("Method %s attempting to inherit adapter from %s (scalarized: %s)",
method->external_name(), super_method->external_name(),
super_method->has_scalarized_args() ? "true" : "false");

// Check that interface methods are valid
AdapterHandlerEntry* super_adapter = super_method->adapter();
Array<InstanceKlass*>* interfaces = holder->local_interfaces();
if (super_method->adapter() != nullptr) {
if(super_adapter->check_interface_calling_conventions(super_method, holder->local_interfaces())) {
log_info(adapters)("Method %s successfully inherited adapter from %s (scalarized: %s)",
method->external_name(), super_method->external_name(),
super_method->has_scalarized_args() ? "true" : "false");

if (super_method->has_scalarized_args()) {
method->set_has_scalarized_args();

if (super_method->c1_needs_stack_repair()) {
method->set_c1_needs_stack_repair();
}
if (super_method->c2_needs_stack_repair()) {
method->set_c2_needs_stack_repair();
}
}
return super_adapter;
}
}
log_info(adapters)("Method %s unable to inherit adapter from %s", method->external_name(), super_method->external_name());
}
}

CompiledEntrySignature ces(method());
ces.compute_calling_conventions();
if (ces.has_scalarized_args()) {
Expand Down
3 changes: 3 additions & 0 deletions src/hotspot/share/runtime/sharedRuntime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,8 @@ class AdapterHandlerEntry : public MetaspaceObj {
}
const GrowableArray<SigEntry>* get_sig_cc_ro() const { return _sig_cc_ro; }

bool check_interface_calling_conventions(Method* method, Array<InstanceKlass*>* interfaces);

uint id() const { return _id; }
AdapterFingerPrint* fingerprint() const { return _fingerprint; }

Expand Down Expand Up @@ -916,6 +918,7 @@ class AdapterHandlerLibrary: public AllStatic {

static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint);
static void create_native_wrapper(const methodHandle& method);
static Method* find_super_method(InstanceKlass* holder, const methodHandle& method);
static AdapterHandlerEntry* get_adapter(const methodHandle& method);
static AdapterHandlerEntry* lookup(const GrowableArray<SigEntry>* sig, bool has_ro_adapter = false);
static bool generate_adapter_code(AdapterHandlerEntry* handler,
Expand Down
12 changes: 12 additions & 0 deletions src/hotspot/share/runtime/signature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,18 @@ class SigEntry {
static TempNewSymbol create_symbol(const GrowableArray<SigEntry>* sig);

void print_on(outputStream* st) const;

bool operator==(const SigEntry& other) const {
return (_bt == other._bt) &&
(_offset == other._offset) &&
(_name == other._name) &&
(_null_marker == other._null_marker) &&
(_vt_oop == other._vt_oop);
}

bool operator!=(const SigEntry& other) const {
return !(*this == other);
}
};

class SigEntryFilter {
Expand Down
122 changes: 122 additions & 0 deletions test/hotspot/jtreg/runtime/valhalla/inlinetypes/A.jcod
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2026, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

class A {
0xCAFEBABE;
65535; // minor version
71; // version
[] { // Constant Pool
; // first element is empty
Method #2 #3; // #1
Class #4; // #2
NameAndType #5 #6; // #3
Utf8 "java/lang/Object"; // #4
Utf8 "<init>"; // #5
Utf8 "()V"; // #6
Class #8; // #7
Utf8 "A"; // #8
Class #10; // #9
Utf8 "I"; // #10
Utf8 "Code"; // #11
Utf8 "LineNumberTable"; // #12
Utf8 "foo"; // #13
Utf8 "(LPoint;)V"; // #14
Utf8 "SourceFile"; // #15
Utf8 "SigTest.java"; // #16
Utf8 "LoadableDescriptors"; // #17
Utf8 "LPoint;"; // #18
}

0x0020; // access
#7; // this_cpx
#2; // super_cpx

[] { // Interfaces
#9;
} // end of Interfaces

[] { // Fields
} // end of Fields

[] { // Methods
{ // method
0x0000; // access
#5; // name_index
#6; // descriptor_index
[] { // Attributes
Attr(#11) { // Code
1; // max_stack
1; // max_locals
Bytes[]{
0x2A 0xB7 0x00 0x01 0xB1;
}
[] { // Traps
} // end of Traps
[] { // Attributes
Attr(#12) { // LineNumberTable
[] { // line_number_table
0 35;
}
} // end of LineNumberTable
} // end of Attributes
} // end of Code
} // end of Attributes
}
;
{ // method
0x0001; // access
#13; // name_index
#14; // descriptor_index
[] { // Attributes
Attr(#11) { // Code
0; // max_stack
2; // max_locals
Bytes[]{
0xB1;
}
[] { // Traps
} // end of Traps
[] { // Attributes
Attr(#12) { // LineNumberTable
[] { // line_number_table
0 36;
}
} // end of LineNumberTable
} // end of Attributes
} // end of Code
} // end of Attributes
}
} // end of Methods

[] { // Attributes
Attr(#15) { // SourceFile
#16;
} // end of SourceFile
;
Attr(#17) { // LoadableDescriptors
[] { // Utf8
// Removed LPoint
}
} // end of LoadableDescriptors
} // end of Attributes
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2026, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

public class AdapterInheritanceApp {
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();

for (int i = 0; i < 100000; i++) {
// A.foo() should be non-scalarized because Point is not present in the
// LoadableDescriptors attribute for either class A or interface I
a.foo(new Point(0, 0));

// B.foo() will inherit the adapter from A.foo() and will also be non-scalarized.
b.foo(new Point(0, 1));

// C.foo() will try to inherit the non-scalarizeda dapter from B.foo() but it
// will run into a mismatch when checking J.foo() which has scalarized calling
// conventions for foo(). The calling conventions must then be recalculated.
c.foo(new Point(1, 0));
}
}
}

value class Point {
int x;
int y;

Point(int x, int y) {
this.x = x;
this.y = y;
}
}

// For this test, these classes are derived from JCOD files which remove Point
// from the LoadableDescriptors attribute so that Point will not be loaded when
// trying to link foo()
//
// interface I {
// void foo(Point i);
// }

// class A implements I {
// void foo(Point i) {}
// }

interface J {
void foo(Point i);
}

class B extends A {
public void foo(Point i) {}
}

class C extends B implements J {
public void foo(Point i) {}
}
Loading