[llvm] [BOLT][AArch64] Add minimal support for liveness analysis. (PR #183298)

Alexandros Lamprineas via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 10 06:13:30 PDT 2026


https://github.com/labrinea updated https://github.com/llvm/llvm-project/pull/183298

>From e0c6d591966a1f9f56d91f1467b165600db01256 Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Tue, 24 Feb 2026 18:18:42 +0000
Subject: [PATCH 1/7] [BOLT][AArch64] Add minimal support for liveness
 analysis.

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()

While doing this I found that LivenessAnalysis::scavengeRegAfter()
is using the LiveIn state instead of the LiveOut state that ought
to do since it is a backwards dataflow analysis. The state before
a given program point actually means "after" in control flow terms.

I have added unittests to cover all the above.

This effort will allow us to scavenge dead registers when emitting
instruction sequences which may clobber live state. An example
use case is to emit the adrp sequence in LongJmp without blindly
using the IP register.
---
 bolt/include/bolt/Core/MCPlusBuilder.h        |   2 +-
 bolt/include/bolt/Passes/LivenessAnalysis.h   |  16 +-
 .../Target/AArch64/AArch64MCPlusBuilder.cpp   | 113 ++++++++++++
 bolt/unittests/Core/MCPlusBuilder.cpp         |  31 ++++
 bolt/unittests/Passes/CMakeLists.txt          |   1 +
 bolt/unittests/Passes/LivenessAnalysis.cpp    | 164 ++++++++++++++++++
 6 files changed, 324 insertions(+), 3 deletions(-)
 create mode 100644 bolt/unittests/Passes/LivenessAnalysis.cpp

diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index a672d7a456896..75d58609e64f4 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -1595,7 +1595,7 @@ class MCPlusBuilder {
     llvm_unreachable("not implemented");
   }
 
-  /// Similar to getDefaultDefIn
+  /// Registers which may contain a meaningful value after a function returns.
   virtual void getDefaultLiveOut(BitVector &Regs) const {
     llvm_unreachable("not implemented");
   }
diff --git a/bolt/include/bolt/Passes/LivenessAnalysis.h b/bolt/include/bolt/Passes/LivenessAnalysis.h
index f4faa1dc34ecd..7545edb7e898e 100644
--- a/bolt/include/bolt/Passes/LivenessAnalysis.h
+++ b/bolt/include/bolt/Passes/LivenessAnalysis.h
@@ -36,8 +36,20 @@ class LivenessAnalysis : public DataflowAnalysis<LivenessAnalysis, BitVector,
         NumRegs(BF.getBinaryContext().MRI->getNumRegs()) {}
   virtual ~LivenessAnalysis();
 
+  // Liveness analysis is a backwards dataflow analysis, therefore
+  // getStateAt ProgramPoint actually means "before" in control
+  // flow terms.
+  BitVector getLiveIn(ProgramPoint P) const { return *this->getStateAt(P); }
+
+  // Liveness analysis is a backwards dataflow analysis, therefore
+  // getStateBefore ProgramPoint actually means "after" in control
+  // flow terms.
+  BitVector getLiveOut(ProgramPoint P) const {
+    return *this->getStateBefore(P);
+  }
+
   bool isAlive(ProgramPoint PP, MCPhysReg Reg) const {
-    const BitVector &BV = *this->getStateAt(PP);
+    const BitVector &BV = getLiveIn(PP);
     const BitVector &RegAliases = BC.MIB->getAliases(Reg);
     return BV.anyCommon(RegAliases);
   }
@@ -47,7 +59,7 @@ class LivenessAnalysis : public DataflowAnalysis<LivenessAnalysis, BitVector,
   // Return a usable general-purpose reg after point P. Return 0 if no reg is
   // available.
   MCPhysReg scavengeRegAfter(ProgramPoint P) {
-    BitVector BV = *this->getStateAt(P);
+    BitVector BV = getLiveOut(P);
     BV.flip();
     BitVector GPRegs(NumRegs, false);
     this->BC.MIB->getGPRegs(GPRegs, /*IncludeAlias=*/false);
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 3955ff378be41..260bb0d0b53ae 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -158,6 +158,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
 
   MCPhysReg getStackPointer() const override { return AArch64::SP; }
   MCPhysReg getFramePointer() const override { return AArch64::FP; }
+  MCPhysReg getFlagsReg() const override { return AArch64::NZCV; }
 
   bool isBreakpoint(const MCInst &Inst) const override {
     return Inst.getOpcode() == AArch64::BRK;
@@ -1238,6 +1239,19 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
     return true;
   }
 
+  BitVector getRegsUsedAsParams() const override {
+    BitVector Regs = BitVector(RegInfo->getNumRegs(), false);
+    Regs |= getAliases(AArch64::X0);
+    Regs |= getAliases(AArch64::X1);
+    Regs |= getAliases(AArch64::X2);
+    Regs |= getAliases(AArch64::X3);
+    Regs |= getAliases(AArch64::X4);
+    Regs |= getAliases(AArch64::X5);
+    Regs |= getAliases(AArch64::X6);
+    Regs |= getAliases(AArch64::X7);
+    return Regs;
+  }
+
   void getCalleeSavedRegs(BitVector &Regs) const override {
     Regs |= getAliases(AArch64::X18);
     Regs |= getAliases(AArch64::X19);
@@ -1254,6 +1268,86 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
     Regs |= getAliases(AArch64::FP);
   }
 
+  void getDefaultLiveOut(BitVector &Regs) const override {
+    getCalleeSavedRegs(Regs);
+    Regs |= getAliases(AArch64::X0);
+    Regs |= getAliases(AArch64::X1);
+    Regs |= getAliases(AArch64::X2);
+    Regs |= getAliases(AArch64::X3);
+    Regs |= getAliases(AArch64::X4);
+    Regs |= getAliases(AArch64::X5);
+    Regs |= getAliases(AArch64::X6);
+    Regs |= getAliases(AArch64::X7);
+  }
+
+  void getGPRegs(BitVector &Regs, bool IncludeAlias = true) const override {
+    if (IncludeAlias) {
+      Regs |= getAliases(AArch64::X0);
+      Regs |= getAliases(AArch64::X1);
+      Regs |= getAliases(AArch64::X2);
+      Regs |= getAliases(AArch64::X3);
+      Regs |= getAliases(AArch64::X4);
+      Regs |= getAliases(AArch64::X5);
+      Regs |= getAliases(AArch64::X6);
+      Regs |= getAliases(AArch64::X7);
+      Regs |= getAliases(AArch64::X8);
+      Regs |= getAliases(AArch64::X9);
+      Regs |= getAliases(AArch64::X10);
+      Regs |= getAliases(AArch64::X11);
+      Regs |= getAliases(AArch64::X12);
+      Regs |= getAliases(AArch64::X13);
+      Regs |= getAliases(AArch64::X14);
+      Regs |= getAliases(AArch64::X15);
+      Regs |= getAliases(AArch64::X16);
+      Regs |= getAliases(AArch64::X17);
+      Regs |= getAliases(AArch64::X18);
+      Regs |= getAliases(AArch64::X19);
+      Regs |= getAliases(AArch64::X20);
+      Regs |= getAliases(AArch64::X21);
+      Regs |= getAliases(AArch64::X22);
+      Regs |= getAliases(AArch64::X23);
+      Regs |= getAliases(AArch64::X24);
+      Regs |= getAliases(AArch64::X25);
+      Regs |= getAliases(AArch64::X26);
+      Regs |= getAliases(AArch64::X27);
+      Regs |= getAliases(AArch64::X28);
+      Regs |= getAliases(AArch64::LR);
+      Regs |= getAliases(AArch64::FP);
+      return;
+    }
+    Regs.set(AArch64::X0);
+    Regs.set(AArch64::X1);
+    Regs.set(AArch64::X2);
+    Regs.set(AArch64::X3);
+    Regs.set(AArch64::X4);
+    Regs.set(AArch64::X5);
+    Regs.set(AArch64::X6);
+    Regs.set(AArch64::X7);
+    Regs.set(AArch64::X8);
+    Regs.set(AArch64::X9);
+    Regs.set(AArch64::X10);
+    Regs.set(AArch64::X11);
+    Regs.set(AArch64::X12);
+    Regs.set(AArch64::X13);
+    Regs.set(AArch64::X14);
+    Regs.set(AArch64::X15);
+    Regs.set(AArch64::X16);
+    Regs.set(AArch64::X17);
+    Regs.set(AArch64::X18);
+    Regs.set(AArch64::X19);
+    Regs.set(AArch64::X20);
+    Regs.set(AArch64::X21);
+    Regs.set(AArch64::X22);
+    Regs.set(AArch64::X23);
+    Regs.set(AArch64::X24);
+    Regs.set(AArch64::X25);
+    Regs.set(AArch64::X26);
+    Regs.set(AArch64::X27);
+    Regs.set(AArch64::X28);
+    Regs.set(AArch64::LR);
+    Regs.set(AArch64::FP);
+  }
+
   const MCExpr *getTargetExprFor(MCInst &Inst, const MCExpr *Expr,
                                  MCContext &Ctx,
                                  uint32_t RelType) const override {
@@ -2412,6 +2506,25 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
            isAArch64ExclusiveStore(Inst);
   }
 
+  bool isCleanRegXOR(const MCInst &Inst) const override {
+    switch (Inst.getOpcode()) {
+    case AArch64::ORRXrs:
+      return Inst.getOperand(1).getReg() == AArch64::XZR &&
+             Inst.getOperand(2).getReg() == AArch64::XZR &&
+             Inst.getOperand(3).getImm() == 0;
+    case AArch64::ORRWrs:
+      return Inst.getOperand(1).getReg() == AArch64::WZR &&
+             Inst.getOperand(2).getReg() == AArch64::WZR &&
+             Inst.getOperand(3).getImm() == 0;
+    case AArch64::MOVZXi:
+    case AArch64::MOVZWi:
+      return Inst.getOperand(1).getImm() == 0 &&
+             Inst.getOperand(2).getImm() == 0;
+    default:
+      return false;
+    }
+  }
+
   bool isStoreToStack(const MCInst &Inst) const {
     if (!mayStore(Inst))
       return false;
diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp
index a8d25f3323b38..a20a6d6deb435 100644
--- a/bolt/unittests/Core/MCPlusBuilder.cpp
+++ b/bolt/unittests/Core/MCPlusBuilder.cpp
@@ -524,6 +524,37 @@ TEST_P(MCPlusBuilderTester, AArch64_Psign_Pauth_variants) {
   ASSERT_TRUE(BC->MIB->isPAuthAndRet(Retab));
 }
 
+TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
+  if (GetParam() != Triple::aarch64)
+    GTEST_SKIP();
+
+  // mov x0, xzr
+  MCInst ORRXrs = MCInstBuilder(AArch64::ORRXrs)
+                      .addReg(AArch64::X0)
+                      .addReg(AArch64::XZR)
+                      .addReg(AArch64::XZR)
+                      .addImm(0);
+  ASSERT_TRUE(BC->MIB->isCleanRegXOR(ORRXrs));
+
+  // mov w0, wzr
+  MCInst ORRWrs = MCInstBuilder(AArch64::ORRWrs)
+                      .addReg(AArch64::W0)
+                      .addReg(AArch64::WZR)
+                      .addReg(AArch64::WZR)
+                      .addImm(0);
+  ASSERT_TRUE(BC->MIB->isCleanRegXOR(ORRWrs));
+
+  // mov x0, #0
+  MCInst MOVZXi =
+      MCInstBuilder(AArch64::MOVZXi).addReg(AArch64::X0).addImm(0).addImm(0);
+  ASSERT_TRUE(BC->MIB->isCleanRegXOR(MOVZXi));
+
+  // mov w0, #0
+  MCInst MOVZWi =
+      MCInstBuilder(AArch64::MOVZWi).addReg(AArch64::W0).addImm(0).addImm(0);
+  ASSERT_TRUE(BC->MIB->isCleanRegXOR(MOVZWi));
+}
+
 #endif // AARCH64_AVAILABLE
 
 #ifdef X86_AVAILABLE
diff --git a/bolt/unittests/Passes/CMakeLists.txt b/bolt/unittests/Passes/CMakeLists.txt
index 17ae8802f6953..bd373cd0006f6 100644
--- a/bolt/unittests/Passes/CMakeLists.txt
+++ b/bolt/unittests/Passes/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_bolt_unittest(PassTests
+  LivenessAnalysis.cpp
   PointerAuthCFIFixup.cpp
 
   DISABLE_LLVM_LINK_LLVM_DYLIB
diff --git a/bolt/unittests/Passes/LivenessAnalysis.cpp b/bolt/unittests/Passes/LivenessAnalysis.cpp
new file mode 100644
index 0000000000000..f30a6410075bd
--- /dev/null
+++ b/bolt/unittests/Passes/LivenessAnalysis.cpp
@@ -0,0 +1,164 @@
+//===- 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/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_scavengeRegAfter) {
+  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();
+  // mov x8, #1 -> LiveIn = {x0}, LiveOut = {x0, x8}
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X0));
+  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X8));
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X0));
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
+  ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::X9);
+  II++;
+  // cbgt x0, #0, target -> LiveIn = {x0, x8}, LiveOut = {x0, x8}
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X0));
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X8));
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X0));
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
+  ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::X9);
+  II = FallThroughBB->begin();
+  // add x0, x8, #1 -> LiveIn = {x8}, LiveOut = {x0}
+  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X0));
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X8));
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X0));
+  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
+  ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::X8);
+  II = TargetBB->begin();
+  // ret -> LiveIn = {x0}, LiveOut = {x0}
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X0));
+  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X8));
+  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X0));
+  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
+  ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::X8);
+}
+
+#endif // AARCH64_AVAILABLE

>From 798f79741f9fd670b7bcd29c8d50abb20b257852 Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Thu, 26 Feb 2026 14:41:50 +0000
Subject: [PATCH 2/7] fix boilerplate comment in new test file fix crash when
 MCInst has expression instead of immediate add more tests for param regs and
 default live out regs

---
 bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp |  2 +-
 bolt/unittests/Core/MCPlusBuilder.cpp            | 13 +++++++++++++
 bolt/unittests/Passes/LivenessAnalysis.cpp       | 13 ++++++++++++-
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 260bb0d0b53ae..12793df7ef95f 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -2518,7 +2518,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
              Inst.getOperand(3).getImm() == 0;
     case AArch64::MOVZXi:
     case AArch64::MOVZWi:
-      return Inst.getOperand(1).getImm() == 0 &&
+      return Inst.getOperand(1).isImm() && Inst.getOperand(1).getImm() == 0 &&
              Inst.getOperand(2).getImm() == 0;
     default:
       return false;
diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp
index a20a6d6deb435..f84c6cc09eec5 100644
--- a/bolt/unittests/Core/MCPlusBuilder.cpp
+++ b/bolt/unittests/Core/MCPlusBuilder.cpp
@@ -8,6 +8,7 @@
 
 #ifdef AARCH64_AVAILABLE
 #include "AArch64Subtarget.h"
+#include "MCTargetDesc/AArch64MCAsmInfo.h"
 #include "MCTargetDesc/AArch64MCTargetDesc.h"
 #endif // AARCH64_AVAILABLE
 
@@ -528,6 +529,9 @@ TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
   if (GetParam() != Triple::aarch64)
     GTEST_SKIP();
 
+  BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
+  BinaryBasicBlock *BB = BF->addBasicBlock();
+
   // mov x0, xzr
   MCInst ORRXrs = MCInstBuilder(AArch64::ORRXrs)
                       .addReg(AArch64::X0)
@@ -553,6 +557,15 @@ TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
   MCInst MOVZWi =
       MCInstBuilder(AArch64::MOVZWi).addReg(AArch64::W0).addImm(0).addImm(0);
   ASSERT_TRUE(BC->MIB->isCleanRegXOR(MOVZWi));
+
+  // movz x0, #:abs_g3:symbol
+  MCInst MOVZXiWithExpr =
+      MCInstBuilder(AArch64::MOVZXi)
+          .addReg(AArch64::X0)
+          .addExpr(MCSpecifierExpr::create(BB->getLabel(), AArch64::S_ABS_G3,
+                                           *BC->Ctx.get()))
+          .addImm(48);
+  ASSERT_FALSE(BC->MIB->isCleanRegXOR(MOVZXiWithExpr));
 }
 
 #endif // AARCH64_AVAILABLE
diff --git a/bolt/unittests/Passes/LivenessAnalysis.cpp b/bolt/unittests/Passes/LivenessAnalysis.cpp
index f30a6410075bd..4e42745c98600 100644
--- a/bolt/unittests/Passes/LivenessAnalysis.cpp
+++ b/bolt/unittests/Passes/LivenessAnalysis.cpp
@@ -1,4 +1,4 @@
-//===- bolt/unittest/Core/MCPlusBuilder.cpp -------------------------------===//
+//===- 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.
@@ -132,6 +132,11 @@ TEST_P(LivenessAnalysisTester, AArch64_scavengeRegAfter) {
   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)));
+
   // mov x8, #1 -> LiveIn = {x0}, LiveOut = {x0, x8}
   ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X0));
   ASSERT_FALSE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X8));
@@ -159,6 +164,12 @@ TEST_P(LivenessAnalysisTester, AArch64_scavengeRegAfter) {
   ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X0));
   ASSERT_FALSE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
   ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::X8);
+
+  // Test that callee saved registers and return registers are LiveOut.
+  BitVector DefaultLiveOutRegs;
+  BC->MIB->getDefaultLiveOut(DefaultLiveOutRegs);
+  ASSERT_TRUE(
+      DefaultLiveOutRegs.subsetOf(Info.getLivenessAnalysis().getLiveOut(&*II)));
 }
 
 #endif // AARCH64_AVAILABLE

>From 7ae58bfd1b5e872abc25703b58287e03a502279b Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Fri, 27 Feb 2026 14:06:17 +0000
Subject: [PATCH 3/7] remove redundant invocation of getCalleeSavedRegs from
 getDefaultLiveOut since it gets called from getStartingStateAtBB anyway

---
 bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 12793df7ef95f..92c105fcc4c23 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -1269,7 +1269,6 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
   }
 
   void getDefaultLiveOut(BitVector &Regs) const override {
-    getCalleeSavedRegs(Regs);
     Regs |= getAliases(AArch64::X0);
     Regs |= getAliases(AArch64::X1);
     Regs |= getAliases(AArch64::X2);

>From d84f1eeac83241990255b3dbafc289d781790ec2 Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Fri, 27 Feb 2026 15:26:21 +0000
Subject: [PATCH 4/7] update comment in unittest

---
 bolt/unittests/Passes/LivenessAnalysis.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bolt/unittests/Passes/LivenessAnalysis.cpp b/bolt/unittests/Passes/LivenessAnalysis.cpp
index 4e42745c98600..914429baf7e22 100644
--- a/bolt/unittests/Passes/LivenessAnalysis.cpp
+++ b/bolt/unittests/Passes/LivenessAnalysis.cpp
@@ -165,7 +165,7 @@ TEST_P(LivenessAnalysisTester, AArch64_scavengeRegAfter) {
   ASSERT_FALSE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
   ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::X8);
 
-  // Test that callee saved registers and return registers are LiveOut.
+  // Test that return registers are LiveOut.
   BitVector DefaultLiveOutRegs;
   BC->MIB->getDefaultLiveOut(DefaultLiveOutRegs);
   ASSERT_TRUE(

>From 41e54185e0cfda4216c7c600bf2090c085789dee Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Fri, 27 Feb 2026 16:12:44 +0000
Subject: [PATCH 5/7] adds removeNonScavengeableRegs to exlude FP and LR from
 scavenging handles exclusive-or-register form in isCleanRegXOR

---
 bolt/include/bolt/Core/MCPlusBuilder.h           |  5 +++++
 bolt/include/bolt/Passes/LivenessAnalysis.h      |  8 +++-----
 bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp | 11 +++++++++++
 bolt/lib/Target/X86/X86MCPlusBuilder.cpp         |  6 ++++++
 bolt/unittests/Core/MCPlusBuilder.cpp            | 16 ++++++++++++++++
 5 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index 75d58609e64f4..0f6076688b65d 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -1605,6 +1605,11 @@ class MCPlusBuilder {
     llvm_unreachable("not implemented");
   }
 
+  /// Remove non scavengeable special registers from \p Regs
+  virtual void removeNonScavengeableRegs(BitVector &Regs) const {
+    llvm_unreachable("not implemented");
+  }
+
   /// Change \p Regs with a bitmask with all general purpose regs that can be
   /// encoded without extra prefix bytes. For x86 only.
   virtual void getClassicGPRegs(BitVector &Regs) const {
diff --git a/bolt/include/bolt/Passes/LivenessAnalysis.h b/bolt/include/bolt/Passes/LivenessAnalysis.h
index 7545edb7e898e..bfcf68f79b144 100644
--- a/bolt/include/bolt/Passes/LivenessAnalysis.h
+++ b/bolt/include/bolt/Passes/LivenessAnalysis.h
@@ -63,12 +63,10 @@ class LivenessAnalysis : public DataflowAnalysis<LivenessAnalysis, BitVector,
     BV.flip();
     BitVector GPRegs(NumRegs, false);
     this->BC.MIB->getGPRegs(GPRegs, /*IncludeAlias=*/false);
-    // Ignore the register used for frame pointer even if it is not alive (it
-    // may be used by CFI which is not represented in our dataflow).
-    BitVector FP = BC.MIB->getAliases(BC.MIB->getFramePointer());
-    FP.flip();
     BV &= GPRegs;
-    BV &= FP;
+    // Ignore target-specific special registers even if they are dead
+    // (they may be used by CFI which is not represented in our dataflow).
+    BC.MIB->removeNonScavengeableRegs(BV);
     int Reg = BV.find_first();
     return Reg != -1 ? Reg : 0;
   }
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 92c105fcc4c23..98da3e628dc86 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -1347,6 +1347,13 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
     Regs.set(AArch64::FP);
   }
 
+  void removeNonScavengeableRegs(BitVector &Regs) const override {
+    BitVector ExclusionMask = getAliases(AArch64::LR);
+    ExclusionMask |= getAliases(AArch64::FP);
+    ExclusionMask.flip();
+    Regs &= ExclusionMask;
+  }
+
   const MCExpr *getTargetExprFor(MCInst &Inst, const MCExpr *Expr,
                                  MCContext &Ctx,
                                  uint32_t RelType) const override {
@@ -2507,6 +2514,10 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
 
   bool isCleanRegXOR(const MCInst &Inst) const override {
     switch (Inst.getOpcode()) {
+    case AArch64::EORXrs:
+    case AArch64::EORWrs:
+      return Inst.getOperand(1).getReg() == Inst.getOperand(2).getReg() &&
+             Inst.getOperand(3).getImm() == 0;
     case AArch64::ORRXrs:
       return Inst.getOperand(1).getReg() == AArch64::XZR &&
              Inst.getOperand(2).getReg() == AArch64::XZR &&
diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
index 51e7d27f18a0b..aa1a70e40669a 100644
--- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
+++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
@@ -827,6 +827,12 @@ class X86MCPlusBuilder : public MCPlusBuilder {
     Regs.set(X86::R15);
   }
 
+  void removeNonScavengeableRegs(BitVector &Regs) const override {
+    BitVector FP = getAliases(X86::RBP);
+    FP.flip();
+    Regs &= FP;
+  }
+
   void getClassicGPRegs(BitVector &Regs) const override {
     Regs |= getAliases(X86::RAX);
     Regs |= getAliases(X86::RBX);
diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp
index f84c6cc09eec5..feb6f57dd2566 100644
--- a/bolt/unittests/Core/MCPlusBuilder.cpp
+++ b/bolt/unittests/Core/MCPlusBuilder.cpp
@@ -532,6 +532,22 @@ TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
   BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
   BinaryBasicBlock *BB = BF->addBasicBlock();
 
+  // eor x0, x0, x0
+  MCInst EORXrs = MCInstBuilder(AArch64::EORXrs)
+                      .addReg(AArch64::X0)
+                      .addReg(AArch64::X0)
+                      .addReg(AArch64::X0)
+                      .addImm(0);
+  ASSERT_TRUE(BC->MIB->isCleanRegXOR(EORXrs));
+
+  // eor w0, w0, w0
+  MCInst EORWrs = MCInstBuilder(AArch64::EORWrs)
+                      .addReg(AArch64::W0)
+                      .addReg(AArch64::W0)
+                      .addReg(AArch64::W0)
+                      .addImm(0);
+  ASSERT_TRUE(BC->MIB->isCleanRegXOR(EORWrs));
+
   // mov x0, xzr
   MCInst ORRXrs = MCInstBuilder(AArch64::ORRXrs)
                       .addReg(AArch64::X0)

>From 9be6789de0c78174213941738bfa9381099031df Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Sun, 1 Mar 2026 21:13:47 +0000
Subject: [PATCH 6/7] reverts scavengeRegAfter into using getStateAt introduces
 scavengeRegFromState(BitVector &LiveRegs) changes getLiveIn/Out to only
 accept MCInst as ProgramPoint

---
 bolt/include/bolt/Passes/LivenessAnalysis.h | 38 +++++++------
 bolt/unittests/Passes/LivenessAnalysis.cpp  | 59 +++++++++++++--------
 2 files changed, 57 insertions(+), 40 deletions(-)

diff --git a/bolt/include/bolt/Passes/LivenessAnalysis.h b/bolt/include/bolt/Passes/LivenessAnalysis.h
index bfcf68f79b144..6fcb66957f028 100644
--- a/bolt/include/bolt/Passes/LivenessAnalysis.h
+++ b/bolt/include/bolt/Passes/LivenessAnalysis.h
@@ -36,20 +36,18 @@ class LivenessAnalysis : public DataflowAnalysis<LivenessAnalysis, BitVector,
         NumRegs(BF.getBinaryContext().MRI->getNumRegs()) {}
   virtual ~LivenessAnalysis();
 
-  // Liveness analysis is a backwards dataflow analysis, therefore
-  // getStateAt ProgramPoint actually means "before" in control
-  // flow terms.
-  BitVector getLiveIn(ProgramPoint P) const { return *this->getStateAt(P); }
-
-  // Liveness analysis is a backwards dataflow analysis, therefore
-  // getStateBefore ProgramPoint actually means "after" in control
-  // flow terms.
-  BitVector getLiveOut(ProgramPoint P) const {
-    return *this->getStateBefore(P);
+  // Return the state before the execution of an Instruction.
+  BitVector getLiveIn(const MCInst &Inst) const {
+    return *this->getStateAt(Inst);
+  }
+
+  // Return the state after the execution of an Instruction.
+  BitVector getLiveOut(const MCInst &Inst) const {
+    return *this->getStateBefore(Inst);
   }
 
   bool isAlive(ProgramPoint PP, MCPhysReg Reg) const {
-    const BitVector &BV = getLiveIn(PP);
+    const BitVector &BV = *this->getStateAt(PP);
     const BitVector &RegAliases = BC.MIB->getAliases(Reg);
     return BV.anyCommon(RegAliases);
   }
@@ -58,16 +56,22 @@ class LivenessAnalysis : public DataflowAnalysis<LivenessAnalysis, BitVector,
 
   // Return a usable general-purpose reg after point P. Return 0 if no reg is
   // available.
-  MCPhysReg scavengeRegAfter(ProgramPoint P) {
-    BitVector BV = getLiveOut(P);
-    BV.flip();
+  MCPhysReg scavengeRegAfter(ProgramPoint P) const {
+    BitVector BV = *this->getStateAt(P);
+    return scavengeRegFromState(BV);
+  }
+
+  // Return a usable general-purpose reg given a liveness state. Return 0 if
+  // no reg is available.
+  MCPhysReg scavengeRegFromState(BitVector &LiveRegs) const {
     BitVector GPRegs(NumRegs, false);
     this->BC.MIB->getGPRegs(GPRegs, /*IncludeAlias=*/false);
-    BV &= GPRegs;
+    LiveRegs.flip();
+    LiveRegs &= GPRegs;
     // Ignore target-specific special registers even if they are dead
     // (they may be used by CFI which is not represented in our dataflow).
-    BC.MIB->removeNonScavengeableRegs(BV);
-    int Reg = BV.find_first();
+    BC.MIB->removeNonScavengeableRegs(LiveRegs);
+    int Reg = LiveRegs.find_first();
     return Reg != -1 ? Reg : 0;
   }
 
diff --git a/bolt/unittests/Passes/LivenessAnalysis.cpp b/bolt/unittests/Passes/LivenessAnalysis.cpp
index 914429baf7e22..7ddf683a855ec 100644
--- a/bolt/unittests/Passes/LivenessAnalysis.cpp
+++ b/bolt/unittests/Passes/LivenessAnalysis.cpp
@@ -87,7 +87,7 @@ struct LivenessAnalysisTester
 INSTANTIATE_TEST_SUITE_P(AArch64, LivenessAnalysisTester,
                          ::testing::Values(Triple::aarch64));
 
-TEST_P(LivenessAnalysisTester, AArch64_scavengeRegAfter) {
+TEST_P(LivenessAnalysisTester, AArch64_scavengeRegFromState) {
   if (GetParam() != Triple::aarch64)
     GTEST_SKIP();
 
@@ -135,41 +135,54 @@ TEST_P(LivenessAnalysisTester, AArch64_scavengeRegAfter) {
 
   // Test that parameter registers are LiveIn.
   BitVector ParamRegs = BC->MIB->getRegsUsedAsParams();
-  ASSERT_TRUE(ParamRegs.subsetOf(Info.getLivenessAnalysis().getLiveIn(&*II)));
+  ASSERT_TRUE(ParamRegs.subsetOf(Info.getLivenessAnalysis().getLiveIn(*II)));
 
+  BitVector LiveIn, LiveOut;
   // mov x8, #1 -> LiveIn = {x0}, LiveOut = {x0, x8}
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X0));
-  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X8));
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X0));
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
-  ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::X9);
+  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}
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X0));
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X8));
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X0));
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
-  ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::X9);
+  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}
-  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X0));
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X8));
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X0));
-  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
-  ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::X8);
+  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}
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X0));
-  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveIn(&*II).test(AArch64::X8));
-  ASSERT_TRUE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X0));
-  ASSERT_FALSE(Info.getLivenessAnalysis().getLiveOut(&*II).test(AArch64::X8));
-  ASSERT_EQ(Info.getLivenessAnalysis().scavengeRegAfter(&*II), AArch64::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_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)));
+      DefaultLiveOutRegs.subsetOf(Info.getLivenessAnalysis().getLiveOut(*II)));
 }
 
 #endif // AARCH64_AVAILABLE

>From a994a76287de8e30244d85a2c1a485e020f39597 Mon Sep 17 00:00:00 2001
From: Alexandros Lamprineas <alexandros.lamprineas at arm.com>
Date: Tue, 10 Mar 2026 13:08:48 +0000
Subject: [PATCH 7/7] reuse param registers for default live out exclude
 platform register from scavenging

---
 bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 98da3e628dc86..b4c08cf1f6153 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -1269,14 +1269,9 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
   }
 
   void getDefaultLiveOut(BitVector &Regs) const override {
-    Regs |= getAliases(AArch64::X0);
-    Regs |= getAliases(AArch64::X1);
-    Regs |= getAliases(AArch64::X2);
-    Regs |= getAliases(AArch64::X3);
-    Regs |= getAliases(AArch64::X4);
-    Regs |= getAliases(AArch64::X5);
-    Regs |= getAliases(AArch64::X6);
-    Regs |= getAliases(AArch64::X7);
+    // According to the AArch64 ABI the return registers are X0 to X7,
+    // which happen to be the same as the parameter registers.
+    Regs |= getRegsUsedAsParams();
   }
 
   void getGPRegs(BitVector &Regs, bool IncludeAlias = true) const override {
@@ -1350,6 +1345,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
   void removeNonScavengeableRegs(BitVector &Regs) const override {
     BitVector ExclusionMask = getAliases(AArch64::LR);
     ExclusionMask |= getAliases(AArch64::FP);
+    ExclusionMask |= getAliases(AArch64::X18); // platform register
     ExclusionMask.flip();
     Regs &= ExclusionMask;
   }



More information about the llvm-commits mailing list