xref: /llvm-project/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp (revision 459a82e6890ff41e30d486f36c8c7ec22628bb7a)
1 //===- RISCVInstrInfoTest.cpp - RISCVInstrInfo unit tests -----------------===//
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 "RISCVInstrInfo.h"
10 #include "RISCVSubtarget.h"
11 #include "RISCVTargetMachine.h"
12 #include "llvm/CodeGen/MachineModuleInfo.h"
13 #include "llvm/IR/DebugInfoMetadata.h"
14 #include "llvm/IR/Module.h"
15 #include "llvm/MC/TargetRegistry.h"
16 #include "llvm/Support/TargetSelect.h"
17 #include "llvm/Target/TargetLoweringObjectFile.h"
18 #include "llvm/Target/TargetMachine.h"
19 #include "llvm/Target/TargetOptions.h"
20 
21 #include "gtest/gtest.h"
22 
23 #include <memory>
24 
25 using namespace llvm;
26 
27 namespace {
28 
29 class RISCVInstrInfoTest : public testing::TestWithParam<const char *> {
30 protected:
31   std::unique_ptr<RISCVTargetMachine> TM;
32   std::unique_ptr<LLVMContext> Ctx;
33   std::unique_ptr<RISCVSubtarget> ST;
34   std::unique_ptr<MachineModuleInfo> MMI;
35   std::unique_ptr<MachineFunction> MF;
36   std::unique_ptr<Module> M;
37 
38   static void SetUpTestSuite() {
39     LLVMInitializeRISCVTargetInfo();
40     LLVMInitializeRISCVTarget();
41     LLVMInitializeRISCVTargetMC();
42   }
43 
44   RISCVInstrInfoTest() {
45     std::string Error;
46     auto TT(Triple::normalize(GetParam()));
47     const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error);
48     TargetOptions Options;
49 
50     TM.reset(static_cast<RISCVTargetMachine *>(TheTarget->createTargetMachine(
51         TT, "generic", "", Options, std::nullopt, std::nullopt,
52         CodeGenOptLevel::Default)));
53 
54     Ctx = std::make_unique<LLVMContext>();
55     M = std::make_unique<Module>("Module", *Ctx);
56     M->setDataLayout(TM->createDataLayout());
57     auto *FType = FunctionType::get(Type::getVoidTy(*Ctx), false);
58     auto *F = Function::Create(FType, GlobalValue::ExternalLinkage, "Test", *M);
59     MMI = std::make_unique<MachineModuleInfo>(TM.get());
60 
61     ST = std::make_unique<RISCVSubtarget>(
62         TM->getTargetTriple(), TM->getTargetCPU(), TM->getTargetCPU(),
63         TM->getTargetFeatureString(),
64         TM->getTargetTriple().isArch64Bit() ? "lp64" : "ilp32", 0, 0, *TM);
65 
66     MF = std::make_unique<MachineFunction>(*F, *TM, *ST, MMI->getContext(), 42);
67   }
68 };
69 
70 TEST_P(RISCVInstrInfoTest, IsAddImmediate) {
71   const RISCVInstrInfo *TII = ST->getInstrInfo();
72   DebugLoc DL;
73 
74   MachineInstr *MI1 = BuildMI(*MF, DL, TII->get(RISCV::ADDI), RISCV::X1)
75                           .addReg(RISCV::X2)
76                           .addImm(-128)
77                           .getInstr();
78   auto MI1Res = TII->isAddImmediate(*MI1, RISCV::X1);
79   ASSERT_TRUE(MI1Res.has_value());
80   EXPECT_EQ(MI1Res->Reg, RISCV::X2);
81   EXPECT_EQ(MI1Res->Imm, -128);
82   EXPECT_FALSE(TII->isAddImmediate(*MI1, RISCV::X2).has_value());
83 
84   MachineInstr *MI2 =
85       BuildMI(*MF, DL, TII->get(RISCV::LUI), RISCV::X1).addImm(-128).getInstr();
86   EXPECT_FALSE(TII->isAddImmediate(*MI2, RISCV::X1));
87 
88   // Check ADDIW isn't treated as isAddImmediate.
89   if (ST->is64Bit()) {
90     MachineInstr *MI3 = BuildMI(*MF, DL, TII->get(RISCV::ADDIW), RISCV::X1)
91                             .addReg(RISCV::X2)
92                             .addImm(-128)
93                             .getInstr();
94     EXPECT_FALSE(TII->isAddImmediate(*MI3, RISCV::X1));
95   }
96 }
97 
98 TEST_P(RISCVInstrInfoTest, IsCopyInstrImpl) {
99   const RISCVInstrInfo *TII = ST->getInstrInfo();
100   DebugLoc DL;
101 
102   // ADDI.
103 
104   MachineInstr *MI1 = BuildMI(*MF, DL, TII->get(RISCV::ADDI), RISCV::X1)
105                           .addReg(RISCV::X2)
106                           .addImm(-128)
107                           .getInstr();
108   auto MI1Res = TII->isCopyInstrImpl(*MI1);
109   EXPECT_FALSE(MI1Res.has_value());
110 
111   MachineInstr *MI2 = BuildMI(*MF, DL, TII->get(RISCV::ADDI), RISCV::X1)
112                           .addReg(RISCV::X2)
113                           .addImm(0)
114                           .getInstr();
115   auto MI2Res = TII->isCopyInstrImpl(*MI2);
116   ASSERT_TRUE(MI2Res.has_value());
117   EXPECT_EQ(MI2Res->Destination->getReg(), RISCV::X1);
118   EXPECT_EQ(MI2Res->Source->getReg(), RISCV::X2);
119 
120   // Partial coverage of FSGNJ_* instructions.
121 
122   MachineInstr *MI3 = BuildMI(*MF, DL, TII->get(RISCV::FSGNJ_D), RISCV::F1_D)
123                           .addReg(RISCV::F2_D)
124                           .addReg(RISCV::F1_D)
125                           .getInstr();
126   auto MI3Res = TII->isCopyInstrImpl(*MI3);
127   EXPECT_FALSE(MI3Res.has_value());
128 
129   MachineInstr *MI4 = BuildMI(*MF, DL, TII->get(RISCV::FSGNJ_D), RISCV::F1_D)
130                           .addReg(RISCV::F2_D)
131                           .addReg(RISCV::F2_D)
132                           .getInstr();
133   auto MI4Res = TII->isCopyInstrImpl(*MI4);
134   ASSERT_TRUE(MI4Res.has_value());
135   EXPECT_EQ(MI4Res->Destination->getReg(), RISCV::F1_D);
136   EXPECT_EQ(MI4Res->Source->getReg(), RISCV::F2_D);
137 
138   // ADD. TODO: Should return true for add reg, x0 and add x0, reg.
139   MachineInstr *MI5 = BuildMI(*MF, DL, TII->get(RISCV::ADD), RISCV::X1)
140                           .addReg(RISCV::X2)
141                           .addReg(RISCV::X3)
142                           .getInstr();
143   auto MI5Res = TII->isCopyInstrImpl(*MI5);
144   EXPECT_FALSE(MI5Res.has_value());
145 
146   MachineInstr *MI6 = BuildMI(*MF, DL, TII->get(RISCV::ADD), RISCV::X1)
147                           .addReg(RISCV::X0)
148                           .addReg(RISCV::X2)
149                           .getInstr();
150   auto MI6Res = TII->isCopyInstrImpl(*MI6);
151   EXPECT_FALSE(MI6Res.has_value());
152 
153   MachineInstr *MI7 = BuildMI(*MF, DL, TII->get(RISCV::ADD), RISCV::X1)
154                           .addReg(RISCV::X2)
155                           .addReg(RISCV::X0)
156                           .getInstr();
157   auto MI7Res = TII->isCopyInstrImpl(*MI7);
158   EXPECT_FALSE(MI7Res.has_value());
159 }
160 
161 TEST_P(RISCVInstrInfoTest, GetMemOperandsWithOffsetWidth) {
162   const RISCVInstrInfo *TII = ST->getInstrInfo();
163   const TargetRegisterInfo *TRI = ST->getRegisterInfo();
164   DebugLoc DL;
165 
166   SmallVector<const MachineOperand *> BaseOps;
167   LocationSize Width = 0;
168   int64_t Offset;
169   bool OffsetIsScalable;
170 
171   auto MMO = MF->getMachineMemOperand(MachinePointerInfo(),
172                                       MachineMemOperand::MOLoad, 1, Align(1));
173   MachineInstr *MI = BuildMI(*MF, DL, TII->get(RISCV::LB), RISCV::X1)
174                          .addReg(RISCV::X2)
175                          .addImm(-128)
176                          .addMemOperand(MMO)
177                          .getInstr();
178   bool Res = TII->getMemOperandsWithOffsetWidth(*MI, BaseOps, Offset,
179                                                 OffsetIsScalable, Width, TRI);
180   ASSERT_TRUE(Res);
181   ASSERT_EQ(BaseOps.size(), 1u);
182   ASSERT_TRUE(BaseOps.front()->isReg());
183   EXPECT_EQ(BaseOps.front()->getReg(), RISCV::X2);
184   EXPECT_EQ(Offset, -128);
185   EXPECT_FALSE(OffsetIsScalable);
186   EXPECT_EQ(Width, 1u);
187 
188   BaseOps.clear();
189   MMO = MF->getMachineMemOperand(MachinePointerInfo(),
190                                  MachineMemOperand::MOStore, 4, Align(4));
191   MI = BuildMI(*MF, DL, TII->get(RISCV::FSW))
192            .addReg(RISCV::F3_F)
193            .addReg(RISCV::X3)
194            .addImm(36)
195            .addMemOperand(MMO);
196   Res = TII->getMemOperandsWithOffsetWidth(*MI, BaseOps, Offset,
197                                            OffsetIsScalable, Width, TRI);
198   ASSERT_TRUE(Res);
199   ASSERT_EQ(BaseOps.size(), 1u);
200   ASSERT_TRUE(BaseOps.front()->isReg());
201   EXPECT_EQ(BaseOps.front()->getReg(), RISCV::X3);
202   EXPECT_EQ(Offset, 36);
203   EXPECT_FALSE(OffsetIsScalable);
204   EXPECT_EQ(Width, 4u);
205 
206   BaseOps.clear();
207   MMO = MF->getMachineMemOperand(MachinePointerInfo(),
208                                  MachineMemOperand::MOStore, 16, Align(16));
209   MI = BuildMI(*MF, DL, TII->get(RISCV::PseudoVLE32_V_M1), RISCV::V8)
210            .addReg(RISCV::X3)
211            .addMemOperand(MMO);
212   Res = TII->getMemOperandsWithOffsetWidth(*MI, BaseOps, Offset,
213                                            OffsetIsScalable, Width, TRI);
214   ASSERT_FALSE(Res); // Vector loads/stored are not handled for now.
215 
216   BaseOps.clear();
217   MI = BuildMI(*MF, DL, TII->get(RISCV::ADDI), RISCV::X4)
218            .addReg(RISCV::X5)
219            .addImm(16);
220   Res = TII->getMemOperandsWithOffsetWidth(*MI, BaseOps, Offset,
221                                            OffsetIsScalable, Width, TRI);
222 
223   BaseOps.clear();
224   MMO = MF->getMachineMemOperand(MachinePointerInfo(),
225                                  MachineMemOperand::MOStore, 4, Align(4));
226   MI = BuildMI(*MF, DL, TII->get(RISCV::SW))
227            .addReg(RISCV::X3)
228            .addFrameIndex(2)
229            .addImm(4)
230            .addMemOperand(MMO);
231   Res = TII->getMemOperandsWithOffsetWidth(*MI, BaseOps, Offset,
232                                            OffsetIsScalable, Width, TRI);
233   ASSERT_TRUE(Res);
234   ASSERT_EQ(BaseOps.size(), 1u);
235   ASSERT_TRUE(BaseOps.front()->isFI());
236   EXPECT_EQ(BaseOps.front()->getIndex(), 2);
237   EXPECT_EQ(Offset, 4);
238   EXPECT_FALSE(OffsetIsScalable);
239   EXPECT_EQ(Width, 4u);
240 }
241 
242 static void expectDIEPrintResult(const DIExpression *Expr, StringRef Expected) {
243   std::string Output;
244   raw_string_ostream OS(Output);
245   Expr->print(OS);
246   EXPECT_EQ(Output, Expected);
247 }
248 
249 TEST_P(RISCVInstrInfoTest, DescribeLoadedValue) {
250   const RISCVInstrInfo *TII = ST->getInstrInfo();
251   DebugLoc DL;
252 
253   MachineBasicBlock *MBB = MF->CreateMachineBasicBlock();
254   MF->getProperties().set(MachineFunctionProperties::Property::NoVRegs);
255 
256   // Register move.
257   auto *MI1 = BuildMI(*MBB, MBB->begin(), DL, TII->get(RISCV::ADDI), RISCV::X1)
258                   .addReg(RISCV::X2)
259                   .addImm(0)
260                   .getInstr();
261   EXPECT_FALSE(TII->describeLoadedValue(*MI1, RISCV::X2).has_value());
262   std::optional<ParamLoadedValue> MI1Res =
263       TII->describeLoadedValue(*MI1, RISCV::X1);
264   ASSERT_TRUE(MI1Res.has_value());
265   ASSERT_TRUE(MI1Res->first.isReg());
266   EXPECT_EQ(MI1Res->first.getReg(), RISCV::X2);
267   expectDIEPrintResult(MI1Res->second, "!DIExpression()");
268 
269   // Load immediate.
270   auto *MI2 = BuildMI(*MBB, MBB->begin(), DL, TII->get(RISCV::ADDI), RISCV::X3)
271                   .addReg(RISCV::X0)
272                   .addImm(111)
273                   .getInstr();
274   std::optional<ParamLoadedValue> MI2Res =
275       TII->describeLoadedValue(*MI2, RISCV::X3);
276   ASSERT_TRUE(MI2Res.has_value());
277   ASSERT_TRUE(MI2Res->first.isReg());
278   EXPECT_EQ(MI2Res->first.getReg(), RISCV::X0);
279   // TODO: Could be a DW_OP_constu if this is recognised as a immediate load
280   // rather than just an addi.
281   expectDIEPrintResult(MI2Res->second, "!DIExpression(DW_OP_plus_uconst, 111)");
282 
283   // Add immediate.
284   auto *MI3 = BuildMI(*MBB, MBB->begin(), DL, TII->get(RISCV::ADDI), RISCV::X2)
285                   .addReg(RISCV::X3)
286                   .addImm(222)
287                   .getInstr();
288   std::optional<ParamLoadedValue> MI3Res =
289       TII->describeLoadedValue(*MI3, RISCV::X2);
290   ASSERT_TRUE(MI3Res.has_value());
291   ASSERT_TRUE(MI3Res->first.isReg());
292   EXPECT_EQ(MI3Res->first.getReg(), RISCV::X3);
293   expectDIEPrintResult(MI3Res->second, "!DIExpression(DW_OP_plus_uconst, 222)");
294 
295   // Load value from memory.
296   // It would be better (more reflective of real-world describeLoadedValue
297   // usage) to test using MachinePointerInfo::getFixedStack, but
298   // unfortunately it would be overly fiddly to make this work.
299   auto MMO = MF->getMachineMemOperand(MachinePointerInfo::getConstantPool(*MF),
300                                       MachineMemOperand::MOLoad, 1, Align(1));
301   auto *MI4 = BuildMI(*MBB, MBB->begin(), DL, TII->get(RISCV::LB), RISCV::X1)
302                   .addReg(RISCV::X2)
303                   .addImm(-128)
304                   .addMemOperand(MMO)
305                   .getInstr();
306   std::optional<ParamLoadedValue> MI4Res =
307       TII->describeLoadedValue(*MI4, RISCV::X1);
308   ASSERT_TRUE(MI4Res.has_value());
309   ASSERT_TRUE(MI4Res->first.isReg());
310   EXPECT_EQ(MI4Res->first.getReg(), RISCV::X2);
311   expectDIEPrintResult(
312       MI4Res->second,
313       "!DIExpression(DW_OP_constu, 128, DW_OP_minus, DW_OP_deref_size, 1)");
314 
315   MF->deleteMachineBasicBlock(MBB);
316 }
317 
318 TEST_P(RISCVInstrInfoTest, GetDestEEW) {
319   const RISCVInstrInfo *TII = ST->getInstrInfo();
320   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VADD_VV), 3), 3u);
321   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VWADD_VV), 3), 4u);
322   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VLE32_V), 5), 5u);
323   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VLSE32_V), 5), 5u);
324   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VREDSUM_VS), 4), 4u);
325   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VWREDSUM_VS), 4), 5u);
326   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VFWREDOSUM_VS), 5), 6u);
327   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VFCVT_RTZ_XU_F_V), 4), 4u);
328   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VFWCVT_RTZ_XU_F_V), 4), 5u);
329   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VSLL_VI), 4), 4u);
330   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VWSLL_VI), 4), 5u);
331   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VMSEQ_VV), 4), 0u);
332   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VMAND_MM), 0), 0u);
333   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VIOTA_M), 3), 3u);
334   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VQMACCU_2x8x2), 3), 5u);
335   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::VFWMACC_4x4x4), 4), 5u);
336   EXPECT_EQ(RISCV::getDestLog2EEW(TII->get(RISCV::THVdotVMAQA_VV), 5), 5u);
337 }
338 
339 } // namespace
340 
341 INSTANTIATE_TEST_SUITE_P(RV32And64, RISCVInstrInfoTest,
342                          testing::Values("riscv32", "riscv64"));
343