1 | //===- CSETest.cpp -----------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "GISelMITest.h" |
10 | #include "llvm/CodeGen/GlobalISel/CSEInfo.h" |
11 | #include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h" |
12 | #include "gtest/gtest.h" |
13 | |
14 | namespace { |
15 | |
16 | TEST_F(AArch64GISelMITest, TestCSE) { |
17 | setUp(); |
18 | if (!TM) |
19 | GTEST_SKIP(); |
20 | |
21 | LLT s16{LLT::scalar(SizeInBits: 16)}; |
22 | LLT s32{LLT::scalar(SizeInBits: 32)}; |
23 | auto MIBInput = B.buildInstr(Opc: TargetOpcode::G_TRUNC, DstOps: {s16}, SrcOps: {Copies[0]}); |
24 | auto MIBInput1 = B.buildInstr(Opc: TargetOpcode::G_TRUNC, DstOps: {s16}, SrcOps: {Copies[1]}); |
25 | auto MIBAdd = B.buildInstr(Opc: TargetOpcode::G_ADD, DstOps: {s16}, SrcOps: {MIBInput, MIBInput}); |
26 | GISelCSEInfo CSEInfo; |
27 | CSEInfo.setCSEConfig(std::make_unique<CSEConfigFull>()); |
28 | CSEInfo.analyze(MF&: *MF); |
29 | B.setCSEInfo(&CSEInfo); |
30 | CSEMIRBuilder CSEB(B.getState()); |
31 | |
32 | CSEB.setInsertPt(MBB&: B.getMBB(), II: B.getInsertPt()); |
33 | Register AddReg = MRI->createGenericVirtualRegister(Ty: s16); |
34 | auto MIBAddCopy = |
35 | CSEB.buildInstr(Opc: TargetOpcode::G_ADD, DstOps: {AddReg}, SrcOps: {MIBInput, MIBInput}); |
36 | EXPECT_EQ(MIBAddCopy->getOpcode(), TargetOpcode::COPY); |
37 | auto MIBAdd2 = |
38 | CSEB.buildInstr(Opc: TargetOpcode::G_ADD, DstOps: {s16}, SrcOps: {MIBInput, MIBInput}); |
39 | EXPECT_TRUE(&*MIBAdd == &*MIBAdd2); |
40 | auto MIBAdd4 = |
41 | CSEB.buildInstr(Opc: TargetOpcode::G_ADD, DstOps: {s16}, SrcOps: {MIBInput, MIBInput}); |
42 | EXPECT_TRUE(&*MIBAdd == &*MIBAdd4); |
43 | auto MIBAdd5 = |
44 | CSEB.buildInstr(Opc: TargetOpcode::G_ADD, DstOps: {s16}, SrcOps: {MIBInput, MIBInput1}); |
45 | EXPECT_TRUE(&*MIBAdd != &*MIBAdd5); |
46 | |
47 | // Try building G_CONSTANTS. |
48 | auto MIBCst = CSEB.buildConstant(Res: s32, Val: 0); |
49 | auto MIBCst1 = CSEB.buildConstant(Res: s32, Val: 0); |
50 | EXPECT_TRUE(&*MIBCst == &*MIBCst1); |
51 | // Try the CFing of BinaryOps. |
52 | auto MIBCF1 = CSEB.buildInstr(Opc: TargetOpcode::G_ADD, DstOps: {s32}, SrcOps: {MIBCst, MIBCst}); |
53 | EXPECT_TRUE(&*MIBCF1 == &*MIBCst); |
54 | |
55 | // Try out building FCONSTANTs. |
56 | auto MIBFP0 = CSEB.buildFConstant(Res: s32, Val: 1.0); |
57 | auto MIBFP0_1 = CSEB.buildFConstant(Res: s32, Val: 1.0); |
58 | EXPECT_TRUE(&*MIBFP0 == &*MIBFP0_1); |
59 | CSEInfo.print(); |
60 | |
61 | // Make sure buildConstant with a vector type doesn't crash, and the elements |
62 | // CSE. |
63 | auto Splat0 = CSEB.buildConstant(Res: LLT::fixed_vector(NumElements: 2, ScalarTy: s32), Val: 0); |
64 | EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR, Splat0->getOpcode()); |
65 | EXPECT_EQ(Splat0.getReg(1), Splat0.getReg(2)); |
66 | EXPECT_EQ(&*MIBCst, MRI->getVRegDef(Splat0.getReg(1))); |
67 | |
68 | auto FSplat = CSEB.buildFConstant(Res: LLT::fixed_vector(NumElements: 2, ScalarTy: s32), Val: 1.0); |
69 | EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR, FSplat->getOpcode()); |
70 | EXPECT_EQ(FSplat.getReg(1), FSplat.getReg(2)); |
71 | EXPECT_EQ(&*MIBFP0, MRI->getVRegDef(FSplat.getReg(1))); |
72 | |
73 | // Check G_UNMERGE_VALUES |
74 | auto MIBUnmerge = CSEB.buildUnmerge(Res: {s32, s32}, Op: Copies[0]); |
75 | auto MIBUnmerge2 = CSEB.buildUnmerge(Res: {s32, s32}, Op: Copies[0]); |
76 | EXPECT_TRUE(&*MIBUnmerge == &*MIBUnmerge2); |
77 | |
78 | // Check G_BUILD_VECTOR |
79 | Register Reg1 = MRI->createGenericVirtualRegister(Ty: s32); |
80 | Register Reg2 = MRI->createGenericVirtualRegister(Ty: s32); |
81 | auto BuildVec1 = |
82 | CSEB.buildBuildVector(Res: LLT::fixed_vector(NumElements: 4, ScalarSizeInBits: 32), Ops: {Reg1, Reg2, Reg1, Reg2}); |
83 | auto BuildVec2 = |
84 | CSEB.buildBuildVector(Res: LLT::fixed_vector(NumElements: 4, ScalarSizeInBits: 32), Ops: {Reg1, Reg2, Reg1, Reg2}); |
85 | EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR, BuildVec1->getOpcode()); |
86 | EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR, BuildVec2->getOpcode()); |
87 | EXPECT_TRUE(&*BuildVec1 == &*BuildVec2); |
88 | |
89 | // Check G_BUILD_VECTOR_TRUNC |
90 | auto BuildVecTrunc1 = CSEB.buildBuildVectorTrunc(Res: LLT::fixed_vector(NumElements: 4, ScalarSizeInBits: 16), |
91 | Ops: {Reg1, Reg2, Reg1, Reg2}); |
92 | auto BuildVecTrunc2 = CSEB.buildBuildVectorTrunc(Res: LLT::fixed_vector(NumElements: 4, ScalarSizeInBits: 16), |
93 | Ops: {Reg1, Reg2, Reg1, Reg2}); |
94 | EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR_TRUNC, BuildVecTrunc1->getOpcode()); |
95 | EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR_TRUNC, BuildVecTrunc2->getOpcode()); |
96 | EXPECT_TRUE(&*BuildVecTrunc1 == &*BuildVecTrunc2); |
97 | |
98 | // Check G_IMPLICIT_DEF |
99 | auto Undef0 = CSEB.buildUndef(Res: s32); |
100 | auto Undef1 = CSEB.buildUndef(Res: s32); |
101 | EXPECT_EQ(&*Undef0, &*Undef1); |
102 | |
103 | // If the observer is installed to the MF, CSE can also |
104 | // track new instructions built without the CSEBuilder and |
105 | // the newly built instructions are available for CSEing next |
106 | // time a build call is made through the CSEMIRBuilder. |
107 | // Additionally, the CSE implementation lazily hashes instructions |
108 | // (every build call) to give chance for the instruction to be fully |
109 | // built (say using .addUse().addDef().. so on). |
110 | GISelObserverWrapper WrapperObserver(&CSEInfo); |
111 | RAIIMFObsDelInstaller Installer(*MF, WrapperObserver); |
112 | MachineIRBuilder RegularBuilder(*MF); |
113 | RegularBuilder.setInsertPt(MBB&: *EntryMBB, II: EntryMBB->begin()); |
114 | auto NonCSEFMul = RegularBuilder.buildInstr(Opcode: TargetOpcode::G_AND) |
115 | .addDef(RegNo: MRI->createGenericVirtualRegister(Ty: s32)) |
116 | .addUse(RegNo: Copies[0]) |
117 | .addUse(RegNo: Copies[1]); |
118 | auto CSEFMul = |
119 | CSEB.buildInstr(Opc: TargetOpcode::G_AND, DstOps: {s32}, SrcOps: {Copies[0], Copies[1]}); |
120 | EXPECT_EQ(&*CSEFMul, &*NonCSEFMul); |
121 | |
122 | auto = CSEB.buildInstr(Opc: TargetOpcode::G_EXTRACT, DstOps: {s16}, |
123 | SrcOps: {Copies[0], static_cast<uint64_t>(0)}); |
124 | auto = CSEB.buildInstr(Opc: TargetOpcode::G_EXTRACT, DstOps: {s16}, |
125 | SrcOps: {Copies[0], static_cast<uint64_t>(0)}); |
126 | auto = CSEB.buildInstr(Opc: TargetOpcode::G_EXTRACT, DstOps: {s16}, |
127 | SrcOps: {Copies[0], static_cast<uint64_t>(1)}); |
128 | EXPECT_EQ(&*ExtractMIB, &*ExtractMIB1); |
129 | EXPECT_NE(&*ExtractMIB, &*ExtractMIB2); |
130 | |
131 | |
132 | auto SextInRegMIB = CSEB.buildSExtInReg(Res: s16, Op: Copies[0], ImmOp: 0); |
133 | auto SextInRegMIB1 = CSEB.buildSExtInReg(Res: s16, Op: Copies[0], ImmOp: 0); |
134 | auto SextInRegMIB2 = CSEB.buildSExtInReg(Res: s16, Op: Copies[0], ImmOp: 1); |
135 | EXPECT_EQ(&*SextInRegMIB, &*SextInRegMIB1); |
136 | EXPECT_NE(&*SextInRegMIB, &*SextInRegMIB2); |
137 | } |
138 | |
139 | TEST_F(AArch64GISelMITest, TestCSEConstantConfig) { |
140 | setUp(); |
141 | if (!TM) |
142 | GTEST_SKIP(); |
143 | |
144 | LLT s16{LLT::scalar(SizeInBits: 16)}; |
145 | auto MIBInput = B.buildInstr(Opc: TargetOpcode::G_TRUNC, DstOps: {s16}, SrcOps: {Copies[0]}); |
146 | auto MIBAdd = B.buildInstr(Opc: TargetOpcode::G_ADD, DstOps: {s16}, SrcOps: {MIBInput, MIBInput}); |
147 | auto MIBZero = B.buildConstant(Res: s16, Val: 0); |
148 | GISelCSEInfo CSEInfo; |
149 | CSEInfo.setCSEConfig(std::make_unique<CSEConfigConstantOnly>()); |
150 | CSEInfo.analyze(MF&: *MF); |
151 | B.setCSEInfo(&CSEInfo); |
152 | CSEMIRBuilder CSEB(B.getState()); |
153 | CSEB.setInsertPt(MBB&: *EntryMBB, II: EntryMBB->begin()); |
154 | auto MIBAdd1 = |
155 | CSEB.buildInstr(Opc: TargetOpcode::G_ADD, DstOps: {s16}, SrcOps: {MIBInput, MIBInput}); |
156 | // We should CSE constants only. Adds should not be CSEd. |
157 | EXPECT_TRUE(MIBAdd1->getOpcode() != TargetOpcode::COPY); |
158 | EXPECT_TRUE(&*MIBAdd1 != &*MIBAdd); |
159 | // We should CSE constant. |
160 | auto MIBZeroTmp = CSEB.buildConstant(Res: s16, Val: 0); |
161 | EXPECT_TRUE(&*MIBZero == &*MIBZeroTmp); |
162 | |
163 | // Check G_IMPLICIT_DEF |
164 | auto Undef0 = CSEB.buildUndef(Res: s16); |
165 | auto Undef1 = CSEB.buildUndef(Res: s16); |
166 | EXPECT_EQ(&*Undef0, &*Undef1); |
167 | } |
168 | |
169 | TEST_F(AArch64GISelMITest, TestCSEImmediateNextCSE) { |
170 | setUp(); |
171 | if (!TM) |
172 | GTEST_SKIP(); |
173 | |
174 | LLT s32{LLT::scalar(SizeInBits: 32)}; |
175 | // We want to check that when the CSE hit is on the next instruction, i.e. at |
176 | // the current insert pt, that the insertion point is moved ahead of the |
177 | // instruction. |
178 | |
179 | GISelCSEInfo CSEInfo; |
180 | CSEInfo.setCSEConfig(std::make_unique<CSEConfigConstantOnly>()); |
181 | CSEInfo.analyze(MF&: *MF); |
182 | B.setCSEInfo(&CSEInfo); |
183 | CSEMIRBuilder CSEB(B.getState()); |
184 | CSEB.buildConstant(Res: s32, Val: 0); |
185 | auto MIBCst2 = CSEB.buildConstant(Res: s32, Val: 2); |
186 | |
187 | // Move the insert point before the second constant. |
188 | CSEB.setInsertPt(MBB&: CSEB.getMBB(), II: --CSEB.getInsertPt()); |
189 | auto MIBCst3 = CSEB.buildConstant(Res: s32, Val: 2); |
190 | EXPECT_TRUE(&*MIBCst2 == &*MIBCst3); |
191 | EXPECT_TRUE(CSEB.getInsertPt() == CSEB.getMBB().end()); |
192 | } |
193 | |
194 | TEST_F(AArch64GISelMITest, TestConstantFoldCTL) { |
195 | setUp(); |
196 | if (!TM) |
197 | GTEST_SKIP(); |
198 | |
199 | LLT s32 = LLT::scalar(SizeInBits: 32); |
200 | |
201 | GISelCSEInfo CSEInfo; |
202 | CSEInfo.setCSEConfig(std::make_unique<CSEConfigConstantOnly>()); |
203 | CSEInfo.analyze(MF&: *MF); |
204 | B.setCSEInfo(&CSEInfo); |
205 | CSEMIRBuilder CSEB(B.getState()); |
206 | auto Cst8 = CSEB.buildConstant(Res: s32, Val: 8); |
207 | auto *CtlzDef = &*CSEB.buildCTLZ(Dst: s32, Src0: Cst8); |
208 | EXPECT_TRUE(CtlzDef->getOpcode() == TargetOpcode::G_CONSTANT); |
209 | EXPECT_TRUE(CtlzDef->getOperand(1).getCImm()->getZExtValue() == 28); |
210 | |
211 | // Test vector. |
212 | auto Cst16 = CSEB.buildConstant(Res: s32, Val: 16); |
213 | auto Cst32 = CSEB.buildConstant(Res: s32, Val: 32); |
214 | auto Cst64 = CSEB.buildConstant(Res: s32, Val: 64); |
215 | LLT VecTy = LLT::fixed_vector(NumElements: 4, ScalarTy: s32); |
216 | auto BV = CSEB.buildBuildVector(Res: VecTy, Ops: {Cst8.getReg(Idx: 0), Cst16.getReg(Idx: 0), |
217 | Cst32.getReg(Idx: 0), Cst64.getReg(Idx: 0)}); |
218 | CSEB.buildCTLZ(Dst: VecTy, Src0: BV); |
219 | |
220 | auto CheckStr = R"( |
221 | ; CHECK: [[CST8:%[0-9]+]]:_(s32) = G_CONSTANT i32 8 |
222 | ; CHECK: [[CST28:%[0-9]+]]:_(s32) = G_CONSTANT i32 28 |
223 | ; CHECK: [[CST16:%[0-9]+]]:_(s32) = G_CONSTANT i32 16 |
224 | ; CHECK: [[CST32:%[0-9]+]]:_(s32) = G_CONSTANT i32 32 |
225 | ; CHECK: [[CST64:%[0-9]+]]:_(s32) = G_CONSTANT i32 64 |
226 | ; CHECK: [[BV1:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[CST8]]:_(s32), [[CST16]]:_(s32), [[CST32]]:_(s32), [[CST64]]:_(s32) |
227 | ; CHECK: [[CST27:%[0-9]+]]:_(s32) = G_CONSTANT i32 27 |
228 | ; CHECK: [[CST26:%[0-9]+]]:_(s32) = G_CONSTANT i32 26 |
229 | ; CHECK: [[CST25:%[0-9]+]]:_(s32) = G_CONSTANT i32 25 |
230 | ; CHECK: [[BV2:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[CST28]]:_(s32), [[CST27]]:_(s32), [[CST26]]:_(s32), [[CST25]]:_(s32) |
231 | )" ; |
232 | |
233 | EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF; |
234 | } |
235 | |
236 | } // namespace |
237 | |