Add interface fast path to art_quick_check_cast for X86_64
X86_64 CC ritzperf results from perf:
art_quick_check_cast: 0.44% -> 0.76%
artIsAssignableFromCode: 1.78% -> 0.11%
Added stub test.
Bug: 32577579
Test: test-art-host
Change-Id: I5ed5675c4674fac8eed8826eb50527f4876e5f07
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 4638c3f..c151f00 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -819,34 +819,60 @@
ScopedObjectAccess soa(self);
// garbage is created during ClassLinker::Init
- StackHandleScope<2> hs(soa.Self());
+ StackHandleScope<4> hs(soa.Self());
Handle<mirror::Class> c(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")));
Handle<mirror::Class> c2(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;")));
+ Handle<mirror::Class> list(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/util/List;")));
+ Handle<mirror::Class> array_list(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/util/ArrayList;")));
EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c.Get()),
+ reinterpret_cast<size_t>(c.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c2.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c2.Get()),
+ reinterpret_cast<size_t>(c2.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c2.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c.Get()),
+ reinterpret_cast<size_t>(c2.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_FALSE(self->IsExceptionPending());
+ Invoke3(reinterpret_cast<size_t>(list.Get()),
+ reinterpret_cast<size_t>(array_list.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ Invoke3(reinterpret_cast<size_t>(list.Get()),
+ reinterpret_cast<size_t>(c2.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
+ EXPECT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+
// TODO: Make the following work. But that would require correct managed frames.
-
- Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c2.Get()),
+ reinterpret_cast<size_t>(c.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_TRUE(self->IsExceptionPending());
self->ClearException();
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index afa1c0f..49e1b56 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1481,6 +1481,32 @@
END_FUNCTION art_quick_unlock_object_no_inline
DEFINE_FUNCTION art_quick_check_cast
+ testl LITERAL(ACCESS_FLAGS_CLASS_IS_INTERFACE), MIRROR_CLASS_ACCESS_FLAGS_OFFSET(%rdi)
+ jz .Lnot_interface
+
+ // There are no read barriers since the iftable is immutable. There can be false negatives for
+ // the read barrier case if classes in the IfTable are in the from-space. In the case where
+ // we do not find a matching interface we call into artIsAssignableFromCode which will have
+ // read barriers.
+ movl MIRROR_CLASS_IF_TABLE_OFFSET(%rsi), %ecx
+ UNPOISON_HEAP_REF %ecx
+ testl %ecx, %ecx
+ jz .Lnot_interface
+ movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), %r8d
+.Lstart_loop:
+ // Re-poison before comparing to prevent rare possible false positives. This is done inside
+ // the loop since heap poisoning is only for testing builds.
+ POISON_HEAP_REF %edi
+ cmpl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rcx), %edi
+ je .Lreturn // Return if same class.
+ UNPOISON_HEAP_REF %edi
+ // Go to next interface.
+ add LITERAL(COMPRESSED_REFERENCE_SIZE * 2), %rcx
+ sub LITERAL(2), %r8
+ jnz .Lstart_loop
+
+.Lnot_interface:
+ // We could check the super classes here but that is usually already checked in the caller.
PUSH rdi // Save args for exc
PUSH rsi
subq LITERAL(8), %rsp // Alignment padding.
@@ -1493,6 +1519,7 @@
addq LITERAL(24), %rsp // pop arguments
CFI_ADJUST_CFA_OFFSET(-24)
+.Lreturn:
ret
CFI_ADJUST_CFA_OFFSET(24 + 4 * 8) // Reset unwind info so following code unwinds.