Implement Interface Method Tables (IMT).
Change-Id: Idf7fe85e1293453a8ad862ff2380dcd5db4e3a39
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 6ea21fc..56facfd 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -44,6 +44,8 @@
kRet0,
kRet1,
kInvokeTgt,
+ kHiddenArg,
+ kHiddenFpArg,
kCount
};
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 3395ae7..52aba9b 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -74,6 +74,8 @@
case kRet0: res = rARM_RET0; break;
case kRet1: res = rARM_RET1; break;
case kInvokeTgt: res = rARM_INVOKE_TGT; break;
+ case kHiddenArg: res = r12; break;
+ case kHiddenFpArg: res = INVALID_REG; break;
case kCount: res = rARM_COUNT; break;
}
return res;
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 62feade..0a5fbb1 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -491,69 +491,56 @@
}
/*
- * All invoke-interface calls bounce off of art_quick_invoke_interface_trampoline,
- * which will locate the target and continue on via a tail call.
+ * Emit the next instruction in an invoke interface sequence. This will do a lookup in the
+ * class's IMT, calling either the actual method or art_quick_imt_conflict_trampoline if
+ * more than one interface method map to the same index. Note also that we'll load the first
+ * argument ("this") into kArg1 here rather than the standard LoadArgRegs.
*/
static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state,
const MethodReference& target_method,
- uint32_t unused, uintptr_t unused2,
- uintptr_t direct_method, InvokeType unused4) {
+ uint32_t method_idx, uintptr_t unused,
+ uintptr_t direct_method, InvokeType unused2) {
Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
- ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline);
- if (direct_method != 0) {
- switch (state) {
- case 0: // Load the trampoline target [sets kInvokeTgt].
- if (cu->instruction_set != kX86) {
- cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(),
- cg->TargetReg(kInvokeTgt));
- }
- // Get the interface Method* [sets kArg0]
- if (direct_method != static_cast<unsigned int>(-1)) {
- cg->LoadConstant(cg->TargetReg(kArg0), direct_method);
- } else {
- CHECK_EQ(cu->dex_file, target_method.dex_file);
- LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_,
- target_method.dex_method_index, 0);
- if (data_target == NULL) {
- data_target = cg->AddWordData(&cg->method_literal_list_,
- target_method.dex_method_index);
- data_target->operands[1] = kInterface;
- }
- LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target);
- cg->AppendLIR(load_pc_rel);
- DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target);
- }
- break;
- default:
- return -1;
- }
- } else {
- switch (state) {
- case 0:
- // Get the current Method* [sets kArg0] - TUNING: remove copy of method if it is promoted.
- cg->LoadCurrMethodDirect(cg->TargetReg(kArg0));
- // Load the trampoline target [sets kInvokeTgt].
- if (cu->instruction_set != kX86) {
- cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(),
- cg->TargetReg(kInvokeTgt));
- }
- break;
- case 1: // Get method->dex_cache_resolved_methods_ [set/use kArg0]
- cg->LoadWordDisp(cg->TargetReg(kArg0),
- mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- cg->TargetReg(kArg0));
- break;
- case 2: // Grab target method* [set/use kArg0]
+ switch (state) {
+ case 0: // Set target method index in case of conflict [set kHiddenArg, kHiddenFpArg (x86)]
CHECK_EQ(cu->dex_file, target_method.dex_file);
- cg->LoadWordDisp(cg->TargetReg(kArg0),
- mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
- (target_method.dex_method_index * 4),
+ CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
+ cg->LoadConstant(cg->TargetReg(kHiddenArg), target_method.dex_method_index);
+ if (cu->instruction_set == kX86) {
+ cg->OpRegCopy(cg->TargetReg(kHiddenFpArg), cg->TargetReg(kHiddenArg));
+ }
+ break;
+ case 1: { // Get "this" [set kArg1]
+ RegLocation rl_arg = info->args[0];
+ cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1));
+ break;
+ }
+ case 2: // Is "this" null? [use kArg1]
+ cg->GenNullCheck(info->args[0].s_reg_low, cg->TargetReg(kArg1), info->opt_flags);
+ // Get this->klass_ [use kArg1, set kInvokeTgt]
+ cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt]
+ cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ case 4: // Get target method [use kInvokeTgt, set kArg0]
+ cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), ((method_idx % ClassLinker::kImtSize) * 4) +
+ mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(),
cg->TargetReg(kArg0));
break;
+ case 5: // Get the compiled code address [use kArg0, set kInvokeTgt]
+ if (cu->instruction_set != kX86) {
+ cg->LoadWordDisp(cg->TargetReg(kArg0),
+ mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ }
+ // Intentional fallthrough for X86
default:
return -1;
- }
}
return state + 1;
}
@@ -1390,11 +1377,8 @@
&vtable_idx,
&direct_code, &direct_method) && !SLOW_INVOKE_PATH;
if (info->type == kInterface) {
- if (fast_path) {
- p_null_ck = &null_ck;
- }
next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck;
- skip_this = false;
+ skip_this = fast_path;
} else if (info->type == kDirect) {
if (fast_path) {
p_null_ck = &null_ck;
@@ -1434,15 +1418,14 @@
if (cu_->instruction_set != kX86) {
call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt));
} else {
- if (fast_path && info->type != kInterface) {
+ if (fast_path) {
call_inst = OpMem(kOpBlx, TargetReg(kArg0),
mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value());
} else {
ThreadOffset trampoline(-1);
switch (info->type) {
case kInterface:
- trampoline = fast_path ? QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline)
- : QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck);
+ trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck);
break;
case kDirect:
trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck);
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index 0ee32d4..9c598e6 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -76,6 +76,8 @@
case kRet0: res = rMIPS_RET0; break;
case kRet1: res = rMIPS_RET1; break;
case kInvokeTgt: res = rMIPS_INVOKE_TGT; break;
+ case kHiddenArg: res = r_T0; break;
+ case kHiddenFpArg: res = INVALID_REG; break;
case kCount: res = rMIPS_COUNT; break;
}
return res;
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 901ac9e..878fa76 100644
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -85,6 +85,8 @@
case kRet0: res = rX86_RET0; break;
case kRet1: res = rX86_RET1; break;
case kInvokeTgt: res = rX86_INVOKE_TGT; break;
+ case kHiddenArg: res = rAX; break;
+ case kHiddenFpArg: res = fr0; break;
case kCount: res = rX86_COUNT; break;
}
return res;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 91b0188..053ea16 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -469,6 +469,11 @@
return CreateTrampoline(instruction_set_, kJniAbi, JNI_ENTRYPOINT_OFFSET(pDlsymLookup));
}
+const std::vector<uint8_t>* CompilerDriver::CreatePortableImtConflictTrampoline() const {
+ return CreateTrampoline(instruction_set_, kPortableAbi,
+ PORTABLE_ENTRYPOINT_OFFSET(pPortableImtConflictTrampoline));
+}
+
const std::vector<uint8_t>* CompilerDriver::CreatePortableResolutionTrampoline() const {
return CreateTrampoline(instruction_set_, kPortableAbi,
PORTABLE_ENTRYPOINT_OFFSET(pPortableResolutionTrampoline));
@@ -479,6 +484,11 @@
PORTABLE_ENTRYPOINT_OFFSET(pPortableToInterpreterBridge));
}
+const std::vector<uint8_t>* CompilerDriver::CreateQuickImtConflictTrampoline() const {
+ return CreateTrampoline(instruction_set_, kQuickAbi,
+ QUICK_ENTRYPOINT_OFFSET(pQuickImtConflictTrampoline));
+}
+
const std::vector<uint8_t>* CompilerDriver::CreateQuickResolutionTrampoline() const {
return CreateTrampoline(instruction_set_, kQuickAbi,
QUICK_ENTRYPOINT_OFFSET(pQuickResolutionTrampoline));
@@ -1080,7 +1090,7 @@
}
use_dex_cache = true;
} else {
- if (sharp_type != kStatic && sharp_type != kDirect && sharp_type != kInterface) {
+ if (sharp_type != kStatic && sharp_type != kDirect) {
return;
}
// TODO: support patching on all architectures.
@@ -1101,9 +1111,7 @@
}
}
if (update_stats && method_code_in_boot) {
- if (sharp_type != kInterface) { // Interfaces always go via a trampoline until we get IMTs.
- stats_->DirectCallsToBoot(*type);
- }
+ stats_->DirectCallsToBoot(*type);
stats_->DirectMethodsToBoot(*type);
}
if (!use_dex_cache && compiling_boot) {
@@ -1145,19 +1153,15 @@
if (compiling_boot) {
*type = sharp_type;
*direct_method = -1;
- if (sharp_type != kInterface) {
- *direct_code = -1;
- }
+ *direct_code = -1;
} else {
bool method_in_image =
Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace();
if (method_in_image) {
- CHECK_EQ(method->IsAbstract(), sharp_type == kInterface);
+ CHECK(!method->IsAbstract());
*type = sharp_type;
*direct_method = reinterpret_cast<uintptr_t>(method);
- if (*type != kInterface) {
- *direct_code = reinterpret_cast<uintptr_t>(method->GetEntryPointFromCompiledCode());
- }
+ *direct_code = reinterpret_cast<uintptr_t>(method->GetEntryPointFromCompiledCode());
target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
target_method->dex_method_index = method->GetDexMethodIndex();
} else if (!must_use_direct_pointers) {
@@ -1187,6 +1191,8 @@
if (resolved_method != NULL) {
if (*invoke_type == kVirtual || *invoke_type == kSuper) {
*vtable_idx = resolved_method->GetMethodIndex();
+ } else if (*invoke_type == kInterface) {
+ *vtable_idx = resolved_method->GetDexMethodIndex();
}
// Don't try to fast-path if we don't understand the caller's class or this appears to be an
// Incompatible Class Change Error.
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 971021f..c791753 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -130,10 +130,14 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateJniDlsymLookup() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const std::vector<uint8_t>* CreatePortableImtConflictTrampoline() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreatePortableResolutionTrampoline() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreatePortableToInterpreterBridge() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickToInterpreterBridge() const
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 871cfd5..af61bcc 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -98,11 +98,15 @@
jni_dlsym_lookup_offset_ = oat_file_->GetOatHeader().GetJniDlsymLookupOffset();
+ portable_imt_conflict_trampoline_offset_ =
+ oat_file_->GetOatHeader().GetPortableImtConflictTrampolineOffset();
portable_resolution_trampoline_offset_ =
oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset();
portable_to_interpreter_bridge_offset_ =
oat_file_->GetOatHeader().GetPortableToInterpreterBridgeOffset();
+ quick_imt_conflict_trampoline_offset_ =
+ oat_file_->GetOatHeader().GetQuickImtConflictTrampolineOffset();
quick_resolution_trampoline_offset_ =
oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset();
quick_to_interpreter_bridge_offset_ =
@@ -390,6 +394,8 @@
ObjectArray<Object>::Alloc(self, object_array_class,
ImageHeader::kImageRootsMax));
image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod());
+ image_roots->Set(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod());
+ image_roots->Set(ImageHeader::kDefaultImt, runtime->GetDefaultImt());
image_roots->Set(ImageHeader::kCalleeSaveMethod,
runtime->GetCalleeSaveMethod(Runtime::kSaveAll));
image_roots->Set(ImageHeader::kRefsOnlySaveMethod,
@@ -527,6 +533,12 @@
#else
copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
#endif
+ } else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
+#if defined(ART_USE_PORTABLE_COMPILER)
+ copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_imt_conflict_trampoline_offset_));
+#else
+ copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_imt_conflict_trampoline_offset_));
+#endif
} else {
// We assume all methods have code. If they don't currently then we set them to the use the
// resolution trampoline. Abstract methods never have code and so we need to make sure their
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 0d85f36..0b408e8 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -40,7 +40,8 @@
explicit ImageWriter(const CompilerDriver& compiler_driver)
: compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
- interpreter_to_compiled_code_bridge_offset_(0), portable_resolution_trampoline_offset_(0),
+ interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
+ portable_resolution_trampoline_offset_(0), quick_imt_conflict_trampoline_offset_(0),
quick_resolution_trampoline_offset_(0) {}
~ImageWriter() {}
@@ -204,8 +205,10 @@
uint32_t interpreter_to_interpreter_bridge_offset_;
uint32_t interpreter_to_compiled_code_bridge_offset_;
uint32_t jni_dlsym_lookup_offset_;
+ uint32_t portable_imt_conflict_trampoline_offset_;
uint32_t portable_resolution_trampoline_offset_;
uint32_t portable_to_interpreter_bridge_offset_;
+ uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
uint32_t quick_to_interpreter_bridge_offset_;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index af86743..815bca5 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -149,7 +149,7 @@
TEST_F(OatTest, OatHeaderSizeCheck) {
// If this test is failing and you have to update these constants,
// it is time to update OatHeader::kOatVersion
- EXPECT_EQ(64U, sizeof(OatHeader));
+ EXPECT_EQ(72U, sizeof(OatHeader));
EXPECT_EQ(28U, sizeof(OatMethodOffsets));
}
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index f681d7d..28355bf 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -55,8 +55,10 @@
size_interpreter_to_interpreter_bridge_(0),
size_interpreter_to_compiled_code_bridge_(0),
size_jni_dlsym_lookup_(0),
+ size_portable_imt_conflict_trampoline_(0),
size_portable_resolution_trampoline_(0),
size_portable_to_interpreter_bridge_(0),
+ size_quick_imt_conflict_trampoline_(0),
size_quick_resolution_trampoline_(0),
size_quick_to_interpreter_bridge_(0),
size_trampoline_alignment_(0),
@@ -229,8 +231,10 @@
DO_TRAMPOLINE(interpreter_to_interpreter_bridge_, InterpreterToInterpreterBridge);
DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_, InterpreterToCompiledCodeBridge);
DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup);
+ DO_TRAMPOLINE(portable_imt_conflict_trampoline_, PortableImtConflictTrampoline);
DO_TRAMPOLINE(portable_resolution_trampoline_, PortableResolutionTrampoline);
DO_TRAMPOLINE(portable_to_interpreter_bridge_, PortableToInterpreterBridge);
+ DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline);
DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline);
DO_TRAMPOLINE(quick_to_interpreter_bridge_, QuickToInterpreterBridge);
@@ -239,8 +243,10 @@
oat_header_->SetInterpreterToInterpreterBridgeOffset(0);
oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0);
oat_header_->SetJniDlsymLookupOffset(0);
+ oat_header_->SetPortableImtConflictTrampolineOffset(0);
oat_header_->SetPortableResolutionTrampolineOffset(0);
oat_header_->SetPortableToInterpreterBridgeOffset(0);
+ oat_header_->SetQuickImtConflictTrampolineOffset(0);
oat_header_->SetQuickResolutionTrampolineOffset(0);
oat_header_->SetQuickToInterpreterBridgeOffset(0);
}
@@ -519,8 +525,10 @@
DO_STAT(size_interpreter_to_interpreter_bridge_);
DO_STAT(size_interpreter_to_compiled_code_bridge_);
DO_STAT(size_jni_dlsym_lookup_);
+ DO_STAT(size_portable_imt_conflict_trampoline_);
DO_STAT(size_portable_resolution_trampoline_);
DO_STAT(size_portable_to_interpreter_bridge_);
+ DO_STAT(size_quick_imt_conflict_trampoline_);
DO_STAT(size_quick_resolution_trampoline_);
DO_STAT(size_quick_to_interpreter_bridge_);
DO_STAT(size_trampoline_alignment_);
@@ -616,8 +624,10 @@
DO_TRAMPOLINE(interpreter_to_interpreter_bridge_);
DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_);
DO_TRAMPOLINE(jni_dlsym_lookup_);
+ DO_TRAMPOLINE(portable_imt_conflict_trampoline_);
DO_TRAMPOLINE(portable_resolution_trampoline_);
DO_TRAMPOLINE(portable_to_interpreter_bridge_);
+ DO_TRAMPOLINE(quick_imt_conflict_trampoline_);
DO_TRAMPOLINE(quick_resolution_trampoline_);
DO_TRAMPOLINE(quick_to_interpreter_bridge_);
#undef DO_TRAMPOLINE
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index e3cb0a8..5d947cf 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -226,8 +226,10 @@
UniquePtr<const std::vector<uint8_t> > interpreter_to_interpreter_bridge_;
UniquePtr<const std::vector<uint8_t> > interpreter_to_compiled_code_bridge_;
UniquePtr<const std::vector<uint8_t> > jni_dlsym_lookup_;
+ UniquePtr<const std::vector<uint8_t> > portable_imt_conflict_trampoline_;
UniquePtr<const std::vector<uint8_t> > portable_resolution_trampoline_;
UniquePtr<const std::vector<uint8_t> > portable_to_interpreter_bridge_;
+ UniquePtr<const std::vector<uint8_t> > quick_imt_conflict_trampoline_;
UniquePtr<const std::vector<uint8_t> > quick_resolution_trampoline_;
UniquePtr<const std::vector<uint8_t> > quick_to_interpreter_bridge_;
@@ -240,8 +242,10 @@
uint32_t size_interpreter_to_interpreter_bridge_;
uint32_t size_interpreter_to_compiled_code_bridge_;
uint32_t size_jni_dlsym_lookup_;
+ uint32_t size_portable_imt_conflict_trampoline_;
uint32_t size_portable_resolution_trampoline_;
uint32_t size_portable_to_interpreter_bridge_;
+ uint32_t size_quick_imt_conflict_trampoline_;
uint32_t size_quick_resolution_trampoline_;
uint32_t size_quick_to_interpreter_bridge_;
uint32_t size_trampoline_alignment_;