llvm-project/bolt/unittests/Passes/LivenessAnalysis.cpp
Alexandros Lamprineas 64b728128d
[BOLT][AArch64] Add minimal support for liveness analysis. (#183298)
In this patch I am adding the missing target hooks required for the
liveness analysis to run on AArch64. These are
 - getFlagsReg()
 - getRegsUsedAsParams()
 - getDefaultLiveOut()
 - getGPRegs()
 - isCleanRegXOR()

I am also introducing the following API in LivenessAnalysis
 - BitVector getLiveIn/Out(const MCInst &)
 - MCPhysReg scavengeRegFromState(BitVector &)
 
My intention is to allow the LongJmp pass scavenge usable registers when
injecting code.
2026-04-02 11:59:59 +01:00

189 lines
6.9 KiB
C++

//===- bolt/unittest/Passes/LivenessAnalysis.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/AArch64MCTargetDesc.h"
#endif // AARCH64_AVAILABLE
#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/BinaryFunction.h"
#include "bolt/Core/BinaryFunctionCallGraph.h"
#include "bolt/Passes/DataflowInfoManager.h"
#include "bolt/Passes/RegAnalysis.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 opts {
extern cl::opt<bool> AssumeABI;
} // namespace opts
namespace {
struct LivenessAnalysisTester
: public testing::TestWithParam<Triple::ArchType> {
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<typename ELF64LE::Ehdr *>(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<orc::SymbolStringPool>(),
ObjFile->getFileName(), nullptr, true, DWARFContext::create(*ObjFile),
{llvm::outs(), llvm::errs()}));
ASSERT_FALSE(!BC);
BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(
createMCPlusBuilder(GetParam(), BC->MIA.get(), BC->MII.get(),
BC->MRI.get(), BC->STI.get())));
}
char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
std::unique_ptr<ObjectFile> ObjFile;
std::unique_ptr<BinaryContext> BC;
};
} // namespace
#ifdef AARCH64_AVAILABLE
INSTANTIATE_TEST_SUITE_P(AArch64, LivenessAnalysisTester,
::testing::Values(Triple::aarch64));
TEST_P(LivenessAnalysisTester, AArch64_scavengeRegFromState) {
if (GetParam() != Triple::aarch64)
GTEST_SKIP();
opts::AssumeABI = true;
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
BinaryBasicBlock *EntryBB = BF->addBasicBlock();
BinaryBasicBlock *FallThroughBB = BF->addBasicBlock();
BinaryBasicBlock *TargetBB = BF->addBasicBlock();
BF->addEntryPoint(*EntryBB);
EntryBB->addSuccessor(FallThroughBB);
EntryBB->addSuccessor(TargetBB);
FallThroughBB->addSuccessor(TargetBB);
EntryBB->setCFIState(0);
FallThroughBB->setCFIState(0);
TargetBB->setCFIState(0);
// mov x8, #1
MCInst MOVZXi =
MCInstBuilder(AArch64::MOVZXi).addReg(AArch64::X8).addImm(1).addImm(0);
// cbgt x0, #0, target
MCInst CBGTXri = MCInstBuilder(AArch64::CBGTXri)
.addReg(AArch64::X0)
.addImm(0)
.addExpr(MCSymbolRefExpr::create(TargetBB->getLabel(),
*BC->Ctx.get()));
// add x0, x8, #1
MCInst ADDXri = MCInstBuilder(AArch64::ADDXri)
.addReg(AArch64::X0)
.addReg(AArch64::X8)
.addImm(1)
.addImm(0);
// ret
MCInst RET = MCInstBuilder(AArch64::RET).addReg(AArch64::LR);
EntryBB->addInstruction(MOVZXi);
EntryBB->addInstruction(CBGTXri);
FallThroughBB->addInstruction(ADDXri);
TargetBB->addInstruction(RET);
BinaryFunctionCallGraph CG(buildCallGraph(*BC));
RegAnalysis RA(*BC, &BC->getBinaryFunctions(), &CG);
DataflowInfoManager Info(*BF, &RA, nullptr);
auto II = EntryBB->begin();
// Test that parameter registers are LiveIn.
BitVector ParamRegs = BC->MIB->getRegsUsedAsParams();
ASSERT_TRUE(ParamRegs.subsetOf(Info.getLivenessAnalysis().getLiveIn(*II)));
BitVector LiveIn, LiveOut;
// mov x8, #1 -> LiveIn = {x0}, LiveOut = {x0, x8}
LiveIn = Info.getLivenessAnalysis().getLiveIn(*II);
LiveOut = Info.getLivenessAnalysis().getLiveOut(*II);
ASSERT_TRUE(LiveIn.test(AArch64::X0));
ASSERT_FALSE(LiveIn.test(AArch64::X8));
ASSERT_TRUE(LiveOut.test(AArch64::X0));
ASSERT_TRUE(LiveOut.test(AArch64::X8));
ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegFromState(LiveOut),
AArch64::X9);
II++;
// cbgt x0, #0, target -> LiveIn = {x0, x8}, LiveOut = {x0, x8}
LiveIn = Info.getLivenessAnalysis().getLiveIn(*II);
LiveOut = Info.getLivenessAnalysis().getLiveOut(*II);
ASSERT_TRUE(LiveIn.test(AArch64::X0));
ASSERT_TRUE(LiveIn.test(AArch64::X8));
ASSERT_TRUE(LiveOut.test(AArch64::X0));
ASSERT_TRUE(LiveOut.test(AArch64::X8));
ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegFromState(LiveOut),
AArch64::X9);
II = FallThroughBB->begin();
// add x0, x8, #1 -> LiveIn = {x8}, LiveOut = {x0}
LiveIn = Info.getLivenessAnalysis().getLiveIn(*II);
LiveOut = Info.getLivenessAnalysis().getLiveOut(*II);
ASSERT_FALSE(LiveIn.test(AArch64::X0));
ASSERT_TRUE(LiveIn.test(AArch64::X8));
ASSERT_TRUE(LiveOut.test(AArch64::X0));
ASSERT_FALSE(LiveOut.test(AArch64::X8));
ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegFromState(LiveOut),
AArch64::X8);
II = TargetBB->begin();
// ret -> LiveIn = {x0}, LiveOut = {x0}
LiveIn = Info.getLivenessAnalysis().getLiveIn(*II);
LiveOut = Info.getLivenessAnalysis().getLiveOut(*II);
ASSERT_TRUE(LiveIn.test(AArch64::X0));
ASSERT_FALSE(LiveIn.test(AArch64::X8));
ASSERT_TRUE(LiveOut.test(AArch64::X0));
ASSERT_FALSE(LiveOut.test(AArch64::X8));
ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegFromState(LiveOut),
AArch64::X8);
// Test that return registers are LiveOut.
BitVector DefaultLiveOutRegs;
BC->MIB->getDefaultLiveOut(DefaultLiveOutRegs);
ASSERT_TRUE(
DefaultLiveOutRegs.subsetOf(Info.getLivenessAnalysis().getLiveOut(*II)));
}
#endif // AARCH64_AVAILABLE