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/CSEMIRBuilder.h"
11 #include "gtest/gtest.h"
12 
13 namespace {
14 
TEST_F(AArch64GISelMITest,TestCSE)15 TEST_F(AArch64GISelMITest, TestCSE) {
16   setUp();
17   if (!TM)
18     return;
19 
20   LLT s16{LLT::scalar(16)};
21   LLT s32{LLT::scalar(32)};
22   auto MIBInput = B.buildInstr(TargetOpcode::G_TRUNC, {s16}, {Copies[0]});
23   auto MIBInput1 = B.buildInstr(TargetOpcode::G_TRUNC, {s16}, {Copies[1]});
24   auto MIBAdd = B.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
25   GISelCSEInfo CSEInfo;
26   CSEInfo.setCSEConfig(std::make_unique<CSEConfigFull>());
27   CSEInfo.analyze(*MF);
28   B.setCSEInfo(&CSEInfo);
29   CSEMIRBuilder CSEB(B.getState());
30 
31   CSEB.setInsertPt(B.getMBB(), B.getInsertPt());
32   Register AddReg = MRI->createGenericVirtualRegister(s16);
33   auto MIBAddCopy =
34       CSEB.buildInstr(TargetOpcode::G_ADD, {AddReg}, {MIBInput, MIBInput});
35   EXPECT_EQ(MIBAddCopy->getOpcode(), TargetOpcode::COPY);
36   auto MIBAdd2 =
37       CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
38   EXPECT_TRUE(&*MIBAdd == &*MIBAdd2);
39   auto MIBAdd4 =
40       CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
41   EXPECT_TRUE(&*MIBAdd == &*MIBAdd4);
42   auto MIBAdd5 =
43       CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput1});
44   EXPECT_TRUE(&*MIBAdd != &*MIBAdd5);
45 
46   // Try building G_CONSTANTS.
47   auto MIBCst = CSEB.buildConstant(s32, 0);
48   auto MIBCst1 = CSEB.buildConstant(s32, 0);
49   EXPECT_TRUE(&*MIBCst == &*MIBCst1);
50   // Try the CFing of BinaryOps.
51   auto MIBCF1 = CSEB.buildInstr(TargetOpcode::G_ADD, {s32}, {MIBCst, MIBCst});
52   EXPECT_TRUE(&*MIBCF1 == &*MIBCst);
53 
54   // Try out building FCONSTANTs.
55   auto MIBFP0 = CSEB.buildFConstant(s32, 1.0);
56   auto MIBFP0_1 = CSEB.buildFConstant(s32, 1.0);
57   EXPECT_TRUE(&*MIBFP0 == &*MIBFP0_1);
58   CSEInfo.print();
59 
60   // Make sure buildConstant with a vector type doesn't crash, and the elements
61   // CSE.
62   auto Splat0 = CSEB.buildConstant(LLT::vector(2, s32), 0);
63   EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR, Splat0->getOpcode());
64   EXPECT_EQ(Splat0.getReg(1), Splat0.getReg(2));
65   EXPECT_EQ(&*MIBCst, MRI->getVRegDef(Splat0.getReg(1)));
66 
67   auto FSplat = CSEB.buildFConstant(LLT::vector(2, s32), 1.0);
68   EXPECT_EQ(TargetOpcode::G_BUILD_VECTOR, FSplat->getOpcode());
69   EXPECT_EQ(FSplat.getReg(1), FSplat.getReg(2));
70   EXPECT_EQ(&*MIBFP0, MRI->getVRegDef(FSplat.getReg(1)));
71 
72   // Check G_UNMERGE_VALUES
73   auto MIBUnmerge = CSEB.buildUnmerge({s32, s32}, Copies[0]);
74   auto MIBUnmerge2 = CSEB.buildUnmerge({s32, s32}, Copies[0]);
75   EXPECT_TRUE(&*MIBUnmerge == &*MIBUnmerge2);
76 
77   // Check G_IMPLICIT_DEF
78   auto Undef0 = CSEB.buildUndef(s32);
79   auto Undef1 = CSEB.buildUndef(s32);
80   EXPECT_EQ(&*Undef0, &*Undef1);
81 
82   // If the observer is installed to the MF, CSE can also
83   // track new instructions built without the CSEBuilder and
84   // the newly built instructions are available for CSEing next
85   // time a build call is made through the CSEMIRBuilder.
86   // Additionally, the CSE implementation lazily hashes instructions
87   // (every build call) to give chance for the instruction to be fully
88   // built (say using .addUse().addDef().. so on).
89   GISelObserverWrapper WrapperObserver(&CSEInfo);
90   RAIIMFObsDelInstaller Installer(*MF, WrapperObserver);
91   MachineIRBuilder RegularBuilder(*MF);
92   RegularBuilder.setInsertPt(*EntryMBB, EntryMBB->begin());
93   auto NonCSEFMul = RegularBuilder.buildInstr(TargetOpcode::G_AND)
94                         .addDef(MRI->createGenericVirtualRegister(s32))
95                         .addUse(Copies[0])
96                         .addUse(Copies[1]);
97   auto CSEFMul =
98       CSEB.buildInstr(TargetOpcode::G_AND, {s32}, {Copies[0], Copies[1]});
99   EXPECT_EQ(&*CSEFMul, &*NonCSEFMul);
100 
101   auto ExtractMIB = CSEB.buildInstr(TargetOpcode::G_EXTRACT, {s16},
102                                     {Copies[0], static_cast<uint64_t>(0)});
103   auto ExtractMIB1 = CSEB.buildInstr(TargetOpcode::G_EXTRACT, {s16},
104                                      {Copies[0], static_cast<uint64_t>(0)});
105   auto ExtractMIB2 = CSEB.buildInstr(TargetOpcode::G_EXTRACT, {s16},
106                                      {Copies[0], static_cast<uint64_t>(1)});
107   EXPECT_EQ(&*ExtractMIB, &*ExtractMIB1);
108   EXPECT_NE(&*ExtractMIB, &*ExtractMIB2);
109 }
110 
TEST_F(AArch64GISelMITest,TestCSEConstantConfig)111 TEST_F(AArch64GISelMITest, TestCSEConstantConfig) {
112   setUp();
113   if (!TM)
114     return;
115 
116   LLT s16{LLT::scalar(16)};
117   auto MIBInput = B.buildInstr(TargetOpcode::G_TRUNC, {s16}, {Copies[0]});
118   auto MIBAdd = B.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
119   auto MIBZero = B.buildConstant(s16, 0);
120   GISelCSEInfo CSEInfo;
121   CSEInfo.setCSEConfig(std::make_unique<CSEConfigConstantOnly>());
122   CSEInfo.analyze(*MF);
123   B.setCSEInfo(&CSEInfo);
124   CSEMIRBuilder CSEB(B.getState());
125   CSEB.setInsertPt(*EntryMBB, EntryMBB->begin());
126   auto MIBAdd1 =
127       CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
128   // We should CSE constants only. Adds should not be CSEd.
129   EXPECT_TRUE(MIBAdd1->getOpcode() != TargetOpcode::COPY);
130   EXPECT_TRUE(&*MIBAdd1 != &*MIBAdd);
131   // We should CSE constant.
132   auto MIBZeroTmp = CSEB.buildConstant(s16, 0);
133   EXPECT_TRUE(&*MIBZero == &*MIBZeroTmp);
134 
135   // Check G_IMPLICIT_DEF
136   auto Undef0 = CSEB.buildUndef(s16);
137   auto Undef1 = CSEB.buildUndef(s16);
138   EXPECT_EQ(&*Undef0, &*Undef1);
139 }
140 
TEST_F(AArch64GISelMITest,TestCSEImmediateNextCSE)141 TEST_F(AArch64GISelMITest, TestCSEImmediateNextCSE) {
142   setUp();
143   if (!TM)
144     return;
145 
146   LLT s32{LLT::scalar(32)};
147   // We want to check that when the CSE hit is on the next instruction, i.e. at
148   // the current insert pt, that the insertion point is moved ahead of the
149   // instruction.
150 
151   GISelCSEInfo CSEInfo;
152   CSEInfo.setCSEConfig(std::make_unique<CSEConfigConstantOnly>());
153   CSEInfo.analyze(*MF);
154   B.setCSEInfo(&CSEInfo);
155   CSEMIRBuilder CSEB(B.getState());
156   CSEB.buildConstant(s32, 0);
157   auto MIBCst2 = CSEB.buildConstant(s32, 2);
158 
159   // Move the insert point before the second constant.
160   CSEB.setInsertPt(CSEB.getMBB(), --CSEB.getInsertPt());
161   auto MIBCst3 = CSEB.buildConstant(s32, 2);
162   EXPECT_TRUE(&*MIBCst2 == &*MIBCst3);
163   EXPECT_TRUE(CSEB.getInsertPt() == CSEB.getMBB().end());
164 }
165 
166 } // namespace
167