//===- bolt/unittest/Core/MCPlusBuilder.cpp -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifdef AARCH64_AVAILABLE #include "AArch64Subtarget.h" #include "MCTargetDesc/AArch64MCAsmInfo.h" #include "MCTargetDesc/AArch64MCTargetDesc.h" #endif // AARCH64_AVAILABLE #ifdef X86_AVAILABLE #include "X86Subtarget.h" #endif // X86_AVAILABLE #include "bolt/Core/BinaryBasicBlock.h" #include "bolt/Core/BinaryFunction.h" #include "bolt/Rewrite/RewriteInstance.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/MC/MCInstBuilder.h" #include "llvm/Support/TargetSelect.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; using namespace bolt; namespace { struct MCPlusBuilderTester : public testing::TestWithParam { void SetUp() override { initalizeLLVM(); prepareElf(); initializeBolt(); } protected: void initalizeLLVM() { #define BOLT_TARGET(target) \ LLVMInitialize##target##TargetInfo(); \ LLVMInitialize##target##TargetMC(); \ LLVMInitialize##target##AsmParser(); \ LLVMInitialize##target##Disassembler(); \ LLVMInitialize##target##Target(); \ LLVMInitialize##target##AsmPrinter(); #include "bolt/Core/TargetConfig.def" } void prepareElf() { memcpy(ElfBuf, "\177ELF", 4); ELF64LE::Ehdr *EHdr = reinterpret_cast(ElfBuf); EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64; EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB; EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64; MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF"); ObjFile = cantFail(ObjectFile::createObjectFile(Source)); } void initializeBolt() { Relocation::Arch = ObjFile->makeTriple().getArch(); BC = cantFail(BinaryContext::createBinaryContext( ObjFile->makeTriple(), std::make_shared(), ObjFile->getFileName(), nullptr, true, DWARFContext::create(*ObjFile), {llvm::outs(), llvm::errs()})); ASSERT_FALSE(!BC); BC->initializeTarget(std::unique_ptr( createMCPlusBuilder(GetParam(), BC->MIA.get(), BC->MII.get(), BC->MRI.get(), BC->STI.get()))); } void assertRegMask(const BitVector &RegMask, std::initializer_list ExpectedRegs) { ASSERT_EQ(RegMask.count(), ExpectedRegs.size()); for (MCPhysReg Reg : ExpectedRegs) ASSERT_TRUE(RegMask[Reg]) << "Expected " << BC->MRI->getName(Reg) << "."; } void assertRegMask(std::function FillRegMask, std::initializer_list ExpectedRegs) { BitVector RegMask(BC->MRI->getNumRegs()); FillRegMask(RegMask); assertRegMask(RegMask, ExpectedRegs); } void testRegAliases(Triple::ArchType Arch, uint64_t Register, std::initializer_list ExpectedAliases, bool OnlySmaller = false) { if (GetParam() != Arch) GTEST_SKIP(); const BitVector &BV = BC->MIB->getAliases(Register, OnlySmaller); assertRegMask(BV, ExpectedAliases); } char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {}; std::unique_ptr ObjFile; std::unique_ptr BC; }; } // namespace #ifdef AARCH64_AVAILABLE INSTANTIATE_TEST_SUITE_P(AArch64, MCPlusBuilderTester, ::testing::Values(Triple::aarch64)); TEST_P(MCPlusBuilderTester, AliasX0) { testRegAliases(Triple::aarch64, AArch64::X0, {AArch64::W0, AArch64::W0_HI, AArch64::X0, AArch64::W0_W1, AArch64::X0_X1, AArch64::X0_X1_X2_X3_X4_X5_X6_X7}); } TEST_P(MCPlusBuilderTester, AliasSmallerX0) { testRegAliases(Triple::aarch64, AArch64::X0, {AArch64::W0, AArch64::W0_HI, AArch64::X0}, /*OnlySmaller=*/true); } TEST_P(MCPlusBuilderTester, AArch64_createLoadImmediate) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); // mov x0, 0x0001000100010001 --> orr x0, xzr, 0x0001000100010001 auto Insts1 = BC->MIB->createLoadImmediate(AArch64::X0, 0x0001000100010001); ASSERT_EQ(Insts1.size(), 1U); ASSERT_EQ(Insts1[0].getOpcode(), AArch64::ORRXri); ASSERT_EQ(Insts1[0].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts1[0].getOperand(1).getReg(), AArch64::XZR); ASSERT_EQ(Insts1[0].getOperand(2).getImm(), 32); // mov w0, #0x00030003 --> orr w0, wzr, 0x00030003 auto Insts2 = BC->MIB->createLoadImmediate(AArch64::W0, 0x00030003); ASSERT_EQ(Insts2.size(), 1U); ASSERT_EQ(Insts2[0].getOpcode(), AArch64::ORRWri); ASSERT_EQ(Insts2[0].getOperand(0).getReg(), AArch64::W0); ASSERT_EQ(Insts2[0].getOperand(1).getReg(), AArch64::WZR); ASSERT_EQ(Insts2[0].getOperand(2).getImm(), 33); // mov x0, #-16 --> movn x0, #15 auto Insts3 = BC->MIB->createLoadImmediate(AArch64::X0, 0xfffffffffffffff0); ASSERT_EQ(Insts3.size(), 1U); ASSERT_EQ(Insts3[0].getOpcode(), AArch64::MOVNXi); ASSERT_EQ(Insts3[0].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts3[0].getOperand(1).getImm(), 15); ASSERT_EQ(Insts3[0].getOperand(2).getImm(), 0); // mov w0, #-1 --> movn w0, #0 auto Insts4 = BC->MIB->createLoadImmediate(AArch64::W0, 0xffffffff); ASSERT_EQ(Insts4.size(), 1U); ASSERT_EQ(Insts4[0].getOpcode(), AArch64::MOVNWi); ASSERT_EQ(Insts4[0].getOperand(0).getReg(), AArch64::W0); ASSERT_EQ(Insts4[0].getOperand(1).getImm(), 0); ASSERT_EQ(Insts4[0].getOperand(2).getImm(), 0); // mov x0, #0x1 // movk x0, #0x2, lsl #16 auto Insts5 = BC->MIB->createLoadImmediate(AArch64::X0, 0x00020001); ASSERT_EQ(Insts5.size(), 2U); ASSERT_EQ(Insts5[0].getOpcode(), AArch64::MOVZXi); ASSERT_EQ(Insts5[0].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts5[0].getOperand(1).getImm(), 1); ASSERT_EQ(Insts5[0].getOperand(2).getImm(), 0); ASSERT_EQ(Insts5[1].getOpcode(), AArch64::MOVKXi); ASSERT_EQ(Insts5[1].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts5[1].getOperand(1).getReg(), AArch64::X0); ASSERT_EQ(Insts5[1].getOperand(2).getImm(), 2); ASSERT_EQ(Insts5[1].getOperand(3).getImm(), 16); // mov x0, #0x1 // movk x0, #0x2, lsl #16 // movk x0, #0x3, lsl #32 auto Insts6 = BC->MIB->createLoadImmediate(AArch64::X0, 0x000300020001); ASSERT_EQ(Insts6.size(), 3U); ASSERT_EQ(Insts6[0].getOpcode(), AArch64::MOVZXi); ASSERT_EQ(Insts6[0].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts6[0].getOperand(1).getImm(), 1); ASSERT_EQ(Insts6[0].getOperand(2).getImm(), 0); ASSERT_EQ(Insts6[1].getOpcode(), AArch64::MOVKXi); ASSERT_EQ(Insts6[1].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts6[1].getOperand(1).getReg(), AArch64::X0); ASSERT_EQ(Insts6[1].getOperand(2).getImm(), 2); ASSERT_EQ(Insts6[1].getOperand(3).getImm(), 16); ASSERT_EQ(Insts6[2].getOpcode(), AArch64::MOVKXi); ASSERT_EQ(Insts6[2].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts6[2].getOperand(1).getReg(), AArch64::X0); ASSERT_EQ(Insts6[2].getOperand(2).getImm(), 3); ASSERT_EQ(Insts6[2].getOperand(3).getImm(), 32); // mov x0, #0x1 // movk x0, #0x2, lsl #16 // movk x0, #0x3, lsl #32 // movk x0, #0x4, lsl #48 auto Insts7 = BC->MIB->createLoadImmediate(AArch64::X0, 0x0004000300020001); ASSERT_EQ(Insts7.size(), 4U); ASSERT_EQ(Insts7[0].getOpcode(), AArch64::MOVZXi); ASSERT_EQ(Insts7[0].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts7[0].getOperand(1).getImm(), 1); ASSERT_EQ(Insts7[0].getOperand(2).getImm(), 0); ASSERT_EQ(Insts7[1].getOpcode(), AArch64::MOVKXi); ASSERT_EQ(Insts7[1].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts7[1].getOperand(1).getReg(), AArch64::X0); ASSERT_EQ(Insts7[1].getOperand(2).getImm(), 2); ASSERT_EQ(Insts7[1].getOperand(3).getImm(), 16); ASSERT_EQ(Insts7[2].getOpcode(), AArch64::MOVKXi); ASSERT_EQ(Insts7[2].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts7[2].getOperand(1).getReg(), AArch64::X0); ASSERT_EQ(Insts7[2].getOperand(2).getImm(), 3); ASSERT_EQ(Insts7[2].getOperand(3).getImm(), 32); ASSERT_EQ(Insts7[3].getOpcode(), AArch64::MOVKXi); ASSERT_EQ(Insts7[3].getOperand(0).getReg(), AArch64::X0); ASSERT_EQ(Insts7[3].getOperand(1).getReg(), AArch64::X0); ASSERT_EQ(Insts7[3].getOperand(2).getImm(), 4); ASSERT_EQ(Insts7[3].getOperand(3).getImm(), 48); } TEST_P(MCPlusBuilderTester, AArch64_ReverseCompAndBranch) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr TargetBB = BF->createBasicBlock(); // Compare register with immediate and branch. // Inversion requires incrementing the immediate value. // cbgt x0, #0, target ~> cblt x0, #1, target MCInst NeedsImmInc = MCInstBuilder(AArch64::CBGTXri) .addReg(AArch64::X0) .addImm(0) .addExpr(MCSymbolRefExpr::create( TargetBB->getLabel(), *BC->Ctx.get())); ASSERT_TRUE(BC->MIB->isReversibleBranch(NeedsImmInc)); BC->MIB->reverseBranchCondition(NeedsImmInc, TargetBB->getLabel(), BC->Ctx.get()); ASSERT_EQ(NeedsImmInc.getOpcode(), AArch64::CBLTXri); ASSERT_EQ(NeedsImmInc.getOperand(1).getImm(), 1); // Compare register with immediate and branch. // Inversion requires decrementing the immediate value. // cblo x0, #1, target ~> cbhi x0, #0, target MCInst NeedsImmDec = MCInstBuilder(AArch64::CBLOXri) .addReg(AArch64::X0) .addImm(1) .addExpr(MCSymbolRefExpr::create( TargetBB->getLabel(), *BC->Ctx.get())); ASSERT_TRUE(BC->MIB->isReversibleBranch(NeedsImmDec)); BC->MIB->reverseBranchCondition(NeedsImmDec, TargetBB->getLabel(), BC->Ctx.get()); ASSERT_EQ(NeedsImmDec.getOpcode(), AArch64::CBHIXri); ASSERT_EQ(NeedsImmDec.getOperand(1).getImm(), 0); // Compare registers and branch. // Inversion requires swapping registers. // cbge x0, x1, target ~> cbgt x1, x0, target MCInst CompRegNeedsRegSwap = MCInstBuilder(AArch64::CBGEXrr) .addReg(AArch64::X0) .addReg(AArch64::X1) .addExpr(MCSymbolRefExpr::create( TargetBB->getLabel(), *BC->Ctx.get())); ASSERT_TRUE(BC->MIB->isReversibleBranch(CompRegNeedsRegSwap)); BC->MIB->reverseBranchCondition(CompRegNeedsRegSwap, TargetBB->getLabel(), BC->Ctx.get()); ASSERT_EQ(CompRegNeedsRegSwap.getOpcode(), AArch64::CBGTXrr); ASSERT_EQ(CompRegNeedsRegSwap.getOperand(0).getReg(), AArch64::X1); ASSERT_EQ(CompRegNeedsRegSwap.getOperand(1).getReg(), AArch64::X0); // Compare bytes and branch. // Inversion requires swapping registers. // cbbhi w0, w1, target ~> cbbhs w1, w0, target MCInst CompByteNeedsRegSwap = MCInstBuilder(AArch64::CBBHIWrr) .addReg(AArch64::W0) .addReg(AArch64::W1) .addExpr(MCSymbolRefExpr::create( TargetBB->getLabel(), *BC->Ctx.get())); ASSERT_TRUE(BC->MIB->isReversibleBranch(CompByteNeedsRegSwap)); BC->MIB->reverseBranchCondition(CompByteNeedsRegSwap, TargetBB->getLabel(), BC->Ctx.get()); ASSERT_EQ(CompByteNeedsRegSwap.getOpcode(), AArch64::CBBHSWrr); ASSERT_EQ(CompByteNeedsRegSwap.getOperand(0).getReg(), AArch64::W1); ASSERT_EQ(CompByteNeedsRegSwap.getOperand(1).getReg(), AArch64::W0); // Compare halfwords and branch. // Inversion requires swapping registers. // cbhhs w0, w1, target ~> cbhhi w1, w0, target MCInst CompHalfNeedsRegSwap = MCInstBuilder(AArch64::CBHHSWrr) .addReg(AArch64::W0) .addReg(AArch64::W1) .addExpr(MCSymbolRefExpr::create( TargetBB->getLabel(), *BC->Ctx.get())); ASSERT_TRUE(BC->MIB->isReversibleBranch(CompHalfNeedsRegSwap)); BC->MIB->reverseBranchCondition(CompHalfNeedsRegSwap, TargetBB->getLabel(), BC->Ctx.get()); ASSERT_EQ(CompHalfNeedsRegSwap.getOpcode(), AArch64::CBHHIWrr); ASSERT_EQ(CompHalfNeedsRegSwap.getOperand(0).getReg(), AArch64::W1); ASSERT_EQ(CompHalfNeedsRegSwap.getOperand(1).getReg(), AArch64::W0); // Compare register with immediate and branch. // Inversion not possible, immediate value underflows. // cblt x0, #0, target MCInst Underflows = MCInstBuilder(AArch64::CBLTXri) .addReg(AArch64::X0) .addImm(0) .addExpr(MCSymbolRefExpr::create(TargetBB->getLabel(), *BC->Ctx.get())); ASSERT_FALSE(BC->MIB->isReversibleBranch(Underflows)); // Compare register with immediate and branch. // Inversion not possible, immediate value overflows. // cbhi x0, #63, target MCInst Overflows = MCInstBuilder(AArch64::CBHIXri) .addReg(AArch64::X0) .addImm(63) .addExpr(MCSymbolRefExpr::create(TargetBB->getLabel(), *BC->Ctx.get())); ASSERT_FALSE(BC->MIB->isReversibleBranch(Overflows)); } TEST_P(MCPlusBuilderTester, AArch64_CmpJE) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); InstructionListType Instrs = BC->MIB->createCmpJE(AArch64::X0, 2, BB->getLabel(), BC->Ctx.get()); BB->addInstructions(Instrs.begin(), Instrs.end()); BB->addSuccessor(BB.get()); auto II = BB->begin(); ASSERT_EQ(II->getOpcode(), AArch64::SUBSXri); ASSERT_EQ(II->getOperand(0).getReg(), AArch64::XZR); ASSERT_EQ(II->getOperand(1).getReg(), AArch64::X0); ASSERT_EQ(II->getOperand(2).getImm(), 2); ASSERT_EQ(II->getOperand(3).getImm(), 0); II++; ASSERT_EQ(II->getOpcode(), AArch64::Bcc); ASSERT_EQ(II->getOperand(0).getImm(), AArch64CC::EQ); const MCSymbol *Label = BC->MIB->getTargetSymbol(*II, 1); ASSERT_EQ(Label, BB->getLabel()); } TEST_P(MCPlusBuilderTester, AArch64_BTI) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); MCInst BTIjc; BC->MIB->createBTI(BTIjc, BTIKind::JC); BB->addInstruction(BTIjc); BC->MIB->setRAState(BTIjc, true); ASSERT_NE(BTIjc.getNumOperands(), 1u); ASSERT_EQ(MCPlus::getNumPrimeOperands(BTIjc), 1u); auto II = BB->begin(); ASSERT_EQ(II->getOpcode(), AArch64::HINT); ASSERT_EQ(II->getOperand(0).getImm(), 38); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::JC)); MCInst BTIj; BC->MIB->createBTI(BTIj, BTIKind::J); BC->MIB->setRAState(BTIj, false); ASSERT_NE(BTIj.getNumOperands(), 1u); ASSERT_EQ(MCPlus::getNumPrimeOperands(BTIj), 1u); II = BB->addInstruction(BTIj); ASSERT_EQ(II->getOpcode(), AArch64::HINT); ASSERT_EQ(II->getOperand(0).getImm(), 36); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::J)); MCInst BTIc; BC->MIB->createBTI(BTIc, BTIKind::C); II = BB->addInstruction(BTIc); ASSERT_EQ(II->getOpcode(), AArch64::HINT); ASSERT_EQ(II->getOperand(0).getImm(), 34); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::C)); MCInst Paciasp = MCInstBuilder(AArch64::PACIASP); II = BB->addInstruction(Paciasp); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::C)); ASSERT_FALSE(BC->MIB->isBTILandingPad(*II, BTIKind::JC)); ASSERT_FALSE(BC->MIB->isBTILandingPad(*II, BTIKind::J)); ASSERT_TRUE(BC->MIB->isImplicitBTIC(*II)); MCInst Pacibsp = MCInstBuilder(AArch64::PACIBSP); II = BB->addInstruction(Pacibsp); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::C)); ASSERT_FALSE(BC->MIB->isBTILandingPad(*II, BTIKind::JC)); ASSERT_FALSE(BC->MIB->isBTILandingPad(*II, BTIKind::J)); ASSERT_TRUE(BC->MIB->isImplicitBTIC(*II)); } TEST_P(MCPlusBuilderTester, AArch64_insertBTI_empty) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X16); BC->MIB->insertBTI(*BB, CallInst); // Check that BTI c is added to the empty block. auto II = BB->begin(); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::C)); } TEST_P(MCPlusBuilderTester, AArch64_insertBTI_0) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); MCInst Inst = MCInstBuilder(AArch64::RET).addReg(AArch64::LR); BB->addInstruction(Inst); // BR x16 needs BTI c or BTI j. We prefer adding a BTI c. MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X16); // Adding an annotation to the call, to check if param numbers are calculated // correctly. Could be any other annotation as well. BC->MIB->setRAState(CallInst, false); ASSERT_NE(CallInst.getNumOperands(), 1u); ASSERT_EQ(MCPlus::getNumPrimeOperands(CallInst), 1u); auto II = BB->begin(); ASSERT_FALSE(BC->MIB->isCallCoveredByBTI(CallInst, *II)); BC->MIB->insertBTI(*BB, CallInst); II = BB->begin(); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::C)); } TEST_P(MCPlusBuilderTester, AArch64_insertBTI_1) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); MCInst BTIc; BC->MIB->createBTI(BTIc, BTIKind::C); BB->addInstruction(BTIc); // BR x16 needs BTI c or BTI j. We have a BTI c, no change is needed. MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X16); BC->MIB->setRAState(CallInst, true); ASSERT_NE(CallInst.getNumOperands(), 1u); ASSERT_EQ(MCPlus::getNumPrimeOperands(CallInst), 1u); auto II = BB->begin(); ASSERT_TRUE(BC->MIB->isCallCoveredByBTI(CallInst, *II)); BC->MIB->insertBTI(*BB, CallInst); II = BB->begin(); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::C)); } TEST_P(MCPlusBuilderTester, AArch64_insertBTI_2) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); MCInst BTIc; BC->MIB->createBTI(BTIc, BTIKind::C); BB->addInstruction(BTIc); // BR x5 needs BTI j // we have BTI c -> extend it to BTI jc. MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X5); auto II = BB->begin(); ASSERT_FALSE(BC->MIB->isCallCoveredByBTI(CallInst, *II)); BC->MIB->insertBTI(*BB, CallInst); II = BB->begin(); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::JC)); } TEST_P(MCPlusBuilderTester, AArch64_insertBTI_3) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); MCInst Inst = MCInstBuilder(AArch64::RET).addReg(AArch64::LR); BB->addInstruction(Inst); // BR x5 needs BTI j MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X5); auto II = BB->begin(); ASSERT_FALSE(BC->MIB->isCallCoveredByBTI(CallInst, *II)); BC->MIB->insertBTI(*BB, CallInst); II = BB->begin(); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::J)); } TEST_P(MCPlusBuilderTester, AArch64_insertBTI_4) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); MCInst Inst = MCInstBuilder(AArch64::RET).addReg(AArch64::LR); BB->addInstruction(Inst); // BLR needs BTI c, regardless of the register used. MCInst CallInst = MCInstBuilder(AArch64::BLR).addReg(AArch64::X5); auto II = BB->begin(); ASSERT_FALSE(BC->MIB->isCallCoveredByBTI(CallInst, *II)); BC->MIB->insertBTI(*BB, CallInst); II = BB->begin(); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::C)); } TEST_P(MCPlusBuilderTester, AArch64_insertBTI_5) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); MCInst BTIj; BC->MIB->createBTI(BTIj, BTIKind::J); BB->addInstruction(BTIj); // BLR needs BTI c, regardless of the register used. // We have a BTI j -> extend it to BTI jc. MCInst CallInst = MCInstBuilder(AArch64::BLR).addReg(AArch64::X5); auto II = BB->begin(); ASSERT_FALSE(BC->MIB->isCallCoveredByBTI(CallInst, *II)); BC->MIB->insertBTI(*BB, CallInst); II = BB->begin(); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::JC)); } TEST_P(MCPlusBuilderTester, AArch64_insertBTI_6) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); MCInst Paciasp = MCInstBuilder(AArch64::PACIASP).addReg(AArch64::LR).addReg(AArch64::SP); BB->addInstruction(Paciasp); // PACI(AB)SP are implicit BTI c, no change needed. MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X17); auto II = BB->begin(); ASSERT_TRUE(BC->MIB->isCallCoveredByBTI(CallInst, *II)); BC->MIB->insertBTI(*BB, CallInst); II = BB->begin(); ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, BTIKind::C)); ASSERT_TRUE(BC->MIB->isPSignOnLR(*II)); } TEST_P(MCPlusBuilderTester, AArch64_CmpJNE) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true); std::unique_ptr BB = BF->createBasicBlock(); InstructionListType Instrs = BC->MIB->createCmpJNE(AArch64::X0, 2, BB->getLabel(), BC->Ctx.get()); BB->addInstructions(Instrs.begin(), Instrs.end()); BB->addSuccessor(BB.get()); auto II = BB->begin(); ASSERT_EQ(II->getOpcode(), AArch64::SUBSXri); ASSERT_EQ(II->getOperand(0).getReg(), AArch64::XZR); ASSERT_EQ(II->getOperand(1).getReg(), AArch64::X0); ASSERT_EQ(II->getOperand(2).getImm(), 2); ASSERT_EQ(II->getOperand(3).getImm(), 0); II++; ASSERT_EQ(II->getOpcode(), AArch64::Bcc); ASSERT_EQ(II->getOperand(0).getImm(), AArch64CC::NE); const MCSymbol *Label = BC->MIB->getTargetSymbol(*II, 1); ASSERT_EQ(Label, BB->getLabel()); } TEST_P(MCPlusBuilderTester, testAccessedRegsImplicitDef) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); // adds x0, x5, #42 MCInst Inst = MCInstBuilder(AArch64::ADDSXri) .addReg(AArch64::X0) .addReg(AArch64::X5) .addImm(42) .addImm(0); assertRegMask([&](BitVector &BV) { BC->MIB->getClobberedRegs(Inst, BV); }, {AArch64::NZCV, AArch64::W0, AArch64::X0, AArch64::W0_HI, AArch64::X0_X1_X2_X3_X4_X5_X6_X7, AArch64::W0_W1, AArch64::X0_X1}); assertRegMask( [&](BitVector &BV) { BC->MIB->getTouchedRegs(Inst, BV); }, {AArch64::NZCV, AArch64::W0, AArch64::W5, AArch64::X0, AArch64::X5, AArch64::W0_HI, AArch64::W5_HI, AArch64::X0_X1_X2_X3_X4_X5_X6_X7, AArch64::X2_X3_X4_X5_X6_X7_X8_X9, AArch64::X4_X5_X6_X7_X8_X9_X10_X11, AArch64::W0_W1, AArch64::W4_W5, AArch64::X0_X1, AArch64::X4_X5}); assertRegMask([&](BitVector &BV) { BC->MIB->getWrittenRegs(Inst, BV); }, {AArch64::NZCV, AArch64::W0, AArch64::X0, AArch64::W0_HI}); assertRegMask([&](BitVector &BV) { BC->MIB->getUsedRegs(Inst, BV); }, {AArch64::W5, AArch64::X5, AArch64::W5_HI}); assertRegMask([&](BitVector &BV) { BC->MIB->getSrcRegs(Inst, BV); }, {AArch64::W5, AArch64::X5, AArch64::W5_HI}); } TEST_P(MCPlusBuilderTester, testAccessedRegsImplicitUse) { if (GetParam() != Triple::aarch64) GTEST_SKIP(); // b.eq