summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Almaz Mingaleev <mingaleev@google.com> 2024-10-30 15:35:14 +0000
committer Almaz Mingaleev <mingaleev@google.com> 2024-11-06 16:24:17 +0000
commitcc22b57fd30005bdb03495412408a56c7ba08fb6 (patch)
treed46d259e79dba7d6a481a579fd9420b6c2d8d451
parent52f22d080cc077eaf11a1c32bb07f87339343cd4 (diff)
Don't virtual dispatch non-copied method defined in an interface.
Following what Class::FindVirtualMethodForVirtualOrInterface does. Currently findVirtual sets MH's kind to INVOKE_INTERFACE only if class on which lookup is done is interface. Bug: 297147201 Test: ./art/test/testrunner/testrunner.py -b --host --64 Change-Id: Ib2576f317f7932111a05e464334de69a00e24ca1
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc26
-rw-r--r--test/2277-methodhandle-invokeexact/src/Main.java144
2 files changed, 157 insertions, 13 deletions
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 38954c8fa1..5e7c3a9611 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -163,7 +163,7 @@ class InvokePolymorphicSlowPathX86_64 : public SlowPathCode {
SaveLiveRegisters(codegen, instruction_->GetLocations());
// Passing `MethodHandle` object as hidden argument.
- __ movq(CpuRegister(RDI), method_handle_);
+ __ movl(CpuRegister(RDI), method_handle_);
x86_64_codegen->InvokeRuntime(QuickEntrypointEnum::kQuickInvokePolymorphicWithHiddenReceiver,
instruction_,
instruction_->GetDexPc());
@@ -4279,10 +4279,28 @@ void IntrinsicCodeGeneratorX86_64::VisitMethodHandleInvokeExact(HInvoke* invoke)
__ testl(Address(method, ArtMethod::AccessFlagsOffset()), Immediate(kAccPrivate));
__ j(kNotZero, &execute_target_method);
- CpuRegister vtable_index = locations->GetTemp(0).AsRegister<CpuRegister>();
+ Label do_virtual_dispatch;
+ CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+
+ __ movl(temp, Address(method, ArtMethod::DeclaringClassOffset()));
+ __ cmpl(temp, Address(receiver, mirror::Object::ClassOffset()));
+ // If method is defined in the receiver's class, execute it as it is.
+ __ j(kEqual, &execute_target_method);
+
+ __ testl(Address(temp, mirror::Class::AccessFlagsOffset()), Immediate(kAccInterface));
+ // If `method`'s declaring class is not an interface, do virtual dispatch.
+ __ j(kZero, &do_virtual_dispatch);
+
+ __ movl(temp, Address(method, ArtMethod::AccessFlagsOffset()));
+ // These flags are uint32_t and their signed value doesn't fit into int32_t (see b/377275405).
+ __ andl(temp, Immediate(bit_cast<int32_t, uint32_t>(kAccIntrinsic | kAccCopied)));
+ __ cmpl(temp, Immediate(kAccCopied));
+ // If method is defined in an interface and is not copied it should be interface dispatched.
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ __ Bind(&do_virtual_dispatch);
// MethodIndex is uint16_t.
- __ movzxw(vtable_index, Address(method, ArtMethod::MethodIndexOffset()));
+ __ movzxw(temp, Address(method, ArtMethod::MethodIndexOffset()));
constexpr uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
// Re-using method register for receiver class.
@@ -4290,7 +4308,7 @@ void IntrinsicCodeGeneratorX86_64::VisitMethodHandleInvokeExact(HInvoke* invoke)
constexpr uint32_t vtable_offset =
mirror::Class::EmbeddedVTableOffset(art::PointerSize::k64).Int32Value();
- __ movq(method, Address(method, vtable_index, TIMES_8, vtable_offset));
+ __ movq(method, Address(method, temp, TIMES_8, vtable_offset));
__ Jump(&execute_target_method);
}
__ Bind(&static_dispatch);
diff --git a/test/2277-methodhandle-invokeexact/src/Main.java b/test/2277-methodhandle-invokeexact/src/Main.java
index bb8c5fec01..a34e5eef60 100644
--- a/test/2277-methodhandle-invokeexact/src/Main.java
+++ b/test/2277-methodhandle-invokeexact/src/Main.java
@@ -33,6 +33,8 @@ public class Main {
Multi.$noinline$testMHFromMain(OPTIONAL_GET);
$noinline$testWithArgs();
$noinline$nullchecks();
+ $noinline$interfaceChecks();
+ $noinline$abstractClass();
}
private static void $noinline$nullchecks() throws Throwable {
@@ -47,6 +49,16 @@ public class Main {
} catch (WrongMethodTypeException expected) {}
try {
+ INTERFACE_DEFAULT_METHOD.invokeExact((I) null);
+ unreachable("Receiver is null, should throw NPE");
+ } catch (NullPointerException expected) {}
+
+ try {
+ INTERFACE_DEFAULT_METHOD.invokeExact((A) null);
+ unreachable("Should throw WMTE: input is of wrong type");
+ } catch (WrongMethodTypeException expected) {}
+
+ try {
MethodHandle mh = null;
mh.invokeExact();
unreachable("MethodHandle object is null, should throw NPE");
@@ -160,6 +172,56 @@ public class Main {
assertEquals(55L, lsum);
}
+ private static void $noinline$interfaceChecks() throws Throwable {
+ FooBarImpl instance = new FooBarImpl();
+
+ String result = null;
+ result = (String) FOO_NONDEFAULT.invokeExact((Foo) instance);
+ assertEquals("FooBarImpl.nonDefault", result);
+
+ result = (String) FOOBARIMPL_NONDEFAULT.invokeExact(instance);
+ assertEquals("FooBarImpl.nonDefault", result);
+
+ result = (String) BAR_DEFAULT.invokeExact((Bar) instance);
+ assertEquals("Bar.defaultToOverride", result);
+
+ result = (String) FOO_DEFAULT.invokeExact((Foo) instance);
+ assertEquals("Bar.defaultToOverride", result);
+
+ result = (String) FOOBARIMPL_DEFAULT.invokeExact(instance);
+ assertEquals("Bar.defaultToOverride", result);
+
+ result = (String) FOO_NONOVERRIDDEN_DEFAULT.invokeExact((Foo) instance);
+ assertEquals("Foo.nonOverriddenDefault", result);
+
+ result = (String) BAR_NONOVERRIDDEN_DEFAULT.invokeExact((Bar) instance);
+ assertEquals("Foo.nonOverriddenDefault", result);
+ }
+
+ private static void $noinline$abstractClass() throws Throwable {
+ FooBarImpl instance = new FooBarImpl();
+
+ String result = null;
+ result = (String) FOOBAR_DEFINEDINABSTRACT.invokeExact((FooBar) instance);
+ assertEquals("FooBar.definedInAbstract", result);
+
+ result = (String) FOOBARIMPL_DEFINEDINABSTRACT.invokeExact(instance);
+ assertEquals("FooBar.definedInAbstract", result);
+
+ FooBar fooBar = new FooBar() {
+ @Override
+ public String nonDefault() {
+ return "anonymous.nonDefault";
+ }
+ };
+
+ result = (String) FOOBAR_DEFINEDINABSTRACT.invokeExact(fooBar);
+ assertEquals("FooBar.definedInAbstract", result);
+
+ result = (String) FOOBAR_NONDEFAULT.invokeExact(fooBar);
+ assertEquals("anonymous.nonDefault", result);
+ }
+
private static void assertEquals(Object expected, Object actual) {
if (!Objects.equals(expected, actual)) {
throw new AssertionError("Expected: " + expected + ", got: " + actual);
@@ -217,6 +279,17 @@ public class Main {
private static final MethodHandle SUM_4IJ;
private static final MethodHandle SUM_5IJ;
+ private static final MethodHandle FOO_NONDEFAULT;
+ private static final MethodHandle FOOBARIMPL_NONDEFAULT;
+ private static final MethodHandle FOO_DEFAULT;
+ private static final MethodHandle BAR_DEFAULT;
+ private static final MethodHandle FOOBAR_DEFINEDINABSTRACT;
+ private static final MethodHandle FOOBAR_NONDEFAULT;
+ private static final MethodHandle FOOBARIMPL_DEFINEDINABSTRACT;
+ private static final MethodHandle FOOBARIMPL_DEFAULT;
+ private static final MethodHandle FOO_NONOVERRIDDEN_DEFAULT;
+ private static final MethodHandle BAR_NONOVERRIDDEN_DEFAULT;
+
static {
try {
VOID_METHOD = MethodHandles.lookup()
@@ -263,27 +336,48 @@ public class Main {
SUM_10I = MethodHandles.lookup()
.findVirtual(Sums.class, "sum", methodType(int.class, repeat(10, int.class)));
- SUM_IJ = MethodHandles.lookup()
+ SUM_IJ = MethodHandles.lookup()
.findVirtual(Sums.class, "sum", methodType(long.class, int.class, long.class));
SUM_2IJ = MethodHandles.lookup()
.findVirtual(Sums.class,
"sum",
methodType(long.class, repeat(2, int.class, long.class)));
- SUM_3IJ = MethodHandles.lookup()
+ SUM_3IJ = MethodHandles.lookup()
.findVirtual(Sums.class,
"sum",
methodType(long.class, repeat(3, int.class, long.class)));
- SUM_4IJ = MethodHandles.lookup()
+ SUM_4IJ = MethodHandles.lookup()
.findVirtual(Sums.class,
"sum",
methodType(long.class, repeat(4, int.class, long.class)));
- SUM_5IJ = MethodHandles.lookup()
+ SUM_5IJ = MethodHandles.lookup()
.findVirtual(Sums.class,
"sum",
methodType(long.class, repeat(5, int.class, long.class)));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+
+ FOO_NONDEFAULT = MethodHandles.lookup()
+ .findVirtual(Foo.class, "nonDefault", methodType(String.class));
+ FOOBARIMPL_NONDEFAULT = MethodHandles.lookup()
+ .findVirtual(FooBarImpl.class, "nonDefault", methodType(String.class));
+ FOO_DEFAULT = MethodHandles.lookup()
+ .findVirtual(Foo.class, "defaultToOverride", methodType(String.class));
+ BAR_DEFAULT = MethodHandles.lookup()
+ .findVirtual(Bar.class, "defaultToOverride", methodType(String.class));
+ FOOBAR_DEFINEDINABSTRACT = MethodHandles.lookup()
+ .findVirtual(FooBar.class, "definedInAbstract", methodType(String.class));
+ FOOBAR_NONDEFAULT = MethodHandles.lookup()
+ .findVirtual(FooBar.class, "nonDefault", methodType(String.class));
+ FOOBARIMPL_DEFINEDINABSTRACT = MethodHandles.lookup()
+ .findVirtual(FooBarImpl.class, "definedInAbstract", methodType(String.class));
+ FOOBARIMPL_DEFAULT = MethodHandles.lookup()
+ .findVirtual(FooBarImpl.class, "defaultToOverride", methodType(String.class));
+ FOO_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup()
+ .findVirtual(Foo.class, "nonOverriddenDefault", methodType(String.class));
+ BAR_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup()
+ .findVirtual(Bar.class, "nonOverriddenDefault", methodType(String.class));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
private static Class<?>[] repeat(int times, Class<?> clazz) {
@@ -301,6 +395,40 @@ public class Main {
return classes;
}
+ static interface Foo {
+ default String defaultToOverride() {
+ return "Foo.defaultToOverride";
+ }
+
+ default String nonOverriddenDefault() {
+ return "Foo.nonOverriddenDefault";
+ }
+
+ String nonDefault();
+ }
+
+ static interface Bar extends Foo {
+ @Override
+ default String defaultToOverride() {
+ return "Bar.defaultToOverride";
+ }
+ }
+
+ static abstract class FooBar implements Foo, Bar {
+ public String definedInAbstract() {
+ return "FooBar.definedInAbstract";
+ }
+ }
+
+ static class FooBarImpl extends FooBar {
+ @Override
+ public String nonDefault() {
+ return "FooBarImpl.nonDefault";
+ }
+ }
+
+ static class MyRuntimeException extends RuntimeException {}
+
static interface I {
public default void defaultMethod() {
STATUS = "I.defaultMethod";
@@ -315,8 +443,6 @@ public class Main {
}
}
- static class MyRuntimeException extends RuntimeException {}
-
static class A extends B implements I {
public int field;
public void voidMethod() {