Skip to content

Commit c36c444

Browse files
aemersonmemfrob
authored andcommitted
[GlobalISel] Implement lowering for G_ROTR and G_ROTL.
This is a straightforward port. Differential Revision: https://reviews.llvm.org/D99449
1 parent e2ebc82 commit c36c444

File tree

3 files changed

+227
-0
lines changed

3 files changed

+227
-0
lines changed

llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ class LegalizerHelper {
348348
LegalizeResult lowerFunnelShiftWithInverse(MachineInstr &MI);
349349
LegalizeResult lowerFunnelShiftAsShifts(MachineInstr &MI);
350350
LegalizeResult lowerFunnelShift(MachineInstr &MI);
351+
LegalizeResult lowerRotateWithReverseRotate(MachineInstr &MI);
352+
LegalizeResult lowerRotate(MachineInstr &MI);
351353

352354
LegalizeResult lowerU64ToF32BitOps(MachineInstr &MI);
353355
LegalizeResult lowerUITOFP(MachineInstr &MI);

llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3228,6 +3228,9 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
32283228
case G_FSHL:
32293229
case G_FSHR:
32303230
return lowerFunnelShift(MI);
3231+
case G_ROTL:
3232+
case G_ROTR:
3233+
return lowerRotate(MI);
32313234
}
32323235
}
32333236

@@ -5351,6 +5354,72 @@ LegalizerHelper::lowerFunnelShift(MachineInstr &MI) {
53515354
return lowerFunnelShiftWithInverse(MI);
53525355
}
53535356

5357+
LegalizerHelper::LegalizeResult
5358+
LegalizerHelper::lowerRotateWithReverseRotate(MachineInstr &MI) {
5359+
Register Dst = MI.getOperand(0).getReg();
5360+
Register Src = MI.getOperand(1).getReg();
5361+
Register Amt = MI.getOperand(2).getReg();
5362+
LLT AmtTy = MRI.getType(Amt);
5363+
auto Zero = MIRBuilder.buildConstant(AmtTy, 0);
5364+
bool IsLeft = MI.getOpcode() == TargetOpcode::G_ROTL;
5365+
unsigned RevRot = IsLeft ? TargetOpcode::G_ROTR : TargetOpcode::G_ROTL;
5366+
auto Neg = MIRBuilder.buildSub(AmtTy, Zero, Amt);
5367+
MIRBuilder.buildInstr(RevRot, {Dst}, {Src, Neg});
5368+
MI.eraseFromParent();
5369+
return Legalized;
5370+
}
5371+
5372+
LegalizerHelper::LegalizeResult LegalizerHelper::lowerRotate(MachineInstr &MI) {
5373+
Register Dst = MI.getOperand(0).getReg();
5374+
Register Src = MI.getOperand(1).getReg();
5375+
Register Amt = MI.getOperand(2).getReg();
5376+
LLT DstTy = MRI.getType(Dst);
5377+
LLT SrcTy = MRI.getType(Dst);
5378+
LLT AmtTy = MRI.getType(Amt);
5379+
5380+
unsigned EltSizeInBits = DstTy.getScalarSizeInBits();
5381+
bool IsLeft = MI.getOpcode() == TargetOpcode::G_ROTL;
5382+
5383+
MIRBuilder.setInstrAndDebugLoc(MI);
5384+
5385+
// If a rotate in the other direction is supported, use it.
5386+
unsigned RevRot = IsLeft ? TargetOpcode::G_ROTR : TargetOpcode::G_ROTL;
5387+
if (LI.isLegalOrCustom({RevRot, {DstTy, SrcTy}}) &&
5388+
isPowerOf2_32(EltSizeInBits))
5389+
return lowerRotateWithReverseRotate(MI);
5390+
5391+
auto Zero = MIRBuilder.buildConstant(AmtTy, 0);
5392+
unsigned ShOpc = IsLeft ? TargetOpcode::G_SHL : TargetOpcode::G_LSHR;
5393+
unsigned RevShiftOpc = IsLeft ? TargetOpcode::G_LSHR : TargetOpcode::G_SHL;
5394+
auto BitWidthMinusOneC = MIRBuilder.buildConstant(AmtTy, EltSizeInBits - 1);
5395+
Register ShVal;
5396+
Register RevShiftVal;
5397+
if (isPowerOf2_32(EltSizeInBits)) {
5398+
// (rotl x, c) -> x << (c & (w - 1)) | x >> (-c & (w - 1))
5399+
// (rotr x, c) -> x >> (c & (w - 1)) | x << (-c & (w - 1))
5400+
auto NegAmt = MIRBuilder.buildSub(AmtTy, Zero, Amt);
5401+
auto ShAmt = MIRBuilder.buildAnd(AmtTy, Amt, BitWidthMinusOneC);
5402+
ShVal = MIRBuilder.buildInstr(ShOpc, {DstTy}, {Src, ShAmt}).getReg(0);
5403+
auto RevAmt = MIRBuilder.buildAnd(AmtTy, NegAmt, BitWidthMinusOneC);
5404+
RevShiftVal =
5405+
MIRBuilder.buildInstr(RevShiftOpc, {DstTy}, {Src, RevAmt}).getReg(0);
5406+
} else {
5407+
// (rotl x, c) -> x << (c % w) | x >> 1 >> (w - 1 - (c % w))
5408+
// (rotr x, c) -> x >> (c % w) | x << 1 << (w - 1 - (c % w))
5409+
auto BitWidthC = MIRBuilder.buildConstant(AmtTy, EltSizeInBits);
5410+
auto ShAmt = MIRBuilder.buildURem(AmtTy, Amt, BitWidthC);
5411+
ShVal = MIRBuilder.buildInstr(ShOpc, {DstTy}, {Src, ShAmt}).getReg(0);
5412+
auto RevAmt = MIRBuilder.buildSub(AmtTy, BitWidthMinusOneC, ShAmt);
5413+
auto One = MIRBuilder.buildConstant(AmtTy, 1);
5414+
auto Inner = MIRBuilder.buildInstr(RevShiftOpc, {DstTy}, {Src, One});
5415+
RevShiftVal =
5416+
MIRBuilder.buildInstr(RevShiftOpc, {DstTy}, {Inner, RevAmt}).getReg(0);
5417+
}
5418+
MIRBuilder.buildOr(Dst, ShVal, RevShiftVal);
5419+
MI.eraseFromParent();
5420+
return Legalized;
5421+
}
5422+
53545423
// Expand s32 = G_UITOFP s64 using bit operations to an IEEE float
53555424
// representation.
53565425
LegalizerHelper::LegalizeResult

llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,162 @@ class DummyGISelObserver : public GISelChangeObserver {
2323
void erasingInstr(MachineInstr &MI) override {}
2424
};
2525

26+
// Test G_ROTL/G_ROTR lowering.
27+
TEST_F(AArch64GISelMITest, LowerRotates) {
28+
setUp();
29+
if (!TM)
30+
return;
31+
32+
// Declare your legalization info
33+
DefineLegalizerInfo(A, {
34+
getActionDefinitionsBuilder({G_ROTR, G_ROTL}).lower(); });
35+
36+
LLT S32 = LLT::scalar(32);
37+
auto Src = B.buildTrunc(S32, Copies[0]);
38+
auto Amt = B.buildTrunc(S32, Copies[1]);
39+
auto ROTR = B.buildInstr(TargetOpcode::G_ROTR, {S32}, {Src, Amt});
40+
auto ROTL = B.buildInstr(TargetOpcode::G_ROTL, {S32}, {Src, Amt});
41+
42+
AInfo Info(MF->getSubtarget());
43+
DummyGISelObserver Observer;
44+
LegalizerHelper Helper(*MF, Info, Observer, B);
45+
// Perform Legalization
46+
EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
47+
Helper.lower(*ROTR, 0, S32));
48+
EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
49+
Helper.lower(*ROTL, 0, S32));
50+
51+
auto CheckStr = R"(
52+
; Check G_ROTR
53+
CHECK: [[SRC:%[0-9]+]]:_(s32) = G_TRUNC
54+
CHECK: [[AMT:%[0-9]+]]:_(s32) = G_TRUNC
55+
CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
56+
CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 31
57+
CHECK: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[C]]:_, [[AMT]]:_
58+
CHECK: [[AND:%[0-9]+]]:_(s32) = G_AND [[AMT]]:_, [[C1]]:_
59+
CHECK: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[SRC]]:_, [[AND]]:_(s32)
60+
CHECK: [[AND1:%[0-9]+]]:_(s32) = G_AND [[SUB]]:_, [[C1]]:_
61+
CHECK: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[SRC]]:_, [[AND1]]:_(s32)
62+
CHECK: G_OR [[LSHR]]:_, [[SHL]]:_
63+
64+
; Check G_ROTL
65+
CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
66+
CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 31
67+
CHECK: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[C]]:_, [[AMT]]:_
68+
CHECK: [[AND:%[0-9]+]]:_(s32) = G_AND [[AMT]]:_, [[C1]]:_
69+
CHECK: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[SRC]]:_, [[AND]]:_(s32)
70+
CHECK: [[AND1:%[0-9]+]]:_(s32) = G_AND [[SUB]]:_, [[C1]]:_
71+
CHECK: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[SRC]]:_, [[AND1]]:_(s32)
72+
CHECK: G_OR [[SHL]]:_, [[LSHR]]:_
73+
)";
74+
75+
// Check
76+
EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
77+
}
78+
79+
// Test G_ROTL/G_ROTR non-pow2 lowering.
80+
TEST_F(AArch64GISelMITest, LowerRotatesNonPow2) {
81+
setUp();
82+
if (!TM)
83+
return;
84+
85+
// Declare your legalization info
86+
DefineLegalizerInfo(A, {
87+
getActionDefinitionsBuilder({G_ROTR, G_ROTL}).lower(); });
88+
89+
LLT S24 = LLT::scalar(24);
90+
auto Src = B.buildTrunc(S24, Copies[0]);
91+
auto Amt = B.buildTrunc(S24, Copies[1]);
92+
auto ROTR = B.buildInstr(TargetOpcode::G_ROTR, {S24}, {Src, Amt});
93+
auto ROTL = B.buildInstr(TargetOpcode::G_ROTL, {S24}, {Src, Amt});
94+
95+
AInfo Info(MF->getSubtarget());
96+
DummyGISelObserver Observer;
97+
LegalizerHelper Helper(*MF, Info, Observer, B);
98+
// Perform Legalization
99+
EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
100+
Helper.lower(*ROTR, 0, S24));
101+
EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
102+
Helper.lower(*ROTL, 0, S24));
103+
104+
auto CheckStr = R"(
105+
; Check G_ROTR
106+
CHECK: [[SRC:%[0-9]+]]:_(s24) = G_TRUNC
107+
CHECK: [[AMT:%[0-9]+]]:_(s24) = G_TRUNC
108+
CHECK: [[C:%[0-9]+]]:_(s24) = G_CONSTANT i24 0
109+
CHECK: [[C1:%[0-9]+]]:_(s24) = G_CONSTANT i24 23
110+
CHECK: [[C2:%[0-9]+]]:_(s24) = G_CONSTANT i24 24
111+
CHECK: [[UREM:%[0-9]+]]:_(s24) = G_UREM [[AMT]]:_, [[C2]]:_
112+
CHECK: [[LSHR:%[0-9]+]]:_(s24) = G_LSHR [[SRC]]:_, [[UREM]]:_(s24)
113+
CHECK: [[SUB:%[0-9]+]]:_(s24) = G_SUB [[C1]]:_, [[UREM]]:_
114+
CHECK: [[C4:%[0-9]+]]:_(s24) = G_CONSTANT i24 1
115+
CHECK: [[SHL:%[0-9]+]]:_(s24) = G_SHL [[SRC]]:_, [[C4]]:_(s24)
116+
CHECK: [[SHL2:%[0-9]+]]:_(s24) = G_SHL [[SHL]]:_, [[SUB]]:_(s24)
117+
CHECK: G_OR [[LSHR]]:_, [[SHL2]]:_
118+
119+
; Check G_ROTL
120+
CHECK: [[C:%[0-9]+]]:_(s24) = G_CONSTANT i24 0
121+
CHECK: [[C1:%[0-9]+]]:_(s24) = G_CONSTANT i24 23
122+
CHECK: [[C2:%[0-9]+]]:_(s24) = G_CONSTANT i24 24
123+
CHECK: [[UREM:%[0-9]+]]:_(s24) = G_UREM [[AMT]]:_, [[C2]]:_
124+
CHECK: [[SHL:%[0-9]+]]:_(s24) = G_SHL [[SRC]]:_, [[UREM]]:_(s24)
125+
CHECK: [[SUB:%[0-9]+]]:_(s24) = G_SUB [[C1]]:_, [[UREM]]:_
126+
CHECK: [[C4:%[0-9]+]]:_(s24) = G_CONSTANT i24 1
127+
CHECK: [[LSHR:%[0-9]+]]:_(s24) = G_LSHR [[SRC]]:_, [[C4]]:_(s24)
128+
CHECK: [[LSHR2:%[0-9]+]]:_(s24) = G_LSHR [[LSHR]]:_, [[SUB]]:_(s24)
129+
CHECK: G_OR [[SHL]]:_, [[LSHR2]]:_
130+
)";
131+
132+
// Check
133+
EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
134+
}
135+
136+
// Test vector G_ROTR lowering.
137+
TEST_F(AArch64GISelMITest, LowerRotatesVector) {
138+
setUp();
139+
if (!TM)
140+
return;
141+
142+
// Declare your legalization info
143+
DefineLegalizerInfo(A, {
144+
getActionDefinitionsBuilder({G_ROTR, G_ROTL}).lower(); });
145+
146+
LLT S32 = LLT::scalar(32);
147+
LLT V4S32 = LLT::vector(4, S32);
148+
auto SrcTrunc = B.buildTrunc(S32, Copies[0]);
149+
auto Src = B.buildSplatVector(V4S32, SrcTrunc);
150+
auto AmtTrunc = B.buildTrunc(S32, Copies[1]);
151+
auto Amt = B.buildSplatVector(V4S32, AmtTrunc);
152+
auto ROTR = B.buildInstr(TargetOpcode::G_ROTR, {V4S32}, {Src, Amt});
153+
154+
AInfo Info(MF->getSubtarget());
155+
DummyGISelObserver Observer;
156+
LegalizerHelper Helper(*MF, Info, Observer, B);
157+
// Perform Legalization
158+
EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
159+
Helper.lower(*ROTR, 0, V4S32));
160+
161+
auto CheckStr = R"(
162+
CHECK: [[SRCTRUNC:%[0-9]+]]:_(s32) = G_TRUNC
163+
CHECK: [[SRC:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[SRCTRUNC]]
164+
CHECK: [[AMTTRUNC:%[0-9]+]]:_(s32) = G_TRUNC
165+
CHECK: [[AMT:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[AMTTRUNC]]
166+
CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
167+
CHECK: [[ZERO:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[C]]
168+
CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 31
169+
CHECK: [[VEC31:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[C1]]
170+
CHECK: [[SUB:%[0-9]+]]:_(<4 x s32>) = G_SUB [[ZERO]]:_, [[AMT]]:_
171+
CHECK: [[AND:%[0-9]+]]:_(<4 x s32>) = G_AND [[AMT]]:_, [[VEC31]]:_
172+
CHECK: [[LSHR:%[0-9]+]]:_(<4 x s32>) = G_LSHR [[SRC]]:_, [[AND]]:_(<4 x s32>)
173+
CHECK: [[AND1:%[0-9]+]]:_(<4 x s32>) = G_AND [[SUB]]:_, [[VEC31]]:_
174+
CHECK: [[SHL:%[0-9]+]]:_(<4 x s32>) = G_SHL [[SRC]]:_, [[AND1]]:_(<4 x s32>)
175+
CHECK: G_OR [[LSHR]]:_, [[SHL]]:_
176+
)";
177+
178+
// Check
179+
EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
180+
}
181+
26182
// Test CTTZ expansion when CTTZ_ZERO_UNDEF is legal or custom,
27183
// in which case it becomes CTTZ_ZERO_UNDEF with select.
28184
TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ0) {

0 commit comments

Comments
 (0)