[FastISel][AArch64] Fold Sign-/Zero-Extend into the shift immediate instruction.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216510 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Juergen Ributzka 2014-08-27 00:58:26 +00:00
parent c04b78627d
commit 836f4bd090
2 changed files with 419 additions and 55 deletions

View File

@ -195,13 +195,16 @@ private:
unsigned Op1, bool Op1IsKill);
unsigned emitLSL_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill,
unsigned Op1Reg, bool Op1IsKill);
unsigned emitLSL_ri(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, uint64_t Imm);
unsigned emitLSL_ri(MVT RetVT, MVT SrcVT, unsigned Op0Reg, bool Op0IsKill,
uint64_t Imm, bool IsZExt = true);
unsigned emitLSR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill,
unsigned Op1Reg, bool Op1IsKill);
unsigned emitLSR_ri(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, uint64_t Imm);
unsigned emitLSR_ri(MVT RetVT, MVT SrcVT, unsigned Op0Reg, bool Op0IsKill,
uint64_t Imm, bool IsZExt = true);
unsigned emitASR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill,
unsigned Op1Reg, bool Op1IsKill);
unsigned emitASR_ri(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, uint64_t Imm);
unsigned emitASR_ri(MVT RetVT, MVT SrcVT, unsigned Op0Reg, bool Op0IsKill,
uint64_t Imm, bool IsZExt = false);
unsigned AArch64MaterializeInt(const ConstantInt *CI, MVT VT);
unsigned AArch64MaterializeFP(const ConstantFP *CFP, MVT VT);
@ -718,8 +721,8 @@ bool AArch64FastISel::SimplifyAddress(Address &Addr, MVT VT) {
Addr.getOffsetReg(), /*TODO:IsKill=*/false,
Addr.getShift());
else
ResultReg = emitLSL_ri(MVT::i64, Addr.getOffsetReg(), /*Op0IsKill=*/false,
Addr.getShift());
ResultReg = emitLSL_ri(MVT::i64, MVT::i64, Addr.getOffsetReg(),
/*Op0IsKill=*/false, Addr.getShift());
if (!ResultReg)
return false;
@ -2381,7 +2384,8 @@ bool AArch64FastISel::FastLowerIntrinsicCall(const IntrinsicInst *II) {
if (VT == MVT::i32) {
MulReg = Emit_SMULL_rr(MVT::i64, LHSReg, LHSIsKill, RHSReg, RHSIsKill);
unsigned ShiftReg = emitLSR_ri(MVT::i64, MulReg, /*IsKill=*/false, 32);
unsigned ShiftReg = emitLSR_ri(MVT::i64, MVT::i64, MulReg,
/*IsKill=*/false, 32);
MulReg = FastEmitInst_extractsubreg(VT, MulReg, /*IsKill=*/true,
AArch64::sub_32);
ShiftReg = FastEmitInst_extractsubreg(VT, ShiftReg, /*IsKill=*/true,
@ -2694,23 +2698,70 @@ unsigned AArch64FastISel::emitLSL_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill,
return ResultReg;
}
unsigned AArch64FastISel::emitLSL_ri(MVT RetVT, unsigned Op0, bool Op0IsKill,
uint64_t Shift) {
unsigned Opc, ImmR, ImmS;
switch (RetVT.SimpleTy) {
default: return 0;
case MVT::i8:
Opc = AArch64::UBFMWri; ImmR = -Shift % 32; ImmS = 7 - Shift; break;
case MVT::i16:
Opc = AArch64::UBFMWri; ImmR = -Shift % 32; ImmS = 15 - Shift; break;
case 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;
}
unsigned AArch64FastISel::emitLSL_ri(MVT RetVT, MVT SrcVT, unsigned Op0,
bool Op0IsKill, uint64_t Shift,
bool IsZext) {
assert(RetVT.SimpleTy >= SrcVT.SimpleTy &&
"Unexpected source/return type pair.");
assert((SrcVT == MVT::i8 || SrcVT == MVT::i16 || SrcVT == MVT::i32 ||
SrcVT == MVT::i64) && "Unexpected source value type.");
assert((RetVT == MVT::i8 || RetVT == MVT::i16 || RetVT == MVT::i32 ||
RetVT == MVT::i64) && "Unexpected return value type.");
bool Is64Bit = (RetVT == MVT::i64);
unsigned RegSize = Is64Bit ? 64 : 32;
unsigned DstBits = RetVT.getSizeInBits();
unsigned SrcBits = SrcVT.getSizeInBits();
// Don't deal with undefined shifts.
if (Shift >= DstBits)
return 0;
// For immediate shifts we can fold the zero-/sign-extension into the shift.
// {S|U}BFM Wd, Wn, #r, #s
// Wd<32+s-r,32-r> = Wn<s:0> when r > s
// %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16
// %2 = shl i16 %1, 4
// Wd<32+7-28,32-28> = Wn<7:0> <- clamp s to 7
// 0b1111_1111_1111_1111__1111_1010_1010_0000 sext
// 0b0000_0000_0000_0000__0000_0101_0101_0000 sext | zext
// 0b0000_0000_0000_0000__0000_1010_1010_0000 zext
// %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16
// %2 = shl i16 %1, 8
// Wd<32+7-24,32-24> = Wn<7:0>
// 0b1111_1111_1111_1111__1010_1010_0000_0000 sext
// 0b0000_0000_0000_0000__0101_0101_0000_0000 sext | zext
// 0b0000_0000_0000_0000__1010_1010_0000_0000 zext
// %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16
// %2 = shl i16 %1, 12
// Wd<32+3-20,32-20> = Wn<3:0>
// 0b1111_1111_1111_1111__1010_0000_0000_0000 sext
// 0b0000_0000_0000_0000__0101_0000_0000_0000 sext | zext
// 0b0000_0000_0000_0000__1010_0000_0000_0000 zext
unsigned ImmR = RegSize - Shift;
// Limit the width to the length of the source type.
unsigned ImmS = std::min<unsigned>(SrcBits - 1, DstBits - 1 - Shift);
static const unsigned OpcTable[2][2] = {
{AArch64::SBFMWri, AArch64::SBFMXri},
{AArch64::UBFMWri, AArch64::UBFMXri}
};
unsigned Opc = OpcTable[IsZext][Is64Bit];
const TargetRegisterClass *RC =
(RetVT == MVT::i64) ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass;
Is64Bit ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass;
if (SrcVT.SimpleTy <= MVT::i32 && RetVT == MVT::i64) {
unsigned TmpReg = MRI.createVirtualRegister(RC);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(AArch64::SUBREG_TO_REG), TmpReg)
.addImm(0)
.addReg(Op0, getKillRegState(Op0IsKill))
.addImm(AArch64::sub_32);
Op0 = TmpReg;
Op0IsKill = true;
}
return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, ImmR, ImmS);
}
@ -2741,20 +2792,86 @@ unsigned AArch64FastISel::emitLSR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill,
return ResultReg;
}
unsigned AArch64FastISel::emitLSR_ri(MVT RetVT, unsigned Op0, bool Op0IsKill,
uint64_t Shift) {
unsigned Opc, ImmS;
switch (RetVT.SimpleTy) {
default: return 0;
case MVT::i8: Opc = AArch64::UBFMWri; ImmS = 7; break;
case MVT::i16: Opc = AArch64::UBFMWri; ImmS = 15; break;
case MVT::i32: Opc = AArch64::UBFMWri; ImmS = 31; break;
case MVT::i64: Opc = AArch64::UBFMXri; ImmS = 63; break;
unsigned AArch64FastISel::emitLSR_ri(MVT RetVT, MVT SrcVT, unsigned Op0,
bool Op0IsKill, uint64_t Shift,
bool IsZExt) {
assert(RetVT.SimpleTy >= SrcVT.SimpleTy &&
"Unexpected source/return type pair.");
assert((SrcVT == MVT::i8 || SrcVT == MVT::i16 || SrcVT == MVT::i32 ||
SrcVT == MVT::i64) && "Unexpected source value type.");
assert((RetVT == MVT::i8 || RetVT == MVT::i16 || RetVT == MVT::i32 ||
RetVT == MVT::i64) && "Unexpected return value type.");
bool Is64Bit = (RetVT == MVT::i64);
unsigned RegSize = Is64Bit ? 64 : 32;
unsigned DstBits = RetVT.getSizeInBits();
unsigned SrcBits = SrcVT.getSizeInBits();
// Don't deal with undefined shifts.
if (Shift >= DstBits)
return 0;
// For immediate shifts we can fold the zero-/sign-extension into the shift.
// {S|U}BFM Wd, Wn, #r, #s
// Wd<s-r:0> = Wn<s:r> when r <= s
// %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16
// %2 = lshr i16 %1, 4
// Wd<7-4:0> = Wn<7:4>
// 0b0000_0000_0000_0000__0000_1111_1111_1010 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0101 sext | zext
// 0b0000_0000_0000_0000__0000_0000_0000_1010 zext
// %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16
// %2 = lshr i16 %1, 8
// Wd<7-7,0> = Wn<7:7>
// 0b0000_0000_0000_0000__0000_0000_1111_1111 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0000 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0000 zext
// %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16
// %2 = lshr i16 %1, 12
// Wd<7-7,0> = Wn<7:7> <- clamp r to 7
// 0b0000_0000_0000_0000__0000_0000_0000_1111 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0000 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0000 zext
if (Shift >= SrcBits && IsZExt)
return AArch64MaterializeInt(ConstantInt::get(*Context, APInt(RegSize, 0)),
RetVT);
// It is not possible to fold a sign-extend into the LShr instruction. In this
// case emit a sign-extend.
if (!IsZExt) {
Op0 = EmitIntExt(SrcVT, Op0, RetVT, IsZExt);
if (!Op0)
return 0;
Op0IsKill = true;
SrcVT = RetVT;
SrcBits = SrcVT.getSizeInBits();
IsZExt = true;
}
unsigned ImmR = std::min<unsigned>(SrcBits - 1, Shift);
unsigned ImmS = SrcBits - 1;
static const unsigned OpcTable[2][2] = {
{AArch64::SBFMWri, AArch64::SBFMXri},
{AArch64::UBFMWri, AArch64::UBFMXri}
};
unsigned Opc = OpcTable[IsZExt][Is64Bit];
const TargetRegisterClass *RC =
(RetVT == MVT::i64) ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass;
return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, Shift, ImmS);
Is64Bit ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass;
if (SrcVT.SimpleTy <= MVT::i32 && RetVT == MVT::i64) {
unsigned TmpReg = MRI.createVirtualRegister(RC);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(AArch64::SUBREG_TO_REG), TmpReg)
.addImm(0)
.addReg(Op0, getKillRegState(Op0IsKill))
.addImm(AArch64::sub_32);
Op0 = TmpReg;
Op0IsKill = true;
}
return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, ImmR, ImmS);
}
unsigned AArch64FastISel::emitASR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill,
@ -2784,20 +2901,74 @@ unsigned AArch64FastISel::emitASR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill,
return ResultReg;
}
unsigned AArch64FastISel::emitASR_ri(MVT RetVT, unsigned Op0, bool Op0IsKill,
uint64_t Shift) {
unsigned Opc, ImmS;
switch (RetVT.SimpleTy) {
default: return 0;
case MVT::i8: Opc = AArch64::SBFMWri; ImmS = 7; break;
case MVT::i16: Opc = AArch64::SBFMWri; ImmS = 15; break;
case MVT::i32: Opc = AArch64::SBFMWri; ImmS = 31; break;
case MVT::i64: Opc = AArch64::SBFMXri; ImmS = 63; break;
}
unsigned AArch64FastISel::emitASR_ri(MVT RetVT, MVT SrcVT, unsigned Op0,
bool Op0IsKill, uint64_t Shift,
bool IsZExt) {
assert(RetVT.SimpleTy >= SrcVT.SimpleTy &&
"Unexpected source/return type pair.");
assert((SrcVT == MVT::i8 || SrcVT == MVT::i16 || SrcVT == MVT::i32 ||
SrcVT == MVT::i64) && "Unexpected source value type.");
assert((RetVT == MVT::i8 || RetVT == MVT::i16 || RetVT == MVT::i32 ||
RetVT == MVT::i64) && "Unexpected return value type.");
bool Is64Bit = (RetVT == MVT::i64);
unsigned RegSize = Is64Bit ? 64 : 32;
unsigned DstBits = RetVT.getSizeInBits();
unsigned SrcBits = SrcVT.getSizeInBits();
// Don't deal with undefined shifts.
if (Shift >= DstBits)
return 0;
// For immediate shifts we can fold the zero-/sign-extension into the shift.
// {S|U}BFM Wd, Wn, #r, #s
// Wd<s-r:0> = Wn<s:r> when r <= s
// %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16
// %2 = ashr i16 %1, 4
// Wd<7-4:0> = Wn<7:4>
// 0b1111_1111_1111_1111__1111_1111_1111_1010 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0101 sext | zext
// 0b0000_0000_0000_0000__0000_0000_0000_1010 zext
// %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16
// %2 = ashr i16 %1, 8
// Wd<7-7,0> = Wn<7:7>
// 0b1111_1111_1111_1111__1111_1111_1111_1111 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0000 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0000 zext
// %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16
// %2 = ashr i16 %1, 12
// Wd<7-7,0> = Wn<7:7> <- clamp r to 7
// 0b1111_1111_1111_1111__1111_1111_1111_1111 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0000 sext
// 0b0000_0000_0000_0000__0000_0000_0000_0000 zext
if (Shift >= SrcBits && IsZExt)
return AArch64MaterializeInt(ConstantInt::get(*Context, APInt(RegSize, 0)),
RetVT);
unsigned ImmR = std::min<unsigned>(SrcBits - 1, Shift);
unsigned ImmS = SrcBits - 1;
static const unsigned OpcTable[2][2] = {
{AArch64::SBFMWri, AArch64::SBFMXri},
{AArch64::UBFMWri, AArch64::UBFMXri}
};
unsigned Opc = OpcTable[IsZExt][Is64Bit];
const TargetRegisterClass *RC =
(RetVT == MVT::i64) ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass;
return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, Shift, ImmS);
Is64Bit ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass;
if (SrcVT.SimpleTy <= MVT::i32 && RetVT == MVT::i64) {
unsigned TmpReg = MRI.createVirtualRegister(RC);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(AArch64::SUBREG_TO_REG), TmpReg)
.addImm(0)
.addReg(Op0, getKillRegState(Op0IsKill))
.addImm(AArch64::sub_32);
Op0 = TmpReg;
Op0IsKill = true;
}
return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, ImmR, ImmS);
}
unsigned AArch64FastISel::EmitIntExt(MVT SrcVT, unsigned SrcReg, MVT DestVT,
@ -2988,29 +3159,47 @@ bool AArch64FastISel::SelectMul(const Instruction *I) {
}
bool AArch64FastISel::SelectShift(const Instruction *I) {
EVT RetEVT = TLI.getValueType(I->getType(), true);
if (!RetEVT.isSimple())
MVT RetVT;
if (!isLoadStoreTypeLegal(I->getType(), RetVT))
return false;
MVT RetVT = RetEVT.getSimpleVT();
unsigned Op0Reg = getRegForValue(I->getOperand(0));
if (!Op0Reg)
return false;
bool Op0IsKill = hasTrivialKill(I->getOperand(0));
if (const auto *C = dyn_cast<ConstantInt>(I->getOperand(1))) {
unsigned ResultReg = 0;
uint64_t ShiftVal = C->getZExtValue();
MVT SrcVT = RetVT;
bool IsZExt = (I->getOpcode() == Instruction::AShr) ? false : true;
const Value * Op0 = I->getOperand(0);
if (const auto *ZExt = dyn_cast<ZExtInst>(Op0)) {
MVT TmpVT;
if (isLoadStoreTypeLegal(ZExt->getSrcTy(), TmpVT)) {
SrcVT = TmpVT;
IsZExt = true;
Op0 = ZExt->getOperand(0);
}
} else if (const auto *SExt = dyn_cast<SExtInst>(Op0)) {
MVT TmpVT;
if (isLoadStoreTypeLegal(SExt->getSrcTy(), TmpVT)) {
SrcVT = TmpVT;
IsZExt = false;
Op0 = SExt->getOperand(0);
}
}
unsigned Op0Reg = getRegForValue(Op0);
if (!Op0Reg)
return false;
bool Op0IsKill = hasTrivialKill(Op0);
switch (I->getOpcode()) {
default: llvm_unreachable("Unexpected instruction.");
case Instruction::Shl:
ResultReg = emitLSL_ri(RetVT, Op0Reg, Op0IsKill, ShiftVal);
ResultReg = emitLSL_ri(RetVT, SrcVT, Op0Reg, Op0IsKill, ShiftVal, IsZExt);
break;
case Instruction::AShr:
ResultReg = emitASR_ri(RetVT, Op0Reg, Op0IsKill, ShiftVal);
ResultReg = emitASR_ri(RetVT, SrcVT, Op0Reg, Op0IsKill, ShiftVal, IsZExt);
break;
case Instruction::LShr:
ResultReg = emitLSR_ri(RetVT, Op0Reg, Op0IsKill, ShiftVal);
ResultReg = emitLSR_ri(RetVT, SrcVT, Op0Reg, Op0IsKill, ShiftVal, IsZExt);
break;
}
if (!ResultReg)
@ -3020,6 +3209,11 @@ bool AArch64FastISel::SelectShift(const Instruction *I) {
return true;
}
unsigned Op0Reg = getRegForValue(I->getOperand(0));
if (!Op0Reg)
return false;
bool Op0IsKill = hasTrivialKill(I->getOperand(0));
unsigned Op1Reg = getRegForValue(I->getOperand(1));
if (!Op1Reg)
return false;

View File

@ -16,6 +16,56 @@ define zeroext i8 @lsl_i8(i8 %a) {
ret i8 %1
}
; CHECK-LABEL: lsl_zext_i8_i16
; CHECK: ubfiz {{w[0-9]*}}, {{w[0-9]*}}, #4, #8
define zeroext i16 @lsl_zext_i8_i16(i8 %b) {
%1 = zext i8 %b to i16
%2 = shl i16 %1, 4
ret i16 %2
}
; CHECK-LABEL: lsl_sext_i8_i16
; CHECK: sbfiz {{w[0-9]*}}, {{w[0-9]*}}, #4, #8
define signext i16 @lsl_sext_i8_i16(i8 %b) {
%1 = sext i8 %b to i16
%2 = shl i16 %1, 4
ret i16 %2
}
; CHECK-LABEL: lsl_zext_i8_i32
; CHECK: ubfiz {{w[0-9]*}}, {{w[0-9]*}}, #4, #8
define i32 @lsl_zext_i8_i32(i8 %b) {
%1 = zext i8 %b to i32
%2 = shl i32 %1, 4
ret i32 %2
}
; CHECK-LABEL: lsl_sext_i8_i32
; CHECK: sbfiz {{w[0-9]*}}, {{w[0-9]*}}, #4, #8
define i32 @lsl_sext_i8_i32(i8 %b) {
%1 = sext i8 %b to i32
%2 = shl i32 %1, 4
ret i32 %2
}
; FIXME: Cannot test this yet, because the target-independent instruction
; selector handles this.
; CHECK-LABEL: lsl_zext_i8_i64
define i64 @lsl_zext_i8_i64(i8 %b) {
%1 = zext i8 %b to i64
%2 = shl i64 %1, 4
ret i64 %2
}
; FIXME: Cannot test this yet, because the target-independent instruction
; selector handles this.
; CHECK-LABEL: lsl_sext_i8_i64
define i64 @lsl_sext_i8_i64(i8 %b) {
%1 = sext i8 %b to i64
%2 = shl i64 %1, 4
ret i64 %2
}
; CHECK-LABEL: lslv_i16
; CHECK: and [[REG1:w[0-9]+]], w1, #0xffff
; CHECK-NEXT: lsl [[REG2:w[0-9]+]], w0, [[REG1]]
@ -32,6 +82,40 @@ define zeroext i16 @lsl_i16(i16 %a) {
ret i16 %1
}
; CHECK-LABEL: lsl_zext_i16_i32
; CHECK: ubfiz {{w[0-9]*}}, {{w[0-9]*}}, #8, #16
define i32 @lsl_zext_i16_i32(i16 %b) {
%1 = zext i16 %b to i32
%2 = shl i32 %1, 8
ret i32 %2
}
; CHECK-LABEL: lsl_sext_i16_i32
; CHECK: sbfiz {{w[0-9]*}}, {{w[0-9]*}}, #8, #16
define i32 @lsl_sext_i16_i32(i16 %b) {
%1 = sext i16 %b to i32
%2 = shl i32 %1, 8
ret i32 %2
}
; FIXME: Cannot test this yet, because the target-independent instruction
; selector handles this.
; CHECK-LABEL: lsl_zext_i16_i64
define i64 @lsl_zext_i16_i64(i16 %b) {
%1 = zext i16 %b to i64
%2 = shl i64 %1, 8
ret i64 %2
}
; FIXME: Cannot test this yet, because the target-independent instruction
; selector handles this.
; CHECK-LABEL: lsl_sext_i16_i64
define i64 @lsl_sext_i16_i64(i16 %b) {
%1 = sext i16 %b to i64
%2 = shl i64 %1, 8
ret i64 %2
}
; CHECK-LABEL: lslv_i32
; CHECK: lsl {{w[0-9]*}}, w0, w1
define zeroext i32 @lslv_i32(i32 %a, i32 %b) {
@ -46,6 +130,26 @@ define zeroext i32 @lsl_i32(i32 %a) {
ret i32 %1
}
; FIXME: Cannot test this yet, because the target-independent instruction
; selector handles this.
; CHECK-LABEL: lsl_zext_i32_i64
define i64 @lsl_zext_i32_i64(i32 %b) {
%1 = zext i32 %b to i64
%2 = shl i64 %1, 16
ret i64 %2
}
; FIXME: Cannot test this yet, because the target-independent instruction
; selector handles this.
; CHECK-LABEL: lsl_sext_i32_i64
define i64 @lsl_sext_i32_i64(i32 %b) {
%1 = sext i32 %b to i64
%2 = shl i64 %1, 16
ret i64 %2
}
; FIXME: Cannot test this yet, because the target-independent instruction
; selector handles this.
; CHECK-LABEL: lslv_i64
; CHECK: lsl {{x[0-9]*}}, x0, x1
define i64 @lslv_i64(i64 %a, i64 %b) {
@ -78,6 +182,40 @@ define zeroext i8 @lsr_i8(i8 %a) {
ret i8 %1
}
; CHECK-LABEL: lsr_zext_i8_i16
; CHECK: ubfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4
define zeroext i16 @lsr_zext_i8_i16(i8 %b) {
%1 = zext i8 %b to i16
%2 = lshr i16 %1, 4
ret i16 %2
}
; CHECK-LABEL: lsr_sext_i8_i16
; CHECK: sxtb [[REG:w[0-9]+]], w0
; CHECK-NEXT: ubfx {{w[0-9]*}}, [[REG]], #4, #12
define signext i16 @lsr_sext_i8_i16(i8 %b) {
%1 = sext i8 %b to i16
%2 = lshr i16 %1, 4
ret i16 %2
}
; CHECK-LABEL: lsr_zext_i8_i32
; CHECK: ubfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4
define i32 @lsr_zext_i8_i32(i8 %b) {
%1 = zext i8 %b to i32
%2 = lshr i32 %1, 4
ret i32 %2
}
; CHECK-LABEL: lsr_sext_i8_i32
; CHECK: sxtb [[REG:w[0-9]+]], w0
; CHECK-NEXT: lsr {{w[0-9]*}}, [[REG]], #4
define i32 @lsr_sext_i8_i32(i8 %b) {
%1 = sext i8 %b to i32
%2 = lshr i32 %1, 4
ret i32 %2
}
; CHECK-LABEL: lsrv_i16
; CHECK: and [[REG1:w[0-9]+]], w0, #0xffff
; CHECK-NEXT: and [[REG2:w[0-9]+]], w1, #0xffff
@ -141,6 +279,38 @@ define zeroext i8 @asr_i8(i8 %a) {
ret i8 %1
}
; CHECK-LABEL: asr_zext_i8_i16
; CHECK: ubfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4
define zeroext i16 @asr_zext_i8_i16(i8 %b) {
%1 = zext i8 %b to i16
%2 = ashr i16 %1, 4
ret i16 %2
}
; CHECK-LABEL: asr_sext_i8_i16
; CHECK: sbfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4
define signext i16 @asr_sext_i8_i16(i8 %b) {
%1 = sext i8 %b to i16
%2 = ashr i16 %1, 4
ret i16 %2
}
; CHECK-LABEL: asr_zext_i8_i32
; CHECK: ubfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4
define i32 @asr_zext_i8_i32(i8 %b) {
%1 = zext i8 %b to i32
%2 = ashr i32 %1, 4
ret i32 %2
}
; CHECK-LABEL: asr_sext_i8_i32
; CHECK: sbfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4
define i32 @asr_sext_i8_i32(i8 %b) {
%1 = sext i8 %b to i32
%2 = ashr i32 %1, 4
ret i32 %2
}
; CHECK-LABEL: asrv_i16
; CHECK: sxth [[REG1:w[0-9]+]], w0
; CHECK-NEXT: and [[REG2:w[0-9]+]], w1, #0xffff