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(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 | |
88 | INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester, |
89 | ::testing::Values(Triple::aarch64)); |
90 | |
91 | TEST_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 | |
98 | TEST_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 | |
108 | INSTANTIATE_TEST_SUITE_P(X86, MCPlusBuilderTester, |
109 | ::testing::Values(Triple::x86_64)); |
110 | |
111 | TEST_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 | |
117 | TEST_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 | |
123 | TEST_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 | |
142 | TEST_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 | |