1 | //===- bolt/unittest/Core/MCPlusBuilder.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 | #ifdef AARCH64_AVAILABLE |
10 | #include "AArch64Subtarget.h" |
11 | #include "MCTargetDesc/AArch64MCTargetDesc.h" |
12 | #endif // AARCH64_AVAILABLE |
13 | |
14 | #ifdef X86_AVAILABLE |
15 | #include "X86Subtarget.h" |
16 | #endif // X86_AVAILABLE |
17 | |
18 | #include "bolt/Core/BinaryBasicBlock.h" |
19 | #include "bolt/Core/BinaryFunction.h" |
20 | #include "bolt/Rewrite/RewriteInstance.h" |
21 | #include "llvm/BinaryFormat/ELF.h" |
22 | #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
23 | #include "llvm/MC/MCInstBuilder.h" |
24 | #include "llvm/Support/TargetSelect.h" |
25 | #include "gtest/gtest.h" |
26 | |
27 | using namespace llvm; |
28 | using namespace llvm::object; |
29 | using namespace llvm::ELF; |
30 | using namespace bolt; |
31 | |
32 | namespace { |
33 | struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> { |
34 | void SetUp() override { |
35 | initalizeLLVM(); |
36 | prepareElf(); |
37 | initializeBolt(); |
38 | } |
39 | |
40 | protected: |
41 | void initalizeLLVM() { |
42 | #define BOLT_TARGET(target) \ |
43 | LLVMInitialize##target##TargetInfo(); \ |
44 | LLVMInitialize##target##TargetMC(); \ |
45 | LLVMInitialize##target##AsmParser(); \ |
46 | LLVMInitialize##target##Disassembler(); \ |
47 | LLVMInitialize##target##Target(); \ |
48 | LLVMInitialize##target##AsmPrinter(); |
49 | |
50 | #include "bolt/Core/TargetConfig.def" |
51 | } |
52 | |
53 | void prepareElf() { |
54 | memcpy(dest: ElfBuf, src: "\177ELF" , n: 4); |
55 | ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf); |
56 | EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64; |
57 | EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB; |
58 | EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64; |
59 | MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF" ); |
60 | ObjFile = cantFail(ValOrErr: ObjectFile::createObjectFile(Object: Source)); |
61 | } |
62 | |
63 | void initializeBolt() { |
64 | Relocation::Arch = ObjFile->makeTriple().getArch(); |
65 | BC = cantFail(ValOrErr: BinaryContext::createBinaryContext( |
66 | TheTriple: ObjFile->makeTriple(), SSP: std::make_shared<orc::SymbolStringPool>(), |
67 | InputFileName: ObjFile->getFileName(), Features: nullptr, IsPIC: true, DwCtx: DWARFContext::create(Obj: *ObjFile), |
68 | Logger: {.Out: llvm::outs(), .Err: llvm::errs()})); |
69 | ASSERT_FALSE(!BC); |
70 | BC->initializeTarget(TargetBuilder: std::unique_ptr<MCPlusBuilder>( |
71 | createMCPlusBuilder(Arch: GetParam(), Analysis: BC->MIA.get(), Info: BC->MII.get(), |
72 | RegInfo: BC->MRI.get(), STI: BC->STI.get()))); |
73 | } |
74 | |
75 | void assertRegMask(const BitVector &RegMask, |
76 | std::initializer_list<MCPhysReg> ExpectedRegs) { |
77 | ASSERT_EQ(RegMask.count(), ExpectedRegs.size()); |
78 | for (MCPhysReg Reg : ExpectedRegs) |
79 | ASSERT_TRUE(RegMask[Reg]) << "Expected " << BC->MRI->getName(RegNo: Reg) << "." ; |
80 | } |
81 | |
82 | void assertRegMask(std::function<void(BitVector &)> FillRegMask, |
83 | std::initializer_list<MCPhysReg> ExpectedRegs) { |
84 | BitVector RegMask(BC->MRI->getNumRegs()); |
85 | FillRegMask(RegMask); |
86 | assertRegMask(RegMask, ExpectedRegs); |
87 | } |
88 | |
89 | void testRegAliases(Triple::ArchType Arch, uint64_t Register, |
90 | std::initializer_list<MCPhysReg> ExpectedAliases, |
91 | bool OnlySmaller = false) { |
92 | if (GetParam() != Arch) |
93 | GTEST_SKIP(); |
94 | |
95 | const BitVector &BV = BC->MIB->getAliases(Reg: Register, OnlySmaller); |
96 | assertRegMask(RegMask: BV, ExpectedRegs: ExpectedAliases); |
97 | } |
98 | |
99 | char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {}; |
100 | std::unique_ptr<ObjectFile> ObjFile; |
101 | std::unique_ptr<BinaryContext> BC; |
102 | }; |
103 | } // namespace |
104 | |
105 | #ifdef AARCH64_AVAILABLE |
106 | |
107 | INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester, |
108 | ::testing::Values(Triple::aarch64)); |
109 | |
110 | TEST_P(MCPlusBuilderTester, AliasX0) { |
111 | testRegAliases(Triple::aarch64, AArch64::X0, |
112 | {AArch64::W0, AArch64::W0_HI, AArch64::X0, AArch64::W0_W1, |
113 | AArch64::X0_X1, AArch64::X0_X1_X2_X3_X4_X5_X6_X7}); |
114 | } |
115 | |
116 | TEST_P(MCPlusBuilderTester, AliasSmallerX0) { |
117 | testRegAliases(Triple::aarch64, AArch64::X0, |
118 | {AArch64::W0, AArch64::W0_HI, AArch64::X0}, |
119 | /*OnlySmaller=*/true); |
120 | } |
121 | |
122 | TEST_P(MCPlusBuilderTester, AArch64_CmpJE) { |
123 | if (GetParam() != Triple::aarch64) |
124 | GTEST_SKIP(); |
125 | BinaryFunction *BF = BC->createInjectedBinaryFunction(Name: "BF" , IsSimple: true); |
126 | std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock(); |
127 | |
128 | InstructionListType Instrs = |
129 | BC->MIB->createCmpJE(AArch64::RegNo: X0, Imm: 2, Target: BB->getLabel(), Ctx: BC->Ctx.get()); |
130 | BB->addInstructions(Begin: Instrs.begin(), End: Instrs.end()); |
131 | BB->addSuccessor(Succ: BB.get()); |
132 | |
133 | auto II = BB->begin(); |
134 | ASSERT_EQ(II->getOpcode(), AArch64::SUBSXri); |
135 | ASSERT_EQ(II->getOperand(0).getReg(), AArch64::XZR); |
136 | ASSERT_EQ(II->getOperand(1).getReg(), AArch64::X0); |
137 | ASSERT_EQ(II->getOperand(2).getImm(), 2); |
138 | ASSERT_EQ(II->getOperand(3).getImm(), 0); |
139 | II++; |
140 | ASSERT_EQ(II->getOpcode(), AArch64::Bcc); |
141 | ASSERT_EQ(II->getOperand(0).getImm(), AArch64CC::EQ); |
142 | const MCSymbol *Label = BC->MIB->getTargetSymbol(Inst: *II, OpNum: 1); |
143 | ASSERT_EQ(Label, BB->getLabel()); |
144 | } |
145 | |
146 | TEST_P(MCPlusBuilderTester, AArch64_CmpJNE) { |
147 | if (GetParam() != Triple::aarch64) |
148 | GTEST_SKIP(); |
149 | BinaryFunction *BF = BC->createInjectedBinaryFunction(Name: "BF" , IsSimple: true); |
150 | std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock(); |
151 | |
152 | InstructionListType Instrs = |
153 | BC->MIB->createCmpJNE(AArch64::RegNo: X0, Imm: 2, Target: BB->getLabel(), Ctx: BC->Ctx.get()); |
154 | BB->addInstructions(Begin: Instrs.begin(), End: Instrs.end()); |
155 | BB->addSuccessor(Succ: BB.get()); |
156 | |
157 | auto II = BB->begin(); |
158 | ASSERT_EQ(II->getOpcode(), AArch64::SUBSXri); |
159 | ASSERT_EQ(II->getOperand(0).getReg(), AArch64::XZR); |
160 | ASSERT_EQ(II->getOperand(1).getReg(), AArch64::X0); |
161 | ASSERT_EQ(II->getOperand(2).getImm(), 2); |
162 | ASSERT_EQ(II->getOperand(3).getImm(), 0); |
163 | II++; |
164 | ASSERT_EQ(II->getOpcode(), AArch64::Bcc); |
165 | ASSERT_EQ(II->getOperand(0).getImm(), AArch64CC::NE); |
166 | const MCSymbol *Label = BC->MIB->getTargetSymbol(Inst: *II, OpNum: 1); |
167 | ASSERT_EQ(Label, BB->getLabel()); |
168 | } |
169 | |
170 | TEST_P(MCPlusBuilderTester, testAccessedRegsImplicitDef) { |
171 | if (GetParam() != Triple::aarch64) |
172 | GTEST_SKIP(); |
173 | |
174 | // adds x0, x5, #42 |
175 | MCInst Inst = MCInstBuilder(AArch64::ADDSXri) |
176 | .addReg(AArch64::X0) |
177 | .addReg(AArch64::X5) |
178 | .addImm(42) |
179 | .addImm(0); |
180 | |
181 | assertRegMask([&](BitVector &BV) { BC->MIB->getClobberedRegs(Inst, BV); }, |
182 | {AArch64::NZCV, AArch64::W0, AArch64::X0, AArch64::W0_HI, |
183 | AArch64::X0_X1_X2_X3_X4_X5_X6_X7, AArch64::W0_W1, |
184 | AArch64::X0_X1}); |
185 | |
186 | assertRegMask( |
187 | [&](BitVector &BV) { BC->MIB->getTouchedRegs(Inst, BV); }, |
188 | {AArch64::NZCV, AArch64::W0, AArch64::W5, AArch64::X0, AArch64::X5, |
189 | AArch64::W0_HI, AArch64::W5_HI, AArch64::X0_X1_X2_X3_X4_X5_X6_X7, |
190 | AArch64::X2_X3_X4_X5_X6_X7_X8_X9, AArch64::X4_X5_X6_X7_X8_X9_X10_X11, |
191 | AArch64::W0_W1, AArch64::W4_W5, AArch64::X0_X1, AArch64::X4_X5}); |
192 | |
193 | assertRegMask([&](BitVector &BV) { BC->MIB->getWrittenRegs(Inst, BV); }, |
194 | {AArch64::NZCV, AArch64::W0, AArch64::X0, AArch64::W0_HI}); |
195 | |
196 | assertRegMask([&](BitVector &BV) { BC->MIB->getUsedRegs(Inst, BV); }, |
197 | {AArch64::W5, AArch64::X5, AArch64::W5_HI}); |
198 | |
199 | assertRegMask([&](BitVector &BV) { BC->MIB->getSrcRegs(Inst, BV); }, |
200 | {AArch64::W5, AArch64::X5, AArch64::W5_HI}); |
201 | } |
202 | |
203 | TEST_P(MCPlusBuilderTester, testAccessedRegsImplicitUse) { |
204 | if (GetParam() != Triple::aarch64) |
205 | GTEST_SKIP(); |
206 | |
207 | // b.eq <label> |
208 | MCInst Inst = |
209 | MCInstBuilder(AArch64::Bcc) |
210 | .addImm(AArch64CC::EQ) |
211 | .addImm(0); // <label> - should be Expr, but immediate 0 works too. |
212 | |
213 | assertRegMask(FillRegMask: [&](BitVector &BV) { BC->MIB->getClobberedRegs(Inst, Regs&: BV); }, |
214 | ExpectedRegs: {}); |
215 | |
216 | assertRegMask([&](BitVector &BV) { BC->MIB->getTouchedRegs(Inst, BV); }, |
217 | {AArch64::NZCV}); |
218 | |
219 | assertRegMask(FillRegMask: [&](BitVector &BV) { BC->MIB->getWrittenRegs(Inst, Regs&: BV); }, ExpectedRegs: {}); |
220 | |
221 | assertRegMask([&](BitVector &BV) { BC->MIB->getUsedRegs(Inst, BV); }, |
222 | {AArch64::NZCV}); |
223 | |
224 | assertRegMask([&](BitVector &BV) { BC->MIB->getSrcRegs(Inst, BV); }, |
225 | {AArch64::NZCV}); |
226 | } |
227 | |
228 | TEST_P(MCPlusBuilderTester, testAccessedRegsMultipleDefs) { |
229 | if (GetParam() != Triple::aarch64) |
230 | GTEST_SKIP(); |
231 | |
232 | // ldr x0, [x5], #16 |
233 | MCInst Inst = MCInstBuilder(AArch64::LDRXpost) |
234 | .addReg(AArch64::X5) |
235 | .addReg(AArch64::X0) |
236 | .addReg(AArch64::X5) |
237 | .addImm(16); |
238 | |
239 | assertRegMask( |
240 | [&](BitVector &BV) { BC->MIB->getClobberedRegs(Inst, BV); }, |
241 | {AArch64::W0, AArch64::W5, AArch64::X0, AArch64::X5, AArch64::W0_HI, |
242 | AArch64::W5_HI, AArch64::X0_X1_X2_X3_X4_X5_X6_X7, |
243 | AArch64::X2_X3_X4_X5_X6_X7_X8_X9, AArch64::X4_X5_X6_X7_X8_X9_X10_X11, |
244 | AArch64::W0_W1, AArch64::W4_W5, AArch64::X0_X1, AArch64::X4_X5}); |
245 | |
246 | assertRegMask( |
247 | [&](BitVector &BV) { BC->MIB->getTouchedRegs(Inst, BV); }, |
248 | {AArch64::W0, AArch64::W5, AArch64::X0, AArch64::X5, AArch64::W0_HI, |
249 | AArch64::W5_HI, AArch64::X0_X1_X2_X3_X4_X5_X6_X7, |
250 | AArch64::X2_X3_X4_X5_X6_X7_X8_X9, AArch64::X4_X5_X6_X7_X8_X9_X10_X11, |
251 | AArch64::W0_W1, AArch64::W4_W5, AArch64::X0_X1, AArch64::X4_X5}); |
252 | |
253 | assertRegMask([&](BitVector &BV) { BC->MIB->getWrittenRegs(Inst, BV); }, |
254 | {AArch64::W0, AArch64::X0, AArch64::W0_HI, AArch64::W5, |
255 | AArch64::X5, AArch64::W5_HI}); |
256 | |
257 | assertRegMask([&](BitVector &BV) { BC->MIB->getUsedRegs(Inst, BV); }, |
258 | {AArch64::W5, AArch64::X5, AArch64::W5_HI}); |
259 | |
260 | assertRegMask([&](BitVector &BV) { BC->MIB->getSrcRegs(Inst, BV); }, |
261 | {AArch64::W5, AArch64::X5, AArch64::W5_HI}); |
262 | } |
263 | |
264 | #endif // AARCH64_AVAILABLE |
265 | |
266 | #ifdef X86_AVAILABLE |
267 | |
268 | INSTANTIATE_TEST_SUITE_P(X86, MCPlusBuilderTester, |
269 | ::testing::Values(Triple::x86_64)); |
270 | |
271 | TEST_P(MCPlusBuilderTester, AliasAX) { |
272 | testRegAliases(Triple::x86_64, X86::AX, |
273 | {X86::RAX, X86::EAX, X86::AX, X86::AL, X86::AH}); |
274 | } |
275 | |
276 | TEST_P(MCPlusBuilderTester, AliasSmallerAX) { |
277 | testRegAliases(Triple::x86_64, X86::AX, {X86::AX, X86::AL, X86::AH}, |
278 | /*OnlySmaller=*/true); |
279 | } |
280 | |
281 | TEST_P(MCPlusBuilderTester, ReplaceRegWithImm) { |
282 | if (GetParam() != Triple::x86_64) |
283 | GTEST_SKIP(); |
284 | BinaryFunction *BF = BC->createInjectedBinaryFunction(Name: "BF" , IsSimple: true); |
285 | std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock(); |
286 | MCInst Inst; // cmpl %eax, %ebx |
287 | Inst.setOpcode(X86::CMP32rr); |
288 | Inst.addOperand(MCOperand::createReg(X86::EAX)); |
289 | Inst.addOperand(MCOperand::createReg(X86::EBX)); |
290 | auto II = BB->addInstruction(Inst); |
291 | bool Replaced = BC->MIB->replaceRegWithImm(*II, X86::EBX, 1); |
292 | ASSERT_TRUE(Replaced); |
293 | ASSERT_EQ(II->getOpcode(), X86::CMP32ri8); |
294 | ASSERT_EQ(II->getOperand(0).getReg(), X86::EAX); |
295 | ASSERT_EQ(II->getOperand(1).getImm(), 1); |
296 | } |
297 | |
298 | TEST_P(MCPlusBuilderTester, X86_CmpJE) { |
299 | if (GetParam() != Triple::x86_64) |
300 | GTEST_SKIP(); |
301 | BinaryFunction *BF = BC->createInjectedBinaryFunction(Name: "BF" , IsSimple: true); |
302 | std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock(); |
303 | |
304 | InstructionListType Instrs = |
305 | BC->MIB->createCmpJE(X86::EAX, 2, BB->getLabel(), BC->Ctx.get()); |
306 | BB->addInstructions(Begin: Instrs.begin(), End: Instrs.end()); |
307 | BB->addSuccessor(Succ: BB.get()); |
308 | |
309 | auto II = BB->begin(); |
310 | ASSERT_EQ(II->getOpcode(), X86::CMP64ri8); |
311 | ASSERT_EQ(II->getOperand(0).getReg(), X86::EAX); |
312 | ASSERT_EQ(II->getOperand(1).getImm(), 2); |
313 | II++; |
314 | ASSERT_EQ(II->getOpcode(), X86::JCC_1); |
315 | const MCSymbol *Label = BC->MIB->getTargetSymbol(Inst: *II, OpNum: 0); |
316 | ASSERT_EQ(Label, BB->getLabel()); |
317 | ASSERT_EQ(II->getOperand(1).getImm(), X86::COND_E); |
318 | } |
319 | |
320 | TEST_P(MCPlusBuilderTester, X86_CmpJNE) { |
321 | if (GetParam() != Triple::x86_64) |
322 | GTEST_SKIP(); |
323 | BinaryFunction *BF = BC->createInjectedBinaryFunction(Name: "BF" , IsSimple: true); |
324 | std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock(); |
325 | |
326 | InstructionListType Instrs = |
327 | BC->MIB->createCmpJNE(X86::EAX, 2, BB->getLabel(), BC->Ctx.get()); |
328 | BB->addInstructions(Begin: Instrs.begin(), End: Instrs.end()); |
329 | BB->addSuccessor(Succ: BB.get()); |
330 | |
331 | auto II = BB->begin(); |
332 | ASSERT_EQ(II->getOpcode(), X86::CMP64ri8); |
333 | ASSERT_EQ(II->getOperand(0).getReg(), X86::EAX); |
334 | ASSERT_EQ(II->getOperand(1).getImm(), 2); |
335 | II++; |
336 | ASSERT_EQ(II->getOpcode(), X86::JCC_1); |
337 | const MCSymbol *Label = BC->MIB->getTargetSymbol(Inst: *II, OpNum: 0); |
338 | ASSERT_EQ(Label, BB->getLabel()); |
339 | ASSERT_EQ(II->getOperand(1).getImm(), X86::COND_NE); |
340 | } |
341 | |
342 | #endif // X86_AVAILABLE |
343 | |
344 | TEST_P(MCPlusBuilderTester, Annotation) { |
345 | MCInst Inst; |
346 | BC->MIB->createTailCall(Inst, Target: BC->Ctx->createNamedTempSymbol(), |
347 | Ctx: BC->Ctx.get()); |
348 | MCSymbol *LPSymbol = BC->Ctx->createNamedTempSymbol(Name: "LP" ); |
349 | uint64_t Value = INT32_MIN; |
350 | // Test encodeAnnotationImm using this indirect way |
351 | BC->MIB->addEHInfo(Inst, LP: MCPlus::MCLandingPad(LPSymbol, Value)); |
352 | // Round-trip encoding-decoding check for negative values |
353 | std::optional<MCPlus::MCLandingPad> EHInfo = BC->MIB->getEHInfo(Inst); |
354 | ASSERT_TRUE(EHInfo.has_value()); |
355 | MCPlus::MCLandingPad LP = EHInfo.value(); |
356 | uint64_t DecodedValue = LP.second; |
357 | ASSERT_EQ(Value, DecodedValue); |
358 | |
359 | // Large int64 should trigger an out of range assertion |
360 | Value = 0x1FF'FFFF'FFFF'FFFFULL; |
361 | Inst.clear(); |
362 | BC->MIB->createTailCall(Inst, Target: BC->Ctx->createNamedTempSymbol(), |
363 | Ctx: BC->Ctx.get()); |
364 | ASSERT_DEATH(BC->MIB->addEHInfo(Inst, MCPlus::MCLandingPad(LPSymbol, Value)), |
365 | "annotation value out of range" ); |
366 | } |
367 | |