From b0dba10fa626198c2006b62374a70a51b13ddfe9 Mon Sep 17 00:00:00 2001 From: Juergen Ributzka Date: Wed, 30 Jul 2014 22:04:22 +0000 Subject: [PATCH] [FastISel][AArch64] Add support for shift-immediate. Currently the shift-immediate versions are not supported by tblgen and hopefully this can be later removed, once the required support has been added to tblgen. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@214345 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/AArch64/AArch64FastISel.cpp | 102 +++++++++++++++++++++++- test/CodeGen/AArch64/fast-isel-shift.ll | 89 +++++++++++++++++++++ 2 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 test/CodeGen/AArch64/fast-isel-shift.ll diff --git a/lib/Target/AArch64/AArch64FastISel.cpp b/lib/Target/AArch64/AArch64FastISel.cpp index 13312433e2d..5dbcab53b45 100644 --- a/lib/Target/AArch64/AArch64FastISel.cpp +++ b/lib/Target/AArch64/AArch64FastISel.cpp @@ -109,6 +109,7 @@ private: bool SelectTrunc(const Instruction *I); bool SelectIntExt(const Instruction *I); bool SelectMul(const Instruction *I); + bool SelectShift(const Instruction *I, bool IsLeftShift, bool IsArithmetic); // Utility helper routines. bool isTypeLegal(Type *Ty, MVT &VT); @@ -129,6 +130,9 @@ private: bool UseUnscaled = false); unsigned EmitIntExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, bool isZExt); unsigned Emiti1Ext(unsigned SrcReg, MVT DestVT, bool isZExt); + unsigned Emit_LSL_ri(MVT RetVT, unsigned Op0, bool Op0IsKill, uint64_t Imm); + unsigned Emit_LSR_ri(MVT RetVT, unsigned Op0, bool Op0IsKill, uint64_t Imm); + unsigned Emit_ASR_ri(MVT RetVT, unsigned Op0, bool Op0IsKill, uint64_t Imm); unsigned AArch64MaterializeFP(const ConstantFP *CFP, MVT VT); unsigned AArch64MaterializeGV(const GlobalValue *GV); @@ -1722,6 +1726,60 @@ unsigned AArch64FastISel::Emiti1Ext(unsigned SrcReg, MVT DestVT, bool isZExt) { } } +unsigned AArch64FastISel::Emit_LSL_ri(MVT RetVT, unsigned Op0, bool Op0IsKill, + uint64_t Shift) { + unsigned Opc, ImmR, ImmS; + switch (RetVT.SimpleTy) { + default: return 0; + case MVT::i8: + case MVT::i16: + case MVT::i32: + RetVT = MVT::i32; + Opc = AArch64::UBFMWri; ImmR = -Shift % 32; ImmS = 31 - Shift; break; + case MVT::i64: + Opc = AArch64::UBFMXri; ImmR = -Shift % 64; ImmS = 63 - Shift; break; + } + + return FastEmitInst_rii(Opc, TLI.getRegClassFor(RetVT), Op0, Op0IsKill, ImmR, + ImmS); +} + +unsigned AArch64FastISel::Emit_LSR_ri(MVT RetVT, unsigned Op0, bool Op0IsKill, + uint64_t Shift) { + unsigned Opc, ImmS; + switch (RetVT.SimpleTy) { + default: return 0; + case MVT::i8: + case MVT::i16: + case MVT::i32: + RetVT = MVT::i32; + Opc = AArch64::UBFMWri; ImmS = 31; break; + case MVT::i64: + Opc = AArch64::UBFMXri; ImmS = 63; break; + } + + return FastEmitInst_rii(Opc, TLI.getRegClassFor(RetVT), Op0, Op0IsKill, Shift, + ImmS); +} + +unsigned AArch64FastISel::Emit_ASR_ri(MVT RetVT, unsigned Op0, bool Op0IsKill, + uint64_t Shift) { + unsigned Opc, ImmS; + switch (RetVT.SimpleTy) { + default: return 0; + case MVT::i8: + case MVT::i16: + case MVT::i32: + RetVT = MVT::i32; + Opc = AArch64::SBFMWri; ImmS = 31; break; + case MVT::i64: + Opc = AArch64::SBFMXri; ImmS = 63; break; + } + + return FastEmitInst_rii(Opc, TLI.getRegClassFor(RetVT), Op0, Op0IsKill, Shift, + ImmS); +} + unsigned AArch64FastISel::EmitIntExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, bool isZExt) { assert(DestVT != MVT::i1 && "ZeroExt/SignExt an i1?"); @@ -1908,6 +1966,40 @@ bool AArch64FastISel::SelectMul(const Instruction *I) { return true; } +bool AArch64FastISel::SelectShift(const Instruction *I, bool IsLeftShift, + bool IsArithmetic) { + EVT RetEVT = TLI.getValueType(I->getType(), true); + if (!RetEVT.isSimple()) + return false; + MVT RetVT = RetEVT.getSimpleVT(); + + if (!isa(I->getOperand(1))) + return false; + + unsigned Op0Reg = getRegForValue(I->getOperand(0)); + if (!Op0Reg) + return false; + bool Op0IsKill = hasTrivialKill(I->getOperand(0)); + + uint64_t ShiftVal = cast(I->getOperand(1))->getZExtValue(); + + unsigned ResultReg; + if (IsLeftShift) + ResultReg = Emit_LSL_ri(RetVT, Op0Reg, Op0IsKill, ShiftVal); + else { + if (IsArithmetic) + ResultReg = Emit_ASR_ri(RetVT, Op0Reg, Op0IsKill, ShiftVal); + else + ResultReg = Emit_LSR_ri(RetVT, Op0Reg, Op0IsKill, ShiftVal); + } + + if (!ResultReg) + return false; + + UpdateValueMap(I, ResultReg); + return true; +} + bool AArch64FastISel::TargetSelectInstruction(const Instruction *I) { switch (I->getOpcode()) { default: @@ -1948,9 +2040,17 @@ bool AArch64FastISel::TargetSelectInstruction(const Instruction *I) { case Instruction::ZExt: case Instruction::SExt: return SelectIntExt(I); + + // FIXME: All of these should really be handled by the target-independent + // selector -> improve FastISel tblgen. case Instruction::Mul: - // FIXME: This really should be handled by the target-independent selector. return SelectMul(I); + case Instruction::Shl: + return SelectShift(I, /*IsLeftShift=*/true, /*IsArithmetic=*/false); + case Instruction::LShr: + return SelectShift(I, /*IsLeftShift=*/false, /*IsArithmetic=*/false); + case Instruction::AShr: + return SelectShift(I, /*IsLeftShift=*/false, /*IsArithmetic=*/true); } return false; // Silence warnings. diff --git a/test/CodeGen/AArch64/fast-isel-shift.ll b/test/CodeGen/AArch64/fast-isel-shift.ll new file mode 100644 index 00000000000..35868ee384f --- /dev/null +++ b/test/CodeGen/AArch64/fast-isel-shift.ll @@ -0,0 +1,89 @@ +; RUN: llc -fast-isel -fast-isel-abort -mtriple=arm64-apple-darwin < %s | FileCheck %s + +; CHECK-LABEL: lsl_i8 +; CHECK: lsl {{w[0-9]*}}, {{w[0-9]*}}, #4 +define zeroext i8 @lsl_i8(i8 %a) { + %1 = shl i8 %a, 4 + ret i8 %1 +} + +; CHECK-LABEL: lsl_i16 +; CHECK: lsl {{w[0-9]*}}, {{w[0-9]*}}, #8 +define zeroext i16 @lsl_i16(i16 %a) { + %1 = shl i16 %a, 8 + ret i16 %1 +} + +; CHECK-LABEL: lsl_i32 +; CHECK: lsl {{w[0-9]*}}, {{w[0-9]*}}, #16 +define zeroext i32 @lsl_i32(i32 %a) { + %1 = shl i32 %a, 16 + ret i32 %1 +} + +; FIXME: This shouldn't use the variable shift version. +; CHECK-LABEL: lsl_i64 +; CHECK: lsl {{x[0-9]*}}, {{x[0-9]*}}, {{x[0-9]*}} +define i64 @lsl_i64(i64 %a) { + %1 = shl i64 %a, 32 + ret i64 %1 +} + +; CHECK-LABEL: lsr_i8 +; CHECK: lsr {{w[0-9]*}}, {{w[0-9]*}}, #4 +define zeroext i8 @lsr_i8(i8 %a) { + %1 = lshr i8 %a, 4 + ret i8 %1 +} + +; CHECK-LABEL: lsr_i16 +; CHECK: lsr {{w[0-9]*}}, {{w[0-9]*}}, #8 +define zeroext i16 @lsr_i16(i16 %a) { + %1 = lshr i16 %a, 8 + ret i16 %1 +} + +; CHECK-LABEL: lsr_i32 +; CHECK: lsr {{w[0-9]*}}, {{w[0-9]*}}, #16 +define zeroext i32 @lsr_i32(i32 %a) { + %1 = lshr i32 %a, 16 + ret i32 %1 +} + +; FIXME: This shouldn't use the variable shift version. +; CHECK-LABEL: lsr_i64 +; CHECK: lsr {{x[0-9]*}}, {{x[0-9]*}}, {{x[0-9]*}} +define i64 @lsr_i64(i64 %a) { + %1 = lshr i64 %a, 32 + ret i64 %1 +} + +; CHECK-LABEL: asr_i8 +; CHECK: asr {{w[0-9]*}}, {{w[0-9]*}}, #4 +define zeroext i8 @asr_i8(i8 %a) { + %1 = ashr i8 %a, 4 + ret i8 %1 +} + +; CHECK-LABEL: asr_i16 +; CHECK: asr {{w[0-9]*}}, {{w[0-9]*}}, #8 +define zeroext i16 @asr_i16(i16 %a) { + %1 = ashr i16 %a, 8 + ret i16 %1 +} + +; CHECK-LABEL: asr_i32 +; CHECK: asr {{w[0-9]*}}, {{w[0-9]*}}, #16 +define zeroext i32 @asr_i32(i32 %a) { + %1 = ashr i32 %a, 16 + ret i32 %1 +} + +; FIXME: This shouldn't use the variable shift version. +; CHECK-LABEL: asr_i64 +; CHECK: asr {{x[0-9]*}}, {{x[0-9]*}}, {{x[0-9]*}} +define i64 @asr_i64(i64 %a) { + %1 = ashr i64 %a, 32 + ret i64 %1 +} +