Implement method calls using relative BL on ARM64.
Change-Id: I9e5d0b6c100b6cddd6bbb7ab07cff77ab104ea31
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index ab71921..a87b06a 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -117,6 +117,7 @@
#define IS_SIGNED_IMM14(value) IS_SIGNED_IMM(14, value)
#define IS_SIGNED_IMM19(value) IS_SIGNED_IMM(19, value)
#define IS_SIGNED_IMM21(value) IS_SIGNED_IMM(21, value)
+#define IS_SIGNED_IMM26(value) IS_SIGNED_IMM(26, value)
// Quick macro used to define the registers.
#define A64_REGISTER_CODE_LIST(R) \
@@ -240,6 +241,7 @@
kA64B2ct, // b.cond [01010100] imm_19[23-5] [0] cond[3-0].
kA64Blr1x, // blr [1101011000111111000000] rn[9-5] [00000].
kA64Br1x, // br [1101011000011111000000] rn[9-5] [00000].
+ kA64Bl1t, // bl [100101] imm26[25-0].
kA64Brk1d, // brk [11010100001] imm_16[20-5] [00000].
kA64B1t, // b [00010100] offset_26[25-0].
kA64Cbnz2rt, // cbnz[00110101] imm_19[23-5] rt[4-0].
diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc
index b1cf279..7c663a9 100644
--- a/compiler/dex/quick/arm64/assemble_arm64.cc
+++ b/compiler/dex/quick/arm64/assemble_arm64.cc
@@ -155,6 +155,10 @@
kFmtRegX, 9, 5, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE0 | IS_BRANCH,
"br", "!0x", kFixupNone),
+ ENCODING_MAP(kA64Bl1t, NO_VARIANTS(0x94000000),
+ kFmtBitBlt, 25, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP,
+ "bl", "!0T", kFixupLabel),
ENCODING_MAP(kA64Brk1d, NO_VARIANTS(0xd4200000),
kFmtBitBlt, 20, 5, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
@@ -873,7 +877,7 @@
((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
int32_t delta = target - pc;
DCHECK_EQ(delta & 0x3, 0);
- if (!IS_SIGNED_IMM19(delta >> 2)) {
+ if (!IS_SIGNED_IMM26(delta >> 2)) {
LOG(FATAL) << "Invalid jump range in kFixupT1Branch";
}
lir->operands[0] = delta >> 2;
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 6081f28..e8de876 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -21,6 +21,8 @@
#include "dex/quick/mir_to_lir-inl.h"
#include "gc/accounting/card_table.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "mirror/art_method.h"
+#include "mirror/object_array-inl.h"
namespace art {
@@ -433,4 +435,117 @@
NewLIR0(kA64Ret);
}
+static bool Arm64UseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
+ // Always emit relative calls.
+ return true;
+}
+
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in static & direct invoke sequences.
+ */
+static int Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+ int state, const MethodReference& target_method,
+ uint32_t unused,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type) {
+ Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+ if (direct_code != 0 && direct_method != 0) {
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ if (direct_code != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+ } else if (Arm64UseRelativeCall(cu, target_method)) {
+ // Defer to linker patch.
+ } else {
+ cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+ }
+ if (direct_method != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
+ } else {
+ cg->LoadMethodAddress(target_method, type, kArg0);
+ }
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ // TUNING: we can save a reg copy if Method* has been promoted.
+ cg->LoadCurrMethodDirect(arg0_ref);
+ break;
+ case 1: // Get method->dex_cache_resolved_methods_
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ // Set up direct code if known.
+ if (direct_code != 0) {
+ if (direct_code != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+ } else if (Arm64UseRelativeCall(cu, target_method)) {
+ // Defer to linker patch.
+ } else {
+ CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
+ cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+ }
+ }
+ break;
+ case 2: // Grab target method*
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ObjectArray<mirror::Object>::OffsetOfElement(
+ target_method.dex_method_index).Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ break;
+ case 3: // Grab the code from the method*
+ if (direct_code == 0) {
+ // kInvokeTgt := arg0_ref->entrypoint
+ cg->LoadWordDisp(arg0_ref,
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(),
+ cg->TargetPtrReg(kInvokeTgt));
+ }
+ break;
+ default:
+ return -1;
+ }
+ }
+ return state + 1;
+}
+
+NextCallInsn Arm64Mir2Lir::GetNextSDCallInsn() {
+ return Arm64NextSDCallInsn;
+}
+
+LIR* Arm64Mir2Lir::CallWithLinkerFixup(const MethodReference& target_method, InvokeType type) {
+ // For ARM64, just generate a relative BL instruction that will be filled in at 'link time'.
+ // If the target turns out to be too far, the linker will generate a thunk for dispatch.
+ int target_method_idx = target_method.dex_method_index;
+ const DexFile* target_dex_file = target_method.dex_file;
+
+ // Generate the call instruction and save index, dex_file, and type.
+ // NOTE: Method deduplication takes linker patches into account, so we can just pass 0
+ // as a placeholder for the offset.
+ LIR* call = RawLIR(current_dalvik_offset_, kA64Bl1t, 0,
+ target_method_idx, WrapPointer(const_cast<DexFile*>(target_dex_file)), type);
+ AppendLIR(call);
+ call_method_insns_.push_back(call);
+ return call;
+}
+
+LIR* Arm64Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
+ LIR* call_insn;
+ if (method_info.FastPath() && Arm64UseRelativeCall(cu_, method_info.GetTargetMethod()) &&
+ (method_info.GetSharpType() == kDirect || method_info.GetSharpType() == kStatic) &&
+ method_info.DirectCode() == static_cast<uintptr_t>(-1)) {
+ call_insn = CallWithLinkerFixup(method_info.GetTargetMethod(), method_info.GetSharpType());
+ } else {
+ call_insn = OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
+ }
+ return call_insn;
+}
+
} // namespace art
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 55cc938..93d9b34 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -259,6 +259,28 @@
size_t GetInstructionOffset(LIR* lir) OVERRIDE;
+ NextCallInsn GetNextSDCallInsn() OVERRIDE;
+
+ /*
+ * @brief Generate a relative call to the method that will be patched at link time.
+ * @param target_method The MethodReference of the method to be invoked.
+ * @param type How the method will be invoked.
+ * @returns Call instruction
+ */
+ LIR* CallWithLinkerFixup(const MethodReference& target_method, InvokeType type);
+
+ /*
+ * @brief Generate the actual call insn based on the method info.
+ * @param method_info the lowering info for the method call.
+ * @returns Call instruction
+ */
+ virtual LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE;
+
+ /*
+ * @brief Handle ARM specific literals.
+ */
+ void InstallLiteralPools() OVERRIDE;
+
LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
private:
@@ -396,6 +418,8 @@
InToRegStorageMapping in_to_reg_storage_mapping_;
static const A64EncodingMap EncodingMap[kA64Last];
+
+ ArenaVector<LIR*> call_method_insns_;
};
} // namespace art
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 0462530..ba47883 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -585,7 +585,8 @@
}
Arm64Mir2Lir::Arm64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
- : Mir2Lir(cu, mir_graph, arena) {
+ : Mir2Lir(cu, mir_graph, arena),
+ call_method_insns_(arena->Adapter()) {
// Sanity check - make sure encoding map lines up.
for (int i = 0; i < kA64Last; i++) {
if (UNWIDE(Arm64Mir2Lir::EncodingMap[i].opcode) != i) {
@@ -1201,4 +1202,21 @@
return call_state;
}
+void Arm64Mir2Lir::InstallLiteralPools() {
+ // PC-relative calls to methods.
+ patches_.reserve(call_method_insns_.size());
+ for (LIR* p : call_method_insns_) {
+ DCHECK_EQ(p->opcode, kA64Bl1t);
+ uint32_t target_method_idx = p->operands[1];
+ const DexFile* target_dex_file =
+ reinterpret_cast<const DexFile*>(UnwrapPointer(p->operands[2]));
+
+ patches_.push_back(LinkerPatch::RelativeCodePatch(p->offset,
+ target_dex_file, target_method_idx));
+ }
+
+ // And do the normal processing.
+ Mir2Lir::InstallLiteralPools();
+}
+
} // namespace art
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index c308932..174e4e0 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -495,7 +495,8 @@
uintptr_t direct_code, uintptr_t direct_method,
InvokeType type) {
DCHECK(cu->instruction_set != kX86 && cu->instruction_set != kX86_64 &&
- cu->instruction_set != kThumb2 && cu->instruction_set != kArm);
+ cu->instruction_set != kThumb2 && cu->instruction_set != kArm &&
+ cu->instruction_set != kArm64);
Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
if (direct_code != 0 && direct_method != 0) {
switch (state) {
@@ -1751,7 +1752,8 @@
LIR* Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
DCHECK(cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64 &&
- cu_->instruction_set != kThumb2 && cu_->instruction_set != kArm);
+ cu_->instruction_set != kThumb2 && cu_->instruction_set != kArm &&
+ cu_->instruction_set != kArm64);
return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
}