xref: /llvm-project/bolt/unittests/Core/MCPlusBuilder.cpp (revision 2ccf7ed277df28651b94bbee9fccefdf22fb074f)
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 
25 using namespace llvm;
26 using namespace llvm::object;
27 using namespace llvm::ELF;
28 using namespace bolt;
29 
30 namespace {
31 struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
32   void SetUp() override {
33     initalizeLLVM();
34     prepareElf();
35     initializeBolt();
36   }
37 
38 protected:
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(ElfBuf, "\177ELF", 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(ObjectFile::createObjectFile(Source));
56   }
57 
58   void initializeBolt() {
59     Relocation::Arch = ObjFile->makeTriple().getArch();
60     BC = cantFail(BinaryContext::createBinaryContext(
61         ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
62         ObjFile->getFileName(), nullptr, true,
63         DWARFContext::create(*ObjFile.get()), {llvm::outs(), llvm::errs()}));
64     ASSERT_FALSE(!BC);
65     BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(
66         createMCPlusBuilder(GetParam(), BC->MIA.get(), BC->MII.get(),
67                             BC->MRI.get(), BC->STI.get())));
68   }
69 
70   void testRegAliases(Triple::ArchType Arch, uint64_t Register,
71                       uint64_t *Aliases, size_t Count,
72                       bool OnlySmaller = false) {
73     if (GetParam() != Arch)
74       GTEST_SKIP();
75 
76     const BitVector &BV = BC->MIB->getAliases(Register, OnlySmaller);
77     ASSERT_EQ(BV.count(), Count);
78     for (size_t I = 0; I < Count; ++I)
79       ASSERT_TRUE(BV[Aliases[I]]);
80   }
81 
82   char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
83   std::unique_ptr<ObjectFile> ObjFile;
84   std::unique_ptr<BinaryContext> BC;
85 };
86 } // namespace
87 
88 #ifdef AARCH64_AVAILABLE
89 
90 INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester,
91                          ::testing::Values(Triple::aarch64));
92 
93 TEST_P(MCPlusBuilderTester, AliasX0) {
94   uint64_t AliasesX0[] = {AArch64::W0,    AArch64::W0_HI,
95                           AArch64::X0,    AArch64::W0_W1,
96                           AArch64::X0_X1, AArch64::X0_X1_X2_X3_X4_X5_X6_X7};
97   size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
98   testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count);
99 }
100 
101 TEST_P(MCPlusBuilderTester, AliasSmallerX0) {
102   uint64_t AliasesX0[] = {AArch64::W0, AArch64::W0_HI, AArch64::X0};
103   size_t AliasesX0Count = sizeof(AliasesX0) / sizeof(*AliasesX0);
104   testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count, true);
105 }
106 
107 #endif // AARCH64_AVAILABLE
108 
109 #ifdef X86_AVAILABLE
110 
111 INSTANTIATE_TEST_SUITE_P(X86, MCPlusBuilderTester,
112                          ::testing::Values(Triple::x86_64));
113 
114 TEST_P(MCPlusBuilderTester, AliasAX) {
115   uint64_t AliasesAX[] = {X86::RAX, X86::EAX, X86::AX, X86::AL, X86::AH};
116   size_t AliasesAXCount = sizeof(AliasesAX) / sizeof(*AliasesAX);
117   testRegAliases(Triple::x86_64, X86::AX, AliasesAX, AliasesAXCount);
118 }
119 
120 TEST_P(MCPlusBuilderTester, AliasSmallerAX) {
121   uint64_t AliasesAX[] = {X86::AX, X86::AL, X86::AH};
122   size_t AliasesAXCount = sizeof(AliasesAX) / sizeof(*AliasesAX);
123   testRegAliases(Triple::x86_64, X86::AX, AliasesAX, AliasesAXCount, true);
124 }
125 
126 TEST_P(MCPlusBuilderTester, ReplaceRegWithImm) {
127   if (GetParam() != Triple::x86_64)
128     GTEST_SKIP();
129   BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
130   std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
131   MCInst Inst; // cmpl    %eax, %ebx
132   Inst.setOpcode(X86::CMP32rr);
133   Inst.addOperand(MCOperand::createReg(X86::EAX));
134   Inst.addOperand(MCOperand::createReg(X86::EBX));
135   auto II = BB->addInstruction(Inst);
136   bool Replaced = BC->MIB->replaceRegWithImm(*II, X86::EBX, 1);
137   ASSERT_TRUE(Replaced);
138   ASSERT_EQ(II->getOpcode(), X86::CMP32ri8);
139   ASSERT_EQ(II->getOperand(0).getReg(), X86::EAX);
140   ASSERT_EQ(II->getOperand(1).getImm(), 1);
141 }
142 
143 #endif // X86_AVAILABLE
144 
145 TEST_P(MCPlusBuilderTester, Annotation) {
146   MCInst Inst;
147   BC->MIB->createTailCall(Inst, BC->Ctx->createNamedTempSymbol(),
148                           BC->Ctx.get());
149   MCSymbol *LPSymbol = BC->Ctx->createNamedTempSymbol("LP");
150   uint64_t Value = INT32_MIN;
151   // Test encodeAnnotationImm using this indirect way
152   BC->MIB->addEHInfo(Inst, MCPlus::MCLandingPad(LPSymbol, Value));
153   // Round-trip encoding-decoding check for negative values
154   std::optional<MCPlus::MCLandingPad> EHInfo = BC->MIB->getEHInfo(Inst);
155   ASSERT_TRUE(EHInfo.has_value());
156   MCPlus::MCLandingPad LP = EHInfo.value();
157   uint64_t DecodedValue = LP.second;
158   ASSERT_EQ(Value, DecodedValue);
159 
160   // Large int64 should trigger an out of range assertion
161   Value = 0x1FF'FFFF'FFFF'FFFFULL;
162   Inst.clear();
163   BC->MIB->createTailCall(Inst, BC->Ctx->createNamedTempSymbol(),
164                           BC->Ctx.get());
165   ASSERT_DEATH(BC->MIB->addEHInfo(Inst, MCPlus::MCLandingPad(LPSymbol, Value)),
166                "annotation value out of range");
167 }
168