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
27using namespace llvm;
28using namespace llvm::object;
29using namespace llvm::ELF;
30using namespace bolt;
31
32namespace {
33struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
34 void SetUp() override {
35 initalizeLLVM();
36 prepareElf();
37 initializeBolt();
38 }
39
40protected:
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
107INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester,
108 ::testing::Values(Triple::aarch64));
109
110TEST_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
116TEST_P(MCPlusBuilderTester, AliasSmallerX0) {
117 testRegAliases(Triple::aarch64, AArch64::X0,
118 {AArch64::W0, AArch64::W0_HI, AArch64::X0},
119 /*OnlySmaller=*/true);
120}
121
122TEST_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
146TEST_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
170TEST_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
203TEST_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
228TEST_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
268INSTANTIATE_TEST_SUITE_P(X86, MCPlusBuilderTester,
269 ::testing::Values(Triple::x86_64));
270
271TEST_P(MCPlusBuilderTester, AliasAX) {
272 testRegAliases(Triple::x86_64, X86::AX,
273 {X86::RAX, X86::EAX, X86::AX, X86::AL, X86::AH});
274}
275
276TEST_P(MCPlusBuilderTester, AliasSmallerAX) {
277 testRegAliases(Triple::x86_64, X86::AX, {X86::AX, X86::AL, X86::AH},
278 /*OnlySmaller=*/true);
279}
280
281TEST_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
298TEST_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
320TEST_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
344TEST_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

source code of bolt/unittests/Core/MCPlusBuilder.cpp