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#endif // AARCH64_AVAILABLE
12
13#ifdef X86_AVAILABLE
14#include "X86Subtarget.h"
15#endif // X86_AVAILABLE
16
17#include "bolt/Core/BinaryBasicBlock.h"
18#include "bolt/Core/BinaryFunction.h"
19#include "bolt/Rewrite/RewriteInstance.h"
20#include "llvm/BinaryFormat/ELF.h"
21#include "llvm/DebugInfo/DWARF/DWARFContext.h"
22#include "llvm/Support/TargetSelect.h"
23#include "gtest/gtest.h"
24
25using namespace llvm;
26using namespace llvm::object;
27using namespace llvm::ELF;
28using namespace bolt;
29
30namespace {
31struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
32 void SetUp() override {
33 initalizeLLVM();
34 prepareElf();
35 initializeBolt();
36 }
37
38protected:
39 void initalizeLLVM() {
40 llvm::InitializeAllTargetInfos();
41 llvm::InitializeAllTargetMCs();
42 llvm::InitializeAllAsmParsers();
43 llvm::InitializeAllDisassemblers();
44 llvm::InitializeAllTargets();
45 llvm::InitializeAllAsmPrinters();
46 }
47
48 void prepareElf() {
49 memcpy(dest: ElfBuf, src: "\177ELF", n: 4);
50 ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
51 EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
52 EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
53 EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;
54 MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
55 ObjFile = cantFail(ValOrErr: ObjectFile::createObjectFile(Object: Source));
56 }
57
58 void initializeBolt() {
59 BC = cantFail(ValOrErr: BinaryContext::createBinaryContext(
60 TheTriple: ObjFile->makeTriple(), InputFileName: ObjFile->getFileName(), Features: nullptr, IsPIC: true,
61 DwCtx: DWARFContext::create(Obj: *ObjFile.get()), Logger: {.Out: llvm::outs(), .Err: llvm::errs()}));
62 ASSERT_FALSE(!BC);
63 BC->initializeTarget(TargetBuilder: std::unique_ptr<MCPlusBuilder>(
64 createMCPlusBuilder(Arch: GetParam(), Analysis: BC->MIA.get(), Info: BC->MII.get(),
65 RegInfo: BC->MRI.get(), STI: BC->STI.get())));
66 }
67
68 void testRegAliases(Triple::ArchType Arch, uint64_t Register,
69 uint64_t *Aliases, size_t Count,
70 bool OnlySmaller = false) {
71 if (GetParam() != Arch)
72 GTEST_SKIP();
73
74 const BitVector &BV = BC->MIB->getAliases(Reg: Register, OnlySmaller);
75 ASSERT_EQ(BV.count(), Count);
76 for (size_t I = 0; I < Count; ++I)
77 ASSERT_TRUE(BV[Aliases[I]]);
78 }
79
80 char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
81 std::unique_ptr<ObjectFile> ObjFile;
82 std::unique_ptr<BinaryContext> BC;
83};
84} // namespace
85
86#ifdef AARCH64_AVAILABLE
87
88INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester,
89 ::testing::Values(Triple::aarch64));
90
91TEST_P(MCPlusBuilderTester, AliasX0) {
92 uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0, AArch64::W0_W1,
93 AArch64::X0_X1, AArch64::X0_X1_X2_X3_X4_X5_X6_X7};
94 size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
95 testRegAliases(Arch: Triple::aarch64, AArch64::Register: X0, Aliases: AliasesX0, Count: AliasesX0Count);
96}
97
98TEST_P(MCPlusBuilderTester, AliasSmallerX0) {
99 uint64_t AliasesX0[] = {AArch64::W0, AArch64::X0};
100 size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
101 testRegAliases(Arch: Triple::aarch64, AArch64::Register: X0, Aliases: AliasesX0, Count: AliasesX0Count, OnlySmaller: true);
102}
103
104#endif // AARCH64_AVAILABLE
105
106#ifdef X86_AVAILABLE
107
108INSTANTIATE_TEST_SUITE_P(X86, MCPlusBuilderTester,
109 ::testing::Values(Triple::x86_64));
110
111TEST_P(MCPlusBuilderTester, AliasAX) {
112 uint64_t AliasesAX[] = {X86::RAX, X86::EAX, X86::AX, X86::AL, X86::AH};
113 size_t AliasesAXCount = sizeof(AliasesAX) / sizeof(*AliasesAX);
114 testRegAliases(Arch: Triple::x86_64, X86::Register: AX, Aliases: AliasesAX, Count: AliasesAXCount);
115}
116
117TEST_P(MCPlusBuilderTester, AliasSmallerAX) {
118 uint64_t AliasesAX[] = {X86::AX, X86::AL, X86::AH};
119 size_t AliasesAXCount = sizeof(AliasesAX) / sizeof(*AliasesAX);
120 testRegAliases(Arch: Triple::x86_64, X86::Register: AX, Aliases: AliasesAX, Count: AliasesAXCount, OnlySmaller: true);
121}
122
123TEST_P(MCPlusBuilderTester, ReplaceRegWithImm) {
124 if (GetParam() != Triple::x86_64)
125 GTEST_SKIP();
126 BinaryFunction *BF = BC->createInjectedBinaryFunction(Name: "BF", IsSimple: true);
127 std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
128 MCInst Inst; // cmpl %eax, %ebx
129 Inst.setOpcode(X86::CMP32rr);
130 Inst.addOperand(Op: MCOperand::createReg(Reg: X86::EAX));
131 Inst.addOperand(Op: MCOperand::createReg(Reg: X86::EBX));
132 auto II = BB->addInstruction(Inst);
133 bool Replaced = BC->MIB->replaceRegWithImm(Inst&: *II, Register: X86::EBX, Imm: 1);
134 ASSERT_TRUE(Replaced);
135 ASSERT_EQ(II->getOpcode(), X86::CMP32ri8);
136 ASSERT_EQ(II->getOperand(0).getReg(), X86::EAX);
137 ASSERT_EQ(II->getOperand(1).getImm(), 1);
138}
139
140#endif // X86_AVAILABLE
141
142TEST_P(MCPlusBuilderTester, Annotation) {
143 MCInst Inst;
144 BC->MIB->createTailCall(Inst, Target: BC->Ctx->createNamedTempSymbol(),
145 Ctx: BC->Ctx.get());
146 MCSymbol *LPSymbol = BC->Ctx->createNamedTempSymbol(Name: "LP");
147 uint64_t Value = INT32_MIN;
148 // Test encodeAnnotationImm using this indirect way
149 BC->MIB->addEHInfo(Inst, LP: MCPlus::MCLandingPad(LPSymbol, Value));
150 // Round-trip encoding-decoding check for negative values
151 std::optional<MCPlus::MCLandingPad> EHInfo = BC->MIB->getEHInfo(Inst);
152 ASSERT_TRUE(EHInfo.has_value());
153 MCPlus::MCLandingPad LP = EHInfo.value();
154 uint64_t DecodedValue = LP.second;
155 ASSERT_EQ(Value, DecodedValue);
156
157 // Large int64 should trigger an out of range assertion
158 Value = 0x1FF'FFFF'FFFF'FFFFULL;
159 Inst.clear();
160 BC->MIB->createTailCall(Inst, Target: BC->Ctx->createNamedTempSymbol(),
161 Ctx: BC->Ctx.get());
162 ASSERT_DEATH(BC->MIB->addEHInfo(Inst, MCPlus::MCLandingPad(LPSymbol, Value)),
163 "annotation value out of range");
164}
165

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