1 | //===- MachineInstrTest.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 "llvm/CodeGen/MachineInstr.h" |
10 | #include "llvm/CodeGen/MachineBasicBlock.h" |
11 | #include "llvm/CodeGen/MachineFunction.h" |
12 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
13 | #include "llvm/CodeGen/MachineMemOperand.h" |
14 | #include "llvm/CodeGen/MachineModuleInfo.h" |
15 | #include "llvm/CodeGen/TargetFrameLowering.h" |
16 | #include "llvm/CodeGen/TargetInstrInfo.h" |
17 | #include "llvm/CodeGen/TargetLowering.h" |
18 | #include "llvm/CodeGen/TargetSubtargetInfo.h" |
19 | #include "llvm/IR/DebugInfoMetadata.h" |
20 | #include "llvm/IR/IRBuilder.h" |
21 | #include "llvm/IR/MemoryModelRelaxationAnnotations.h" |
22 | #include "llvm/IR/ModuleSlotTracker.h" |
23 | #include "llvm/MC/MCAsmInfo.h" |
24 | #include "llvm/MC/MCSymbol.h" |
25 | #include "llvm/MC/TargetRegistry.h" |
26 | #include "llvm/Support/TargetSelect.h" |
27 | #include "llvm/Target/TargetMachine.h" |
28 | #include "llvm/Target/TargetOptions.h" |
29 | #include "llvm/TargetParser/Triple.h" |
30 | #include "gmock/gmock.h" |
31 | #include "gtest/gtest.h" |
32 | |
33 | using namespace llvm; |
34 | |
35 | namespace { |
36 | // Include helper functions to ease the manipulation of MachineFunctions. |
37 | #include "MFCommon.inc" |
38 | |
39 | std::unique_ptr<MCContext> createMCContext(MCAsmInfo *AsmInfo) { |
40 | Triple TheTriple(/*ArchStr=*/"" , /*VendorStr=*/"" , /*OSStr=*/"" , |
41 | /*EnvironmentStr=*/"elf" ); |
42 | return std::make_unique<MCContext>(args&: TheTriple, args&: AsmInfo, args: nullptr, args: nullptr, |
43 | args: nullptr, args: nullptr, args: false); |
44 | } |
45 | |
46 | // This test makes sure that MachineInstr::isIdenticalTo handles Defs correctly |
47 | // for various combinations of IgnoreDefs, and also that it is symmetrical. |
48 | TEST(IsIdenticalToTest, DifferentDefs) { |
49 | LLVMContext Ctx; |
50 | Module Mod("Module" , Ctx); |
51 | auto MF = createMachineFunction(Ctx, M&: Mod); |
52 | |
53 | unsigned short NumOps = 2; |
54 | unsigned char NumDefs = 1; |
55 | struct { |
56 | MCInstrDesc MCID; |
57 | MCOperandInfo OpInfo[2]; |
58 | } Table = { |
59 | .MCID: {.Opcode: 0, .NumOperands: NumOps, .NumDefs: NumDefs, .Size: 0, .SchedClass: 0, .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: 1ULL << MCID::HasOptionalDef, .TSFlags: 0}, |
60 | .OpInfo: {{.RegClass: 0, .Flags: 0, .OperandType: MCOI::OPERAND_REGISTER, .Constraints: 0}, |
61 | {.RegClass: 0, .Flags: 1 << MCOI::OptionalDef, .OperandType: MCOI::OPERAND_REGISTER, .Constraints: 0}}}; |
62 | |
63 | // Create two MIs with different virtual reg defs and the same uses. |
64 | unsigned VirtualDef1 = -42; // The value doesn't matter, but the sign does. |
65 | unsigned VirtualDef2 = -43; |
66 | unsigned VirtualUse = -44; |
67 | |
68 | auto MI1 = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
69 | MI1->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: VirtualDef1, /*isDef*/ true)); |
70 | MI1->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: VirtualUse, /*isDef*/ false)); |
71 | |
72 | auto MI2 = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
73 | MI2->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: VirtualDef2, /*isDef*/ true)); |
74 | MI2->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: VirtualUse, /*isDef*/ false)); |
75 | |
76 | // Check that they are identical when we ignore virtual register defs, but not |
77 | // when we check defs. |
78 | ASSERT_FALSE(MI1->isIdenticalTo(*MI2, MachineInstr::CheckDefs)); |
79 | ASSERT_FALSE(MI2->isIdenticalTo(*MI1, MachineInstr::CheckDefs)); |
80 | |
81 | ASSERT_TRUE(MI1->isIdenticalTo(*MI2, MachineInstr::IgnoreVRegDefs)); |
82 | ASSERT_TRUE(MI2->isIdenticalTo(*MI1, MachineInstr::IgnoreVRegDefs)); |
83 | |
84 | // Create two MIs with different virtual reg defs, and a def or use of a |
85 | // sentinel register. |
86 | unsigned SentinelReg = 0; |
87 | |
88 | auto MI3 = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
89 | MI3->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: VirtualDef1, /*isDef*/ true)); |
90 | MI3->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: SentinelReg, /*isDef*/ true)); |
91 | |
92 | auto MI4 = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
93 | MI4->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: VirtualDef2, /*isDef*/ true)); |
94 | MI4->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: SentinelReg, /*isDef*/ false)); |
95 | |
96 | // Check that they are never identical. |
97 | ASSERT_FALSE(MI3->isIdenticalTo(*MI4, MachineInstr::CheckDefs)); |
98 | ASSERT_FALSE(MI4->isIdenticalTo(*MI3, MachineInstr::CheckDefs)); |
99 | |
100 | ASSERT_FALSE(MI3->isIdenticalTo(*MI4, MachineInstr::IgnoreVRegDefs)); |
101 | ASSERT_FALSE(MI4->isIdenticalTo(*MI3, MachineInstr::IgnoreVRegDefs)); |
102 | } |
103 | |
104 | // Check that MachineInstrExpressionTrait::isEqual is symmetric and in sync with |
105 | // MachineInstrExpressionTrait::getHashValue |
106 | void checkHashAndIsEqualMatch(MachineInstr *MI1, MachineInstr *MI2) { |
107 | bool IsEqual1 = MachineInstrExpressionTrait::isEqual(LHS: MI1, RHS: MI2); |
108 | bool IsEqual2 = MachineInstrExpressionTrait::isEqual(LHS: MI2, RHS: MI1); |
109 | |
110 | ASSERT_EQ(IsEqual1, IsEqual2); |
111 | |
112 | auto Hash1 = MachineInstrExpressionTrait::getHashValue(MI: MI1); |
113 | auto Hash2 = MachineInstrExpressionTrait::getHashValue(MI: MI2); |
114 | |
115 | ASSERT_EQ(IsEqual1, Hash1 == Hash2); |
116 | } |
117 | |
118 | // This test makes sure that MachineInstrExpressionTraits::isEqual is in sync |
119 | // with MachineInstrExpressionTraits::getHashValue. |
120 | TEST(MachineInstrExpressionTraitTest, IsEqualAgreesWithGetHashValue) { |
121 | LLVMContext Ctx; |
122 | Module Mod("Module" , Ctx); |
123 | auto MF = createMachineFunction(Ctx, M&: Mod); |
124 | |
125 | unsigned short NumOps = 2; |
126 | unsigned char NumDefs = 1; |
127 | struct { |
128 | MCInstrDesc MCID; |
129 | MCOperandInfo OpInfo[2]; |
130 | } Table = { |
131 | .MCID: {.Opcode: 0, .NumOperands: NumOps, .NumDefs: NumDefs, .Size: 0, .SchedClass: 0, .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: 1ULL << MCID::HasOptionalDef, .TSFlags: 0}, |
132 | .OpInfo: {{.RegClass: 0, .Flags: 0, .OperandType: MCOI::OPERAND_REGISTER, .Constraints: 0}, |
133 | {.RegClass: 0, .Flags: 1 << MCOI::OptionalDef, .OperandType: MCOI::OPERAND_REGISTER, .Constraints: 0}}}; |
134 | |
135 | // Define a series of instructions with different kinds of operands and make |
136 | // sure that the hash function is consistent with isEqual for various |
137 | // combinations of them. |
138 | unsigned VirtualDef1 = -42; |
139 | unsigned VirtualDef2 = -43; |
140 | unsigned VirtualReg = -44; |
141 | unsigned SentinelReg = 0; |
142 | unsigned PhysicalReg = 45; |
143 | |
144 | auto VD1VU = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
145 | VD1VU->addOperand(MF&: *MF, |
146 | Op: MachineOperand::CreateReg(Reg: VirtualDef1, /*isDef*/ true)); |
147 | VD1VU->addOperand(MF&: *MF, |
148 | Op: MachineOperand::CreateReg(Reg: VirtualReg, /*isDef*/ false)); |
149 | |
150 | auto VD2VU = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
151 | VD2VU->addOperand(MF&: *MF, |
152 | Op: MachineOperand::CreateReg(Reg: VirtualDef2, /*isDef*/ true)); |
153 | VD2VU->addOperand(MF&: *MF, |
154 | Op: MachineOperand::CreateReg(Reg: VirtualReg, /*isDef*/ false)); |
155 | |
156 | auto VD1SU = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
157 | VD1SU->addOperand(MF&: *MF, |
158 | Op: MachineOperand::CreateReg(Reg: VirtualDef1, /*isDef*/ true)); |
159 | VD1SU->addOperand(MF&: *MF, |
160 | Op: MachineOperand::CreateReg(Reg: SentinelReg, /*isDef*/ false)); |
161 | |
162 | auto VD1SD = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
163 | VD1SD->addOperand(MF&: *MF, |
164 | Op: MachineOperand::CreateReg(Reg: VirtualDef1, /*isDef*/ true)); |
165 | VD1SD->addOperand(MF&: *MF, |
166 | Op: MachineOperand::CreateReg(Reg: SentinelReg, /*isDef*/ true)); |
167 | |
168 | auto VD2PU = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
169 | VD2PU->addOperand(MF&: *MF, |
170 | Op: MachineOperand::CreateReg(Reg: VirtualDef2, /*isDef*/ true)); |
171 | VD2PU->addOperand(MF&: *MF, |
172 | Op: MachineOperand::CreateReg(Reg: PhysicalReg, /*isDef*/ false)); |
173 | |
174 | auto VD2PD = MF->CreateMachineInstr(MCID: Table.MCID, DL: DebugLoc()); |
175 | VD2PD->addOperand(MF&: *MF, |
176 | Op: MachineOperand::CreateReg(Reg: VirtualDef2, /*isDef*/ true)); |
177 | VD2PD->addOperand(MF&: *MF, |
178 | Op: MachineOperand::CreateReg(Reg: PhysicalReg, /*isDef*/ true)); |
179 | |
180 | checkHashAndIsEqualMatch(MI1: VD1VU, MI2: VD2VU); |
181 | checkHashAndIsEqualMatch(MI1: VD1VU, MI2: VD1SU); |
182 | checkHashAndIsEqualMatch(MI1: VD1VU, MI2: VD1SD); |
183 | checkHashAndIsEqualMatch(MI1: VD1VU, MI2: VD2PU); |
184 | checkHashAndIsEqualMatch(MI1: VD1VU, MI2: VD2PD); |
185 | |
186 | checkHashAndIsEqualMatch(MI1: VD2VU, MI2: VD1SU); |
187 | checkHashAndIsEqualMatch(MI1: VD2VU, MI2: VD1SD); |
188 | checkHashAndIsEqualMatch(MI1: VD2VU, MI2: VD2PU); |
189 | checkHashAndIsEqualMatch(MI1: VD2VU, MI2: VD2PD); |
190 | |
191 | checkHashAndIsEqualMatch(MI1: VD1SU, MI2: VD1SD); |
192 | checkHashAndIsEqualMatch(MI1: VD1SU, MI2: VD2PU); |
193 | checkHashAndIsEqualMatch(MI1: VD1SU, MI2: VD2PD); |
194 | |
195 | checkHashAndIsEqualMatch(MI1: VD1SD, MI2: VD2PU); |
196 | checkHashAndIsEqualMatch(MI1: VD1SD, MI2: VD2PD); |
197 | |
198 | checkHashAndIsEqualMatch(MI1: VD2PU, MI2: VD2PD); |
199 | } |
200 | |
201 | TEST(MachineInstrPrintingTest, DebugLocPrinting) { |
202 | LLVMContext Ctx; |
203 | Module Mod("Module" , Ctx); |
204 | auto MF = createMachineFunction(Ctx, M&: Mod); |
205 | |
206 | struct { |
207 | MCInstrDesc MCID; |
208 | MCOperandInfo OpInfo; |
209 | } Table = {.MCID: {.Opcode: 0, .NumOperands: 1, .NumDefs: 1, .Size: 0, .SchedClass: 0, .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: 0, .TSFlags: 0}, |
210 | .OpInfo: {.RegClass: 0, .Flags: 0, .OperandType: MCOI::OPERAND_REGISTER, .Constraints: 0}}; |
211 | |
212 | DIFile *DIF = DIFile::getDistinct(Context&: Ctx, Filename: "filename" , Directory: "" ); |
213 | DISubprogram *DIS = DISubprogram::getDistinct( |
214 | Context&: Ctx, Scope: nullptr, Name: "" , LinkageName: "" , File: DIF, Line: 0, Type: nullptr, ScopeLine: 0, ContainingType: nullptr, VirtualIndex: 0, ThisAdjustment: 0, Flags: DINode::FlagZero, |
215 | SPFlags: DISubprogram::SPFlagZero, Unit: nullptr); |
216 | DILocation *DIL = DILocation::get(Context&: Ctx, Line: 1, Column: 5, Scope: DIS); |
217 | DebugLoc DL(DIL); |
218 | MachineInstr *MI = MF->CreateMachineInstr(MCID: Table.MCID, DL); |
219 | MI->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: 0, /*isDef*/ true)); |
220 | |
221 | std::string str; |
222 | raw_string_ostream OS(str); |
223 | MI->print(OS, /*IsStandalone*/true, /*SkipOpers*/false, /*SkipDebugLoc*/false, |
224 | /*AddNewLine*/false); |
225 | ASSERT_TRUE( |
226 | StringRef(OS.str()).starts_with("$noreg = UNKNOWN debug-location " )); |
227 | ASSERT_TRUE(StringRef(OS.str()).ends_with("filename:1:5" )); |
228 | } |
229 | |
230 | TEST(MachineInstrSpan, DistanceBegin) { |
231 | LLVMContext Ctx; |
232 | Module Mod("Module" , Ctx); |
233 | auto MF = createMachineFunction(Ctx, M&: Mod); |
234 | auto MBB = MF->CreateMachineBasicBlock(); |
235 | |
236 | MCInstrDesc MCID = {.Opcode: 0, .NumOperands: 0, .NumDefs: 0, .Size: 0, .SchedClass: 0, .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: 0, .TSFlags: 0}; |
237 | |
238 | auto MII = MBB->begin(); |
239 | MachineInstrSpan MIS(MII, MBB); |
240 | ASSERT_TRUE(MIS.empty()); |
241 | |
242 | auto MI = MF->CreateMachineInstr(MCID, DL: DebugLoc()); |
243 | MBB->insert(I: MII, MI); |
244 | ASSERT_TRUE(std::distance(MIS.begin(), MII) == 1); |
245 | } |
246 | |
247 | TEST(MachineInstrSpan, DistanceEnd) { |
248 | LLVMContext Ctx; |
249 | Module Mod("Module" , Ctx); |
250 | auto MF = createMachineFunction(Ctx, M&: Mod); |
251 | auto MBB = MF->CreateMachineBasicBlock(); |
252 | |
253 | MCInstrDesc MCID = {.Opcode: 0, .NumOperands: 0, .NumDefs: 0, .Size: 0, .SchedClass: 0, .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: 0, .TSFlags: 0}; |
254 | |
255 | auto MII = MBB->end(); |
256 | MachineInstrSpan MIS(MII, MBB); |
257 | ASSERT_TRUE(MIS.empty()); |
258 | |
259 | auto MI = MF->CreateMachineInstr(MCID, DL: DebugLoc()); |
260 | MBB->insert(I: MII, MI); |
261 | ASSERT_TRUE(std::distance(MIS.begin(), MII) == 1); |
262 | } |
263 | |
264 | TEST(MachineInstrExtraInfo, AddExtraInfo) { |
265 | LLVMContext Ctx; |
266 | Module Mod("Module" , Ctx); |
267 | auto MF = createMachineFunction(Ctx, M&: Mod); |
268 | MCInstrDesc MCID = {.Opcode: 0, .NumOperands: 0, .NumDefs: 0, .Size: 0, .SchedClass: 0, .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: 0, .TSFlags: 0}; |
269 | |
270 | auto MI = MF->CreateMachineInstr(MCID, DL: DebugLoc()); |
271 | auto MAI = MCAsmInfo(); |
272 | auto MC = createMCContext(AsmInfo: &MAI); |
273 | auto MMO = MF->getMachineMemOperand(PtrInfo: MachinePointerInfo(), |
274 | F: MachineMemOperand::MOLoad, Size: 8, BaseAlignment: Align(8)); |
275 | SmallVector<MachineMemOperand *, 2> MMOs; |
276 | MMOs.push_back(Elt: MMO); |
277 | MCSymbol *Sym1 = MC->createTempSymbol(Name: "pre_label" , AlwaysAddSuffix: false); |
278 | MCSymbol *Sym2 = MC->createTempSymbol(Name: "post_label" , AlwaysAddSuffix: false); |
279 | MDNode *HAM = MDNode::getDistinct(Context&: Ctx, MDs: std::nullopt); |
280 | MDNode *PCS = MDNode::getDistinct(Context&: Ctx, MDs: std::nullopt); |
281 | MDNode *MMRA = MMRAMetadata::getTagMD(Ctx, Prefix: "foo" , Suffix: "bar" ); |
282 | |
283 | ASSERT_TRUE(MI->memoperands_empty()); |
284 | ASSERT_FALSE(MI->getPreInstrSymbol()); |
285 | ASSERT_FALSE(MI->getPostInstrSymbol()); |
286 | ASSERT_FALSE(MI->getHeapAllocMarker()); |
287 | ASSERT_FALSE(MI->getPCSections()); |
288 | ASSERT_FALSE(MI->getMMRAMetadata()); |
289 | |
290 | MI->setMemRefs(MF&: *MF, MemRefs: MMOs); |
291 | ASSERT_TRUE(MI->memoperands().size() == 1); |
292 | ASSERT_FALSE(MI->getPreInstrSymbol()); |
293 | ASSERT_FALSE(MI->getPostInstrSymbol()); |
294 | ASSERT_FALSE(MI->getHeapAllocMarker()); |
295 | ASSERT_FALSE(MI->getPCSections()); |
296 | ASSERT_FALSE(MI->getMMRAMetadata()); |
297 | |
298 | MI->setPreInstrSymbol(MF&: *MF, Symbol: Sym1); |
299 | ASSERT_TRUE(MI->memoperands().size() == 1); |
300 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
301 | ASSERT_FALSE(MI->getPostInstrSymbol()); |
302 | ASSERT_FALSE(MI->getHeapAllocMarker()); |
303 | ASSERT_FALSE(MI->getPCSections()); |
304 | ASSERT_FALSE(MI->getMMRAMetadata()); |
305 | |
306 | MI->setPostInstrSymbol(MF&: *MF, Symbol: Sym2); |
307 | ASSERT_TRUE(MI->memoperands().size() == 1); |
308 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
309 | ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2); |
310 | ASSERT_FALSE(MI->getHeapAllocMarker()); |
311 | ASSERT_FALSE(MI->getPCSections()); |
312 | ASSERT_FALSE(MI->getMMRAMetadata()); |
313 | |
314 | MI->setHeapAllocMarker(MF&: *MF, MD: HAM); |
315 | ASSERT_TRUE(MI->memoperands().size() == 1); |
316 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
317 | ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2); |
318 | ASSERT_TRUE(MI->getHeapAllocMarker() == HAM); |
319 | ASSERT_FALSE(MI->getPCSections()); |
320 | ASSERT_FALSE(MI->getMMRAMetadata()); |
321 | |
322 | MI->setPCSections(MF&: *MF, MD: PCS); |
323 | ASSERT_TRUE(MI->memoperands().size() == 1); |
324 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
325 | ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2); |
326 | ASSERT_TRUE(MI->getHeapAllocMarker() == HAM); |
327 | ASSERT_TRUE(MI->getPCSections() == PCS); |
328 | ASSERT_FALSE(MI->getMMRAMetadata()); |
329 | |
330 | MI->setMMRAMetadata(MF&: *MF, MMRAs: MMRA); |
331 | ASSERT_TRUE(MI->memoperands().size() == 1); |
332 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
333 | ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2); |
334 | ASSERT_TRUE(MI->getHeapAllocMarker() == HAM); |
335 | ASSERT_TRUE(MI->getPCSections() == PCS); |
336 | ASSERT_TRUE(MI->getMMRAMetadata() == MMRA); |
337 | |
338 | // Check with nothing but MMRAs. |
339 | MachineInstr *MMRAMI = MF->CreateMachineInstr(MCID, DL: DebugLoc()); |
340 | ASSERT_FALSE(MMRAMI->getMMRAMetadata()); |
341 | MMRAMI->setMMRAMetadata(MF&: *MF, MMRAs: MMRA); |
342 | ASSERT_TRUE(MMRAMI->getMMRAMetadata() == MMRA); |
343 | } |
344 | |
345 | TEST(MachineInstrExtraInfo, ChangeExtraInfo) { |
346 | LLVMContext Ctx; |
347 | Module Mod("Module" , Ctx); |
348 | auto MF = createMachineFunction(Ctx, M&: Mod); |
349 | MCInstrDesc MCID = {.Opcode: 0, .NumOperands: 0, .NumDefs: 0, .Size: 0, .SchedClass: 0, .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: 0, .TSFlags: 0}; |
350 | |
351 | auto MI = MF->CreateMachineInstr(MCID, DL: DebugLoc()); |
352 | auto MAI = MCAsmInfo(); |
353 | auto MC = createMCContext(AsmInfo: &MAI); |
354 | auto MMO = MF->getMachineMemOperand(PtrInfo: MachinePointerInfo(), |
355 | F: MachineMemOperand::MOLoad, Size: 8, BaseAlignment: Align(8)); |
356 | SmallVector<MachineMemOperand *, 2> MMOs; |
357 | MMOs.push_back(Elt: MMO); |
358 | MCSymbol *Sym1 = MC->createTempSymbol(Name: "pre_label" , AlwaysAddSuffix: false); |
359 | MCSymbol *Sym2 = MC->createTempSymbol(Name: "post_label" , AlwaysAddSuffix: false); |
360 | MDNode *HAM = MDNode::getDistinct(Context&: Ctx, MDs: std::nullopt); |
361 | MDNode *PCS = MDNode::getDistinct(Context&: Ctx, MDs: std::nullopt); |
362 | |
363 | MDNode *MMRA1 = MMRAMetadata::getTagMD(Ctx, Prefix: "foo" , Suffix: "bar" ); |
364 | MDNode *MMRA2 = MMRAMetadata::getTagMD(Ctx, Prefix: "bar" , Suffix: "bux" ); |
365 | |
366 | MI->setMemRefs(MF&: *MF, MemRefs: MMOs); |
367 | MI->setPreInstrSymbol(MF&: *MF, Symbol: Sym1); |
368 | MI->setPostInstrSymbol(MF&: *MF, Symbol: Sym2); |
369 | MI->setHeapAllocMarker(MF&: *MF, MD: HAM); |
370 | MI->setPCSections(MF&: *MF, MD: PCS); |
371 | MI->setMMRAMetadata(MF&: *MF, MMRAs: MMRA1); |
372 | |
373 | MMOs.push_back(Elt: MMO); |
374 | |
375 | MI->setMemRefs(MF&: *MF, MemRefs: MMOs); |
376 | ASSERT_TRUE(MI->memoperands().size() == 2); |
377 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
378 | ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2); |
379 | ASSERT_TRUE(MI->getHeapAllocMarker() == HAM); |
380 | ASSERT_TRUE(MI->getPCSections() == PCS); |
381 | ASSERT_TRUE(MI->getMMRAMetadata() == MMRA1); |
382 | |
383 | MI->setPostInstrSymbol(MF&: *MF, Symbol: Sym1); |
384 | ASSERT_TRUE(MI->memoperands().size() == 2); |
385 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
386 | ASSERT_TRUE(MI->getPostInstrSymbol() == Sym1); |
387 | ASSERT_TRUE(MI->getHeapAllocMarker() == HAM); |
388 | ASSERT_TRUE(MI->getPCSections() == PCS); |
389 | ASSERT_TRUE(MI->getMMRAMetadata() == MMRA1); |
390 | |
391 | MI->setMMRAMetadata(MF&: *MF, MMRAs: MMRA2); |
392 | ASSERT_TRUE(MI->memoperands().size() == 2); |
393 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
394 | ASSERT_TRUE(MI->getPostInstrSymbol() == Sym1); |
395 | ASSERT_TRUE(MI->getHeapAllocMarker() == HAM); |
396 | ASSERT_TRUE(MI->getPCSections() == PCS); |
397 | ASSERT_TRUE(MI->getMMRAMetadata() == MMRA2); |
398 | } |
399 | |
400 | TEST(MachineInstrExtraInfo, RemoveExtraInfo) { |
401 | LLVMContext Ctx; |
402 | Module Mod("Module" , Ctx); |
403 | auto MF = createMachineFunction(Ctx, M&: Mod); |
404 | MCInstrDesc MCID = {.Opcode: 0, .NumOperands: 0, .NumDefs: 0, .Size: 0, .SchedClass: 0, .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: 0, .TSFlags: 0}; |
405 | |
406 | auto MI = MF->CreateMachineInstr(MCID, DL: DebugLoc()); |
407 | auto MAI = MCAsmInfo(); |
408 | auto MC = createMCContext(AsmInfo: &MAI); |
409 | auto MMO = MF->getMachineMemOperand(PtrInfo: MachinePointerInfo(), |
410 | F: MachineMemOperand::MOLoad, Size: 8, BaseAlignment: Align(8)); |
411 | SmallVector<MachineMemOperand *, 2> MMOs; |
412 | MMOs.push_back(Elt: MMO); |
413 | MMOs.push_back(Elt: MMO); |
414 | MCSymbol *Sym1 = MC->createTempSymbol(Name: "pre_label" , AlwaysAddSuffix: false); |
415 | MCSymbol *Sym2 = MC->createTempSymbol(Name: "post_label" , AlwaysAddSuffix: false); |
416 | MDNode *HAM = MDNode::getDistinct(Context&: Ctx, MDs: std::nullopt); |
417 | MDNode *PCS = MDNode::getDistinct(Context&: Ctx, MDs: std::nullopt); |
418 | |
419 | MDNode *MMRA = MDTuple::get(Context&: Ctx, MDs: {}); |
420 | |
421 | MI->setMemRefs(MF&: *MF, MemRefs: MMOs); |
422 | MI->setPreInstrSymbol(MF&: *MF, Symbol: Sym1); |
423 | MI->setPostInstrSymbol(MF&: *MF, Symbol: Sym2); |
424 | MI->setHeapAllocMarker(MF&: *MF, MD: HAM); |
425 | MI->setPCSections(MF&: *MF, MD: PCS); |
426 | MI->setMMRAMetadata(MF&: *MF, MMRAs: MMRA); |
427 | |
428 | MI->setPostInstrSymbol(MF&: *MF, Symbol: nullptr); |
429 | ASSERT_TRUE(MI->memoperands().size() == 2); |
430 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
431 | ASSERT_FALSE(MI->getPostInstrSymbol()); |
432 | ASSERT_TRUE(MI->getHeapAllocMarker() == HAM); |
433 | ASSERT_TRUE(MI->getPCSections() == PCS); |
434 | ASSERT_TRUE(MI->getMMRAMetadata() == MMRA); |
435 | |
436 | MI->setHeapAllocMarker(MF&: *MF, MD: nullptr); |
437 | ASSERT_TRUE(MI->memoperands().size() == 2); |
438 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
439 | ASSERT_FALSE(MI->getPostInstrSymbol()); |
440 | ASSERT_FALSE(MI->getHeapAllocMarker()); |
441 | ASSERT_TRUE(MI->getPCSections() == PCS); |
442 | ASSERT_TRUE(MI->getMMRAMetadata() == MMRA); |
443 | |
444 | MI->setPCSections(MF&: *MF, MD: nullptr); |
445 | ASSERT_TRUE(MI->memoperands().size() == 2); |
446 | ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1); |
447 | ASSERT_FALSE(MI->getPostInstrSymbol()); |
448 | ASSERT_FALSE(MI->getHeapAllocMarker()); |
449 | ASSERT_FALSE(MI->getPCSections()); |
450 | ASSERT_TRUE(MI->getMMRAMetadata() == MMRA); |
451 | |
452 | MI->setPreInstrSymbol(MF&: *MF, Symbol: nullptr); |
453 | ASSERT_TRUE(MI->memoperands().size() == 2); |
454 | ASSERT_FALSE(MI->getPreInstrSymbol()); |
455 | ASSERT_FALSE(MI->getPostInstrSymbol()); |
456 | ASSERT_FALSE(MI->getHeapAllocMarker()); |
457 | ASSERT_FALSE(MI->getPCSections()); |
458 | ASSERT_TRUE(MI->getMMRAMetadata() == MMRA); |
459 | |
460 | MI->setMemRefs(MF&: *MF, MemRefs: {}); |
461 | ASSERT_TRUE(MI->memoperands_empty()); |
462 | ASSERT_FALSE(MI->getPreInstrSymbol()); |
463 | ASSERT_FALSE(MI->getPostInstrSymbol()); |
464 | ASSERT_FALSE(MI->getHeapAllocMarker()); |
465 | ASSERT_FALSE(MI->getPCSections()); |
466 | ASSERT_TRUE(MI->getMMRAMetadata() == MMRA); |
467 | |
468 | MI->setMMRAMetadata(MF&: *MF, MMRAs: nullptr); |
469 | ASSERT_TRUE(MI->memoperands_empty()); |
470 | ASSERT_FALSE(MI->getPreInstrSymbol()); |
471 | ASSERT_FALSE(MI->getPostInstrSymbol()); |
472 | ASSERT_FALSE(MI->getHeapAllocMarker()); |
473 | ASSERT_FALSE(MI->getPCSections()); |
474 | ASSERT_FALSE(MI->getMMRAMetadata()); |
475 | } |
476 | |
477 | TEST(MachineInstrDebugValue, AddDebugValueOperand) { |
478 | LLVMContext Ctx; |
479 | Module Mod("Module" , Ctx); |
480 | auto MF = createMachineFunction(Ctx, M&: Mod); |
481 | |
482 | for (const unsigned short Opcode : |
483 | {TargetOpcode::DBG_VALUE, TargetOpcode::DBG_VALUE_LIST, |
484 | TargetOpcode::DBG_INSTR_REF, TargetOpcode::DBG_PHI, |
485 | TargetOpcode::DBG_LABEL}) { |
486 | const MCInstrDesc MCID = { |
487 | .Opcode: Opcode, .NumOperands: 0, .NumDefs: 0, .Size: 0, .SchedClass: 0, |
488 | .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: (1ULL << MCID::Pseudo) | (1ULL << MCID::Variadic), |
489 | .TSFlags: 0}; |
490 | |
491 | auto *MI = MF->CreateMachineInstr(MCID, DL: DebugLoc()); |
492 | MI->addOperand(MF&: *MF, Op: MachineOperand::CreateReg(Reg: 0, /*isDef*/ false)); |
493 | |
494 | MI->addOperand(MF&: *MF, Op: MachineOperand::CreateImm(Val: 0)); |
495 | MI->getOperand(i: 1).ChangeToRegister(Reg: 0, isDef: false); |
496 | |
497 | ASSERT_TRUE(MI->getOperand(0).isDebug()); |
498 | ASSERT_TRUE(MI->getOperand(1).isDebug()); |
499 | } |
500 | } |
501 | |
502 | MATCHER_P(HasMIMetadata, MIMD, "" ) { |
503 | return arg->getDebugLoc() == MIMD.getDL() && |
504 | arg->getPCSections() == MIMD.getPCSections(); |
505 | } |
506 | |
507 | TEST(MachineInstrBuilder, BuildMI) { |
508 | LLVMContext Ctx; |
509 | MDNode *PCS = MDNode::getDistinct(Context&: Ctx, MDs: std::nullopt); |
510 | MDNode *DI = MDNode::getDistinct(Context&: Ctx, MDs: std::nullopt); |
511 | DebugLoc DL(DI); |
512 | MIMetadata MIMD(DL, PCS); |
513 | EXPECT_EQ(MIMD.getDL(), DL); |
514 | EXPECT_EQ(MIMD.getPCSections(), PCS); |
515 | // Check common BuildMI() overloads propagate MIMetadata. |
516 | Module Mod("Module" , Ctx); |
517 | auto MF = createMachineFunction(Ctx, M&: Mod); |
518 | auto MBB = MF->CreateMachineBasicBlock(); |
519 | MCInstrDesc MCID = {.Opcode: 0, .NumOperands: 0, .NumDefs: 0, .Size: 0, .SchedClass: 0, .NumImplicitUses: 0, .NumImplicitDefs: 0, .ImplicitOffset: 0, .OpInfoOffset: 0, .Flags: 0, .TSFlags: 0}; |
520 | EXPECT_THAT(BuildMI(*MF, MIMD, MCID), HasMIMetadata(MIMD)); |
521 | EXPECT_THAT(BuildMI(*MF, MIMD, MCID), HasMIMetadata(MIMD)); |
522 | EXPECT_THAT(BuildMI(*MBB, MBB->end(), MIMD, MCID), HasMIMetadata(MIMD)); |
523 | EXPECT_THAT(BuildMI(*MBB, MBB->end(), MIMD, MCID), HasMIMetadata(MIMD)); |
524 | EXPECT_THAT(BuildMI(*MBB, MBB->instr_end(), MIMD, MCID), HasMIMetadata(MIMD)); |
525 | EXPECT_THAT(BuildMI(*MBB, *MBB->begin(), MIMD, MCID), HasMIMetadata(MIMD)); |
526 | EXPECT_THAT(BuildMI(*MBB, &*MBB->begin(), MIMD, MCID), HasMIMetadata(MIMD)); |
527 | EXPECT_THAT(BuildMI(MBB, MIMD, MCID), HasMIMetadata(MIMD)); |
528 | } |
529 | |
530 | static_assert(std::is_trivially_copyable_v<MCOperand>, "trivially copyable" ); |
531 | |
532 | TEST(MachineInstrTest, SpliceOperands) { |
533 | LLVMContext Ctx; |
534 | Module Mod("Module" , Ctx); |
535 | std::unique_ptr<MachineFunction> MF = createMachineFunction(Ctx, M&: Mod); |
536 | MachineBasicBlock *MBB = MF->CreateMachineBasicBlock(); |
537 | MCInstrDesc MCID = {.Opcode: TargetOpcode::INLINEASM, |
538 | .NumOperands: 0, |
539 | .NumDefs: 0, |
540 | .Size: 0, |
541 | .SchedClass: 0, |
542 | .NumImplicitUses: 0, |
543 | .NumImplicitDefs: 0, |
544 | .ImplicitOffset: 0, |
545 | .OpInfoOffset: 0, |
546 | .Flags: (1ULL << MCID::Pseudo) | (1ULL << MCID::Variadic), |
547 | .TSFlags: 0}; |
548 | MachineInstr *MI = MF->CreateMachineInstr(MCID, DL: DebugLoc()); |
549 | MBB->insert(I: MBB->begin(), MI); |
550 | MI->addOperand(Op: MachineOperand::CreateImm(Val: 0)); |
551 | MI->addOperand(Op: MachineOperand::CreateImm(Val: 1)); |
552 | MI->addOperand(Op: MachineOperand::CreateImm(Val: 2)); |
553 | MI->addOperand(Op: MachineOperand::CreateImm(Val: 3)); |
554 | MI->addOperand(Op: MachineOperand::CreateImm(Val: 4)); |
555 | |
556 | MI->removeOperand(OpNo: 1); |
557 | EXPECT_EQ(MI->getOperand(1).getImm(), MachineOperand::CreateImm(2).getImm()); |
558 | EXPECT_EQ(MI->getNumOperands(), 4U); |
559 | |
560 | MachineOperand Ops[] = { |
561 | MachineOperand::CreateImm(Val: 42), MachineOperand::CreateImm(Val: 1024), |
562 | MachineOperand::CreateImm(Val: 2048), MachineOperand::CreateImm(Val: 4096), |
563 | MachineOperand::CreateImm(Val: 8192), |
564 | }; |
565 | auto *It = MI->operands_begin(); |
566 | ++It; |
567 | MI->insert(InsertBefore: It, Ops); |
568 | |
569 | EXPECT_EQ(MI->getNumOperands(), 9U); |
570 | EXPECT_EQ(MI->getOperand(0).getImm(), MachineOperand::CreateImm(0).getImm()); |
571 | EXPECT_EQ(MI->getOperand(1).getImm(), MachineOperand::CreateImm(42).getImm()); |
572 | EXPECT_EQ(MI->getOperand(2).getImm(), |
573 | MachineOperand::CreateImm(1024).getImm()); |
574 | EXPECT_EQ(MI->getOperand(3).getImm(), |
575 | MachineOperand::CreateImm(2048).getImm()); |
576 | EXPECT_EQ(MI->getOperand(4).getImm(), |
577 | MachineOperand::CreateImm(4096).getImm()); |
578 | EXPECT_EQ(MI->getOperand(5).getImm(), |
579 | MachineOperand::CreateImm(8192).getImm()); |
580 | EXPECT_EQ(MI->getOperand(6).getImm(), MachineOperand::CreateImm(2).getImm()); |
581 | EXPECT_EQ(MI->getOperand(7).getImm(), MachineOperand::CreateImm(3).getImm()); |
582 | EXPECT_EQ(MI->getOperand(8).getImm(), MachineOperand::CreateImm(4).getImm()); |
583 | |
584 | // test tied operands |
585 | MCRegisterClass MRC{ |
586 | .RegsBegin: 0, .RegSet: 0, .NameIdx: 0, .RegsSize: 0, .RegSetSize: 0, .ID: 0, .RegSizeInBits: 0, .CopyCost: 0, /*Allocatable=*/true, /*BaseClass=*/true}; |
587 | TargetRegisterClass RC{.MC: &MRC, .SubClassMask: 0, .SuperRegIndices: 0, .LaneMask: {}, .AllocationPriority: 0, .GlobalPriority: 0, .TSFlags: 0, .HasDisjunctSubRegs: 0, .CoveredBySubRegs: 0, .SuperClasses: 0, .OrderFunc: 0}; |
588 | // MachineRegisterInfo will be very upset if these registers aren't |
589 | // allocatable. |
590 | assert(RC.isAllocatable() && "unusable TargetRegisterClass" ); |
591 | MachineRegisterInfo &MRI = MF->getRegInfo(); |
592 | Register A = MRI.createVirtualRegister(RegClass: &RC); |
593 | Register B = MRI.createVirtualRegister(RegClass: &RC); |
594 | MI->getOperand(i: 0).ChangeToRegister(Reg: A, /*isDef=*/true); |
595 | MI->getOperand(i: 1).ChangeToRegister(Reg: B, /*isDef=*/false); |
596 | MI->tieOperands(DefIdx: 0, UseIdx: 1); |
597 | EXPECT_TRUE(MI->getOperand(0).isTied()); |
598 | EXPECT_TRUE(MI->getOperand(1).isTied()); |
599 | EXPECT_EQ(MI->findTiedOperandIdx(0), 1U); |
600 | EXPECT_EQ(MI->findTiedOperandIdx(1), 0U); |
601 | MI->insert(InsertBefore: &MI->getOperand(i: 1), Ops: {MachineOperand::CreateImm(Val: 7)}); |
602 | EXPECT_TRUE(MI->getOperand(0).isTied()); |
603 | EXPECT_TRUE(MI->getOperand(1).isImm()); |
604 | EXPECT_TRUE(MI->getOperand(2).isTied()); |
605 | EXPECT_EQ(MI->findTiedOperandIdx(0), 2U); |
606 | EXPECT_EQ(MI->findTiedOperandIdx(2), 0U); |
607 | EXPECT_EQ(MI->getOperand(0).getReg(), A); |
608 | EXPECT_EQ(MI->getOperand(2).getReg(), B); |
609 | |
610 | // bad inputs |
611 | EXPECT_EQ(MI->getNumOperands(), 10U); |
612 | MI->insert(InsertBefore: MI->operands_begin(), Ops: {}); |
613 | EXPECT_EQ(MI->getNumOperands(), 10U); |
614 | } |
615 | |
616 | } // end namespace |
617 | |