diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 978d3da9a11..8754d3d250d 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -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) \ diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 4b86362a231..7469245d949 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -2896,6 +2896,33 @@ GrowableArray* CompiledEntrySignature::get_supers() { return _supers; } +bool AdapterHandlerEntry::check_interface_calling_conventions(Method* super_method, Array* 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; @@ -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 @@ -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 + 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* 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()) { diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index 7f17873c139..58efd12e215 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -861,6 +861,8 @@ class AdapterHandlerEntry : public MetaspaceObj { } const GrowableArray* get_sig_cc_ro() const { return _sig_cc_ro; } + bool check_interface_calling_conventions(Method* method, Array* interfaces); + uint id() const { return _id; } AdapterFingerPrint* fingerprint() const { return _fingerprint; } @@ -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* sig, bool has_ro_adapter = false); static bool generate_adapter_code(AdapterHandlerEntry* handler, diff --git a/src/hotspot/share/runtime/signature.hpp b/src/hotspot/share/runtime/signature.hpp index 20154c9e1f9..e82b77d757f 100644 --- a/src/hotspot/share/runtime/signature.hpp +++ b/src/hotspot/share/runtime/signature.hpp @@ -596,6 +596,18 @@ class SigEntry { static TempNewSymbol create_symbol(const GrowableArray* 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 { diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/A.jcod b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/A.jcod new file mode 100644 index 00000000000..af5bc3e4fc7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/A.jcod @@ -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 ""; // #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 +} diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AdapterInheritanceApp.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AdapterInheritanceApp.java new file mode 100644 index 00000000000..84a55ccce6b --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AdapterInheritanceApp.java @@ -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) {} +} diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AdapterInheritanceTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AdapterInheritanceTest.java new file mode 100644 index 00000000000..3ab1648b809 --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/AdapterInheritanceTest.java @@ -0,0 +1,46 @@ +/* + * 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. + */ + +/* + * @test + * @summary Test that adapters are properly inherited or calculated during method linking + * @enablePreview + * @library /test/lib + * @compile A.jcod I.jcod AdapterInheritanceApp.java + * @run main/othervm AdapterInheritanceTest + */ + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class AdapterInheritanceTest { + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("--enable-preview", "-Xlog:adapters", "AdapterInheritanceApp"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldContain("B.foo(Point) successfully inherited adapter from void A.foo(Point) (scalarized: false)"); + output.shouldContain("B.foo(Point) has scalarized/non-scalarized calling convention mismatch with interface method void J.foo(Point)"); + output.shouldContain("C.foo(Point) unable to inherit adapter from void B.foo(Point)"); + } +} diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/I.jcod b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/I.jcod new file mode 100644 index 00000000000..93ebaa9a74a --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/I.jcod @@ -0,0 +1,73 @@ +/* + * 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. + */ + +file "I.class" { + 0xCAFEBABE; + 65535; // minor version + 71; // version + [] { // Constant Pool + ; // first element is empty + Class #2; // #1 + Utf8 "I"; // #2 + Class #4; // #3 + Utf8 "java/lang/Object"; // #4 + Utf8 "foo"; // #5 + Utf8 "(LPoint;)V"; // #6 + Utf8 "SourceFile"; // #7 + Utf8 "SigTest.java"; // #8 + Utf8 "LoadableDescriptors"; // #9 + Utf8 "LPoint;"; // #10 + } + + 0x0600; // access + #1; // this_cpx + #3; // super_cpx + + [] { // Interfaces + } // end of Interfaces + + [] { // Fields + } // end of Fields + + [] { // Methods + { // method + 0x0401; // access + #5; // name_index + #6; // descriptor_index + [] { // Attributes + } // end of Attributes + } + } // end of Methods + + [] { // Attributes + Attr(#7) { // SourceFile + #8; + } // end of SourceFile + ; + Attr(#9) { // LoadableDescriptors + [] { // Utf8 + // Removed LPoint + } + } // end of LoadableDescriptors + } // end of Attributes +}