[llvm] [BOLT] Identify indirect tail call (PR #121146)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 17 00:49:52 PST 2025


https://github.com/liusy58 updated https://github.com/llvm/llvm-project/pull/121146

>From 99dde038d99158a89d4541fa5642c5f3ac2d74ae Mon Sep 17 00:00:00 2001
From: Nicholas <45984215+liusy58 at users.noreply.github.com>
Date: Fri, 17 Jan 2025 09:48:17 +0800
Subject: [PATCH] [BOLT][AArch64]  Speedup `computeInstructionSize` (#121106)

AArch64 instructions have a fixed size 4 bytes, no need to compute.
---
 bolt/include/bolt/Core/BinaryContext.h        |  6 +++
 bolt/include/bolt/Core/MCPlusBuilder.h        | 10 ++++
 bolt/lib/Core/BinaryFunction.cpp              | 53 +++++++++++++++++++
 .../Target/AArch64/AArch64MCPlusBuilder.cpp   | 15 ++++++
 bolt/lib/Target/X86/X86MCPlusBuilder.cpp      | 11 ++++
 bolt/test/AArch64/indirect-tail-call.s        | 32 +++++++++++
 6 files changed, 127 insertions(+)
 create mode 100644 bolt/test/AArch64/indirect-tail-call.s

diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 115e59ca0697e5..94fe4aa8aa0e57 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -1363,6 +1363,12 @@ class BinaryContext {
     if (std::optional<uint32_t> Size = MIB->getSize(Inst))
       return *Size;
 
+    if (MIB->isPseudo(Inst))
+      return 0;
+
+    if (std::optional<uint32_t> Size = MIB->getInstructionSize(Inst))
+      return *Size;
+
     if (!Emitter)
       Emitter = this->MCE.get();
     SmallString<256> Code;
diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index 3634fed9757ceb..a38f17b2c81b09 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -610,6 +610,11 @@ class MCPlusBuilder {
 
   virtual bool isLeave(const MCInst &Inst) const { return false; }
 
+  virtual bool hasUseOrDefofSPOrFP(const MCInst &Inst) const {
+    llvm_unreachable("not implemented");
+    return false;
+  }
+
   virtual bool isADRP(const MCInst &Inst) const {
     llvm_unreachable("not implemented");
     return false;
@@ -1204,6 +1209,11 @@ class MCPlusBuilder {
   /// Get instruction size specified via annotation.
   std::optional<uint32_t> getSize(const MCInst &Inst) const;
 
+  /// Get target-specific instruction size.
+  virtual std::optional<uint32_t> getInstructionSize(const MCInst &Inst) const {
+    return std::nullopt;
+  }
+
   /// Set instruction size.
   void setSize(MCInst &Inst, uint32_t Size) const;
 
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index 1c5cd62a095b24..f44303b52c7a0b 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -1961,6 +1961,59 @@ bool BinaryFunction::postProcessIndirectBranches(
       bool IsEpilogue = llvm::any_of(BB, [&](const MCInst &Instr) {
         return BC.MIB->isLeave(Instr) || BC.MIB->isPop(Instr);
       });
+      if (BC.isAArch64()) {
+        // Any adr instruction of aarch64 will generate a new entry,
+        // Adr instruction cannt afford to do any optimizations
+        if (!IsEpilogue && !isMultiEntry()) {
+          BinaryBasicBlock::iterator LastDefCFAOffsetInstIter = BB.end();
+          // find the last OpDefCfaOffset 0 instruction.
+          for (BinaryBasicBlock::iterator Iter = BB.begin(); Iter != BB.end();
+               ++Iter) {
+            if (&*Iter == &Instr) {
+              break;
+            }
+            if (BC.MIB->isCFI(*Iter)) {
+              const MCCFIInstruction *CFIInst =
+                  BB.getParent()->getCFIFor(*Iter);
+              if ((CFIInst->getOperation() ==
+                   MCCFIInstruction::OpDefCfaOffset) &&
+                  (CFIInst->getOffset() == 0)) {
+                LastDefCFAOffsetInstIter = Iter;
+                break;
+              }
+            }
+          }
+          if (LastDefCFAOffsetInstIter != BB.end()) {
+            IsEpilogue = true;
+            // make sure there is no instruction manipulating sp between the two
+            // instructions
+            BinaryBasicBlock::iterator Iter = LastDefCFAOffsetInstIter;
+            while (&*Iter != &Instr) {
+              if (BC.MIB->hasUseOrDefofSPOrFP(*Iter)) {
+                IsEpilogue = false;
+                break;
+              }
+              ++Iter;
+            }
+          }
+        }
+
+        if (!IsEpilogue) {
+          IsEpilogue = true;
+          BinaryFunction *Func = BB.getFunction();
+          for (const BinaryBasicBlock &BinaryBB : *Func) {
+            for (const MCInst &Inst : BinaryBB) {
+              if (BC.MIB->hasUseOrDefofSPOrFP(Inst)) {
+                IsEpilogue = false;
+                break;
+              }
+            }
+            if (!IsEpilogue) {
+              break;
+            }
+          }
+        }
+      }
       if (IsEpilogue) {
         BC.MIB->convertJmpToTailCall(Instr);
         BB.removeAllSuccessors();
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 679c9774c767f7..e6e79cd33605fc 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -1792,6 +1792,21 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
   }
 
   uint16_t getMinFunctionAlignment() const override { return 4; }
+
+  std::optional<uint32_t>
+  getInstructionSize(const MCInst &Inst) const override {
+    return 4;
+  }
+
+  bool hasUseOrDefofSPOrFP(const MCInst &Inst) const override {
+    if (isPseudo(Inst) || isNoop(Inst) || isCFI(Inst)) {
+      return false;
+    }
+    return hasDefOfPhysReg(Inst, AArch64::SP) ||
+           hasUseOfPhysReg(Inst, AArch64::SP) ||
+           hasDefOfPhysReg(Inst, AArch64::FP) ||
+           hasUseOfPhysReg(Inst, AArch64::FP);
+  }
 };
 
 } // end anonymous namespace
diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
index 63086c06d74fd9..ad998d8601e26f 100644
--- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
+++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
@@ -89,6 +89,17 @@ class X86MCPlusBuilder : public MCPlusBuilder {
 public:
   using MCPlusBuilder::MCPlusBuilder;
 
+  virtual bool hasUseOrDefofSPOrFP(const MCInst &Inst) const override {
+    bool IsLoad, IsStore, IsStoreFromReg, IsSimple, IsIndexed;
+    MCPhysReg Reg;
+    int32_t SrcImm;
+    uint16_t StackPtrReg;
+    int64_t StackOffset;
+    uint8_t Size;
+    return isStackAccess(Inst, IsLoad, IsStore, IsStoreFromReg, Reg, SrcImm,
+                         StackPtrReg, StackOffset, Size, IsSimple, IsIndexed);
+  }
+
   std::unique_ptr<MCSymbolizer>
   createTargetSymbolizer(BinaryFunction &Function,
                          bool CreateNewSymbols) const override {
diff --git a/bolt/test/AArch64/indirect-tail-call.s b/bolt/test/AArch64/indirect-tail-call.s
new file mode 100644
index 00000000000000..2a928bd81ee35f
--- /dev/null
+++ b/bolt/test/AArch64/indirect-tail-call.s
@@ -0,0 +1,32 @@
+## This test checks that inline is properly handled by BOLT on aarch64.
+
+# REQUIRES: system-linux
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags -O0 %t.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt --print-all --print-only=indirect  \
+# RUN: %t.exe -o %t.bolt | FileCheck %s
+
+#CHECK: Binary Function "indirect" after building cfg {
+#CHECK-NOT: # UNKNOWN CONTROL FLOW
+#CHECK: End of Function "indirect"
+	.text
+	.globl	indirect                       
+	.type	indirect, at function
+indirect:      
+    cbz	x0, .LBB0_2                         
+	ldr	x8, [x0]
+	ldr	x1, [x8]
+	br	x1
+.LBB0_2:
+	mov	w0, #3
+	ret
+	.size	indirect, .-indirect
+
+
+	.globl	main                            
+	.type	main, at function
+main:                                   
+	mov	w0, wzr
+	ret
+	.size	main, .-main



More information about the llvm-commits mailing list