diff options
| -rw-r--r-- | llvm/lib/Target/AArch64/AArch64FastISel.cpp | 115 | ||||
| -rw-r--r-- | llvm/test/CodeGen/AArch64/arm64-fast-isel-call.ll | 69 |
2 files changed, 143 insertions, 41 deletions
diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp index d80f34ca2d3..3d5fb66146e 100644 --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -55,9 +55,10 @@ class AArch64FastISel : public FastISel { int FI; } Base; int64_t Offset; + const GlobalValue *GV; public: - Address() : Kind(RegBase), Offset(0) { Base.Reg = 0; } + Address() : Kind(RegBase), Offset(0), GV(nullptr) { Base.Reg = 0; } void setKind(BaseKind K) { Kind = K; } BaseKind getKind() const { return Kind; } bool isRegBase() const { return Kind == RegBase; } @@ -81,6 +82,9 @@ class AArch64FastISel : public FastISel { void setOffset(int64_t O) { Offset = O; } int64_t getOffset() { return Offset; } + void setGlobalValue(const GlobalValue *G) { GV = G; } + const GlobalValue *getGlobalValue() { return GV; } + bool isValid() { return isFIBase() || (isRegBase() && getReg() != 0); } }; @@ -115,6 +119,7 @@ private: bool isTypeLegal(Type *Ty, MVT &VT); bool isLoadStoreTypeLegal(Type *Ty, MVT &VT); bool ComputeAddress(const Value *Obj, Address &Addr); + bool ComputeCallAddress(const Value *V, Address &Addr); bool SimplifyAddress(Address &Addr, MVT VT, int64_t ScaleFactor, bool UseUnscaled); void AddLoadStoreOperands(Address &Addr, const MachineInstrBuilder &MIB, @@ -420,6 +425,56 @@ bool AArch64FastISel::ComputeAddress(const Value *Obj, Address &Addr) { return Addr.isValid(); } +bool AArch64FastISel::ComputeCallAddress(const Value *V, Address &Addr) { + const User *U = nullptr; + unsigned Opcode = Instruction::UserOp1; + bool InMBB = true; + + if (const auto *I = dyn_cast<Instruction>(V)) { + Opcode = I->getOpcode(); + U = I; + InMBB = I->getParent() == FuncInfo.MBB->getBasicBlock(); + } else if (const auto *C = dyn_cast<ConstantExpr>(V)) { + Opcode = C->getOpcode(); + U = C; + } + + switch (Opcode) { + default: break; + case Instruction::BitCast: + // Look past bitcasts if its operand is in the same BB. + if (InMBB) + return ComputeCallAddress(U->getOperand(0), Addr); + break; + case Instruction::IntToPtr: + // Look past no-op inttoptrs if its operand is in the same BB. + if (InMBB && + TLI.getValueType(U->getOperand(0)->getType()) == TLI.getPointerTy()) + return ComputeCallAddress(U->getOperand(0), Addr); + break; + case Instruction::PtrToInt: + // Look past no-op ptrtoints if its operand is in the same BB. + if (InMBB && + TLI.getValueType(U->getType()) == TLI.getPointerTy()) + return ComputeCallAddress(U->getOperand(0), Addr); + break; + } + + if (const GlobalValue *GV = dyn_cast<GlobalValue>(V)) { + Addr.setGlobalValue(GV); + return true; + } + + // If all else fails, try to materialize the value in a register. + if (!Addr.getGlobalValue()) { + Addr.setReg(getRegForValue(V)); + return Addr.getReg() != 0; + } + + return false; +} + + bool AArch64FastISel::isTypeLegal(Type *Ty, MVT &VT) { EVT evt = TLI.getValueType(Ty, true); @@ -1343,9 +1398,13 @@ bool AArch64FastISel::FastLowerCall(CallLoweringInfo &CLI) { const Value *Callee = CLI.Callee; const char *SymName = CLI.SymName; - // Only handle global variable Callees. - const GlobalValue *GV = dyn_cast<GlobalValue>(Callee); - if (!GV) + CodeModel::Model CM = TM.getCodeModel(); + // Only support the small and large code model. + if (CM != CodeModel::Small && CM != CodeModel::Large) + return false; + + // FIXME: Add large code model support for ELF. + if (CM == CodeModel::Large && !Subtarget->isTargetMachO()) return false; // Let SDISel handle vararg functions. @@ -1380,6 +1439,10 @@ bool AArch64FastISel::FastLowerCall(CallLoweringInfo &CLI) { OutVTs.push_back(VT); } + Address Addr; + if (!ComputeCallAddress(Callee, Addr)) + return false; + // Handle the arguments now that we've gotten them. unsigned NumBytes; if (!ProcessCallArgs(CLI, OutVTs, NumBytes)) @@ -1387,12 +1450,42 @@ bool AArch64FastISel::FastLowerCall(CallLoweringInfo &CLI) { // Issue the call. MachineInstrBuilder MIB; - MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(AArch64::BL)); - CLI.Call = MIB; - if (!SymName) - MIB.addGlobalAddress(GV, 0, 0); - else - MIB.addExternalSymbol(SymName, 0); + if (CM == CodeModel::Small) { + unsigned CallOpc = Addr.getReg() ? AArch64::BLR : AArch64::BL; + MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(CallOpc)); + if (SymName) + MIB.addExternalSymbol(SymName, 0); + else if (Addr.getGlobalValue()) + MIB.addGlobalAddress(Addr.getGlobalValue(), 0, 0); + else if (Addr.getReg()) + MIB.addReg(Addr.getReg()); + else + return false; + } else { + unsigned CallReg = 0; + if (SymName) { + unsigned ADRPReg = createResultReg(&AArch64::GPR64commonRegClass); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(AArch64::ADRP), + ADRPReg) + .addExternalSymbol(SymName, AArch64II::MO_GOT | AArch64II::MO_PAGE); + + CallReg = createResultReg(&AArch64::GPR64RegClass); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(AArch64::LDRXui), + CallReg) + .addReg(ADRPReg) + .addExternalSymbol(SymName, AArch64II::MO_GOT | AArch64II::MO_PAGEOFF | + AArch64II::MO_NC); + } else if (Addr.getGlobalValue()) { + CallReg = AArch64MaterializeGV(Addr.getGlobalValue()); + } else if (Addr.getReg()) + CallReg = Addr.getReg(); + + if (!CallReg) + return false; + + MIB = BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(AArch64::BLR)).addReg(CallReg); + } // Add implicit physical register uses to the call. for (auto Reg : CLI.OutRegs) @@ -1402,6 +1495,8 @@ bool AArch64FastISel::FastLowerCall(CallLoweringInfo &CLI) { // Proper defs for return values will be added by setPhysRegsDeadExcept(). MIB.addRegMask(TRI.getCallPreservedMask(CC)); + CLI.Call = MIB; + // Finish off the call including any return values. return FinishCall(CLI, RetVT, NumBytes); } diff --git a/llvm/test/CodeGen/AArch64/arm64-fast-isel-call.ll b/llvm/test/CodeGen/AArch64/arm64-fast-isel-call.ll index caf73bf58a4..2a77eb4bcc8 100644 --- a/llvm/test/CodeGen/AArch64/arm64-fast-isel-call.ll +++ b/llvm/test/CodeGen/AArch64/arm64-fast-isel-call.ll @@ -1,5 +1,6 @@ -; RUN: llc < %s -O0 -fast-isel-abort -mtriple=arm64-apple-darwin | FileCheck %s -; RUN: llc < %s -O0 -fast-isel-abort -mtriple=aarch64_be-linux-gnu | FileCheck %s --check-prefix=CHECK-BE +; RUN: llc -O0 -fast-isel-abort -code-model=small -mtriple=arm64-apple-darwin < %s | FileCheck %s +; RUN: llc -O0 -fast-isel-abort -code-model=large -mtriple=arm64-apple-darwin < %s | FileCheck %s --check-prefix=LARGE +; RUN: llc -O0 -fast-isel-abort -code-model=small -mtriple=aarch64_be-linux-gnu < %s | FileCheck %s --check-prefix=CHECK-BE define void @call0() nounwind { entry: @@ -8,8 +9,12 @@ entry: define void @foo0() nounwind { entry: -; CHECK: foo0 -; CHECK: bl _call0 +; CHECK-LABEL: foo0 +; CHECK: bl _call0 +; LARGE-LABEL: foo0 +; LARGE: adrp [[REG0:x[0-9]+]], _call0@GOTPAGE +; LARGE: ldr [[REG1:x[0-9]+]], {{\[}}[[REG0]], _call0@GOTPAGEOFF{{\]}} +; LARGE-NEXT: blr [[REG1]] call void @call0() ret void } @@ -24,10 +29,10 @@ entry: define i32 @foo1(i32 %a) nounwind { entry: -; CHECK: foo1 -; CHECK: stur w0, [x29, #-4] -; CHECK-NEXT: ldur w0, [x29, #-4] -; CHECK-NEXT: bl _call1 +; CHECK-LABEL: foo1 +; CHECK: stur w0, [x29, #-4] +; CHECK-NEXT: ldur w0, [x29, #-4] +; CHECK-NEXT: bl _call1 %a.addr = alloca i32, align 4 store i32 %a, i32* %a.addr, align 4 %tmp = load i32* %a.addr, align 4 @@ -37,10 +42,10 @@ entry: define i32 @sext_(i8 %a, i16 %b) nounwind { entry: -; CHECK: @sext_ -; CHECK: sxtb w0, w0 -; CHECK: sxth w1, w1 -; CHECK: bl _foo_sext_ +; CHECK-LABEL: @sext_ +; CHECK: sxtb w0, w0 +; CHECK: sxth w1, w1 +; CHECK: bl _foo_sext_ call void @foo_sext_(i8 signext %a, i16 signext %b) ret i32 0 } @@ -49,9 +54,9 @@ declare void @foo_sext_(i8 %a, i16 %b) define i32 @zext_(i8 %a, i16 %b) nounwind { entry: -; CHECK: @zext_ -; CHECK: uxtb w0, w0 -; CHECK: uxth w1, w1 +; CHECK-LABEL: @zext_ +; CHECK: uxtb w0, w0 +; CHECK: uxth w1, w1 call void @foo_zext_(i8 zeroext %a, i16 zeroext %b) ret i32 0 } @@ -60,10 +65,10 @@ declare void @foo_zext_(i8 %a, i16 %b) define i32 @t1(i32 %argc, i8** nocapture %argv) { entry: -; CHECK: @t1 +; CHECK-LABEL: @t1 ; The last parameter will be passed on stack via i8. -; CHECK: strb w{{[0-9]+}}, [sp] -; CHECK-NEXT: bl _bar +; CHECK: strb w{{[0-9]+}}, [sp] +; CHECK: bl _bar %call = call i32 @bar(i8 zeroext 0, i8 zeroext -8, i8 zeroext -69, i8 zeroext 28, i8 zeroext 40, i8 zeroext -70, i8 zeroext 28, i8 zeroext 39, i8 zeroext -41) ret i32 0 } @@ -73,18 +78,18 @@ declare i32 @bar(i8 zeroext, i8 zeroext, i8 zeroext, i8 zeroext, i8 zeroext, i8 ; Test materialization of integers. Target-independent selector handles this. define i32 @t2() { entry: -; CHECK: @t2 -; CHECK: movz x0, #0 -; CHECK: orr w1, wzr, #0xfffffff8 -; CHECK: orr w[[REG:[0-9]+]], wzr, #0x3ff -; CHECK: orr w[[REG2:[0-9]+]], wzr, #0x2 -; CHECK: movz w[[REG3:[0-9]+]], #0 -; CHECK: orr w[[REG4:[0-9]+]], wzr, #0x1 -; CHECK: uxth w2, w[[REG]] -; CHECK: sxtb w3, w[[REG2]] -; CHECK: and w4, w[[REG3]], #0x1 -; CHECK: and w5, w[[REG4]], #0x1 -; CHECK: bl _func2 +; CHECK-LABEL: @t2 +; CHECK: movz x0, #0 +; CHECK: orr w1, wzr, #0xfffffff8 +; CHECK: orr w[[REG:[0-9]+]], wzr, #0x3ff +; CHECK: orr w[[REG2:[0-9]+]], wzr, #0x2 +; CHECK: movz w[[REG3:[0-9]+]], #0 +; CHECK: orr w[[REG4:[0-9]+]], wzr, #0x1 +; CHECK: uxth w2, w[[REG]] +; CHECK: sxtb w3, w[[REG2]] +; CHECK: and w4, w[[REG3]], #0x1 +; CHECK: and w5, w[[REG4]], #0x1 +; CHECK: bl _func2 %call = call i32 @func2(i64 zeroext 0, i32 signext -8, i16 zeroext 1023, i8 signext -254, i1 zeroext 0, i1 zeroext 1) ret i32 0 } @@ -94,7 +99,9 @@ declare i32 @func2(i64 zeroext, i32 signext, i16 zeroext, i8 signext, i1 zeroext declare void @callee_b0f(i8 %bp10, i8 %bp11, i8 %bp12, i8 %bp13, i8 %bp14, i8 %bp15, i8 %bp17, i8 %bp18, i8 %bp19) define void @caller_b1f() { entry: - ; CHECK-BE: strb w{{.*}}, [sp, #7] +; CHECK-BE-LABEL: caller_b1f +; CHECK-BE: strb w{{.*}}, [sp, #7] call void @callee_b0f(i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 42) ret void } + |

