[llvm] [BOLT][ICP] Use PC-relative instructions for PIC binaries in ICP (PR #216587)

via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 16 10:50:44 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-bolt

Author: Jinjie Huang  (Jinjie-Huang)

<details>
<summary>Changes</summary>

Currently, BOLT x86 ICP optimization inserts an immediate-to-register comparison instruction, which forces the use of 32-bit signed (32S) relocations due to ABI limitations. This behavior does not align well with the conventions of PIC/PIE binaries and makes it highly prone to JITLink relocation overflow errors when dealing with large binaries.

Therefore, this patch introduces an additional lea instruction for the x86 architecture to leverage PC-relative (32PC) addressing. This new approach takes effect by default for PIC binaries or under specific options.

---
Full diff: https://github.com/llvm/llvm-project/pull/216587.diff


3 Files Affected:

- (modified) bolt/lib/Passes/IndirectCallPromotion.cpp (+5) 
- (modified) bolt/lib/Target/X86/X86MCPlusBuilder.cpp (+57-7) 
- (added) bolt/test/X86/icp-pic.s (+48) 


``````````diff
diff --git a/bolt/lib/Passes/IndirectCallPromotion.cpp b/bolt/lib/Passes/IndirectCallPromotion.cpp
index 39ae4cda145c4..ed67e8028a043 100644
--- a/bolt/lib/Passes/IndirectCallPromotion.cpp
+++ b/bolt/lib/Passes/IndirectCallPromotion.cpp
@@ -34,6 +34,7 @@ extern cl::OptionCategory BoltOptCategory;
 extern cl::opt<IndirectCallPromotionType> ICP;
 extern cl::opt<unsigned> Verbosity;
 extern cl::opt<unsigned> ExecutionCountThreshold;
+extern cl::opt<bool> X86ICPPIC;
 
 static cl::opt<unsigned> ICPJTRemainingPercentThreshold(
     "icp-jt-remaining-percent-threshold",
@@ -1497,6 +1498,10 @@ Error IndirectCallPromotion::runOnFunctions(BinaryContext &BC) {
                             std::max<uint64_t>(TotalIndexBasedCandidates, 1))
       << "%\n";
 
+  if (BC.isX86() && (!BC.HasFixedLoadAddress || opts::X86ICPPIC))
+    BC.outs() << "BOLT-INFO: ICP is using x86 PIC mode with PC-relative "
+                 "LEA target checks\n";
+
 #ifndef NDEBUG
   verifyProfile(BFs);
 #endif
diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
index 684bedacde3e9..d1d42df08437e 100644
--- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
+++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
@@ -21,6 +21,7 @@
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCInstBuilder.h"
 #include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
 #include "llvm/MC/MCRegister.h"
 #include "llvm/MC/MCRegisterInfo.h"
 #include "llvm/Support/CommandLine.h"
@@ -45,6 +46,11 @@ static cl::opt<bool> X86StripRedundantAddressSize(
     cl::desc("Remove redundant Address-Size override prefix"), cl::init(true),
     cl::cat(BoltOptCategory));
 
+cl::opt<bool>
+    X86ICPPIC("x86-icp-pic",
+              cl::desc("force x86 ICP to use PC-relative LEA target checks"),
+              cl::cat(BoltOptCategory));
+
 } // namespace opts
 
 namespace {
@@ -3298,6 +3304,9 @@ class X86MCPlusBuilder : public MCPlusBuilder {
       const bool MinimizeCodeSize, MCContext *Ctx) override {
     const bool IsTailCall = isTailCall(CallInst);
     const bool IsJumpTable = getJumpTable(CallInst) != 0;
+    const bool UsePIC =
+        !IsJumpTable &&
+        (Ctx->getObjectFileInfo()->isPositionIndependent() || opts::X86ICPPIC);
     BlocksVectorTy Results;
 
     // Label for the current code block.
@@ -3314,7 +3323,8 @@ class X86MCPlusBuilder : public MCPlusBuilder {
            "There must be a vtable entry for every method "
            "in the targets vector.");
 
-    if (MinimizeCodeSize && !LoadElim) {
+    // PIC mode uses an extra lea instruction and a register.
+    if (UsePIC || (MinimizeCodeSize && !LoadElim)) {
       std::set<unsigned> UsedRegs;
 
       for (unsigned int I = 0; I < MCPlus::getNumPrimeOperands(CallInst); ++I) {
@@ -3322,11 +3332,17 @@ class X86MCPlusBuilder : public MCPlusBuilder {
         if (Op.isReg())
           UsedRegs.insert(Op.getReg());
       }
-
-      if (UsedRegs.count(X86::R10) == 0)
-        FuncAddrReg = X86::R10;
-      else if (UsedRegs.count(X86::R11) == 0)
-        FuncAddrReg = X86::R11;
+      if (UsePIC)
+        for (const MCInst *Inst : MethodFetchInsns)
+          for (const MCOperand &Op : *Inst)
+            if (Op.isReg())
+              UsedRegs.insert(Op.getReg());
+      const MCPhysReg FirstReg = UsePIC ? X86::R11 : X86::R10;
+      const MCPhysReg SecondReg = UsePIC ? X86::R10 : X86::R11;
+      if (UsedRegs.count(FirstReg) == 0)
+        FuncAddrReg = FirstReg;
+      else if (UsedRegs.count(SecondReg) == 0)
+        FuncAddrReg = SecondReg;
       else
         return Results;
     }
@@ -3343,7 +3359,41 @@ class X86MCPlusBuilder : public MCPlusBuilder {
       Results.emplace_back(NextTarget, InstructionListType());
       InstructionListType *NewCall = &Results.back().second;
 
-      if (MinimizeCodeSize && !LoadElim) {
+      if (UsePIC) {
+        assert(Targets[i].first && "PIC ICP target must have a symbol");
+        const MCSymbol *Sym = LoadElim ? VtableSyms[i].first : Targets[i].first;
+        const uint64_t Addend = LoadElim ? VtableSyms[i].second : 0;
+
+        NewCall->push_back(CallInst);
+        MCInst &Target = NewCall->back();
+        Target.clear();
+        createLea(Target, Sym, FuncAddrReg, Ctx);
+        if (Addend) {
+          const MCExpr *Expr = MCBinaryExpr::createAdd(
+              MCSymbolRefExpr::create(Sym, *Ctx),
+              MCConstantExpr::create(Addend, *Ctx), *Ctx);
+          Target.getOperand(4) = MCOperand::createExpr(Expr);
+        }
+
+        NewCall->push_back(CallInst);
+        MCInst &Compare = NewCall->back();
+        Compare.clear();
+        if (isBranchOnReg(CallInst)) {
+          Compare.setOpcode(X86::CMP64rr);
+          for (unsigned I = 0;
+               I < Info->get(CallInst.getOpcode()).getNumOperands(); ++I)
+            if (!CallInst.getOperand(I).isInst())
+              Compare.addOperand(CallInst.getOperand(I));
+          Compare.addOperand(MCOperand::createReg(FuncAddrReg));
+        } else {
+          Compare.setOpcode(X86::CMP64mr);
+          for (unsigned I = 0;
+               I < Info->get(CallInst.getOpcode()).getNumOperands(); ++I)
+            if (!CallInst.getOperand(I).isInst())
+              Compare.addOperand(CallInst.getOperand(I));
+          Compare.addOperand(MCOperand::createReg(FuncAddrReg));
+        }
+      } else if (MinimizeCodeSize && !LoadElim) {
         // Load the call target into FuncAddrReg.
         NewCall->push_back(CallInst); // Copy CallInst in order to get SMLoc
         MCInst &Target = NewCall->back();
diff --git a/bolt/test/X86/icp-pic.s b/bolt/test/X86/icp-pic.s
new file mode 100644
index 0000000000000..ad7499324c335
--- /dev/null
+++ b/bolt/test/X86/icp-pic.s
@@ -0,0 +1,48 @@
+# REQUIRES: system-linux, x86-registered-target
+
+## Verify that x86 ICP materializes the promoted target address with a
+## PC-relative LEA instruction, automatically for a PIE input.
+
+# RUN: split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t/main.s -o %t.o
+# RUN: link_fdata %t/main.s %t.o %t/fdata
+
+## PIE inputs enable x86 ICP PIC mode automatically.
+# RUN: ld.lld -pie --entry=reg_call --emit-relocs %t.o -o %t.pie
+# RUN: llvm-bolt %t.pie -o %t.pie.out --relocs --data=%t/fdata --icp=calls \
+# RUN:   --icp-calls-topn=1 --lite=0 --use-gnu-stack \
+# RUN:   --custom-allocation-vma=0x80000000 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=PIC-INFO
+# RUN: llvm-objdump -d --no-show-raw-insn %t.pie.out | \
+# RUN:   FileCheck %s --check-prefix=LEA
+
+# PIC-INFO: BOLT-INFO: ICP is using x86 PIC mode with PC-relative LEA target checks
+# FORCE-INFO: BOLT-INFO: ICP is using x86 PIC mode with PC-relative LEA target checks
+
+# LEA-LABEL: <reg_call_site>:
+# LEA:       leaq {{.*}}(%rip), %r11
+# LEA-SAME:  <target>
+
+#--- main.s
+.text
+.globl reg_call
+.type reg_call, at function
+reg_call:
+reg_call_site:
+  callq *%rax
+# FDATA: 1 reg_call #reg_call_site# 1 target 0 0 100
+  retq
+.size reg_call, .-reg_call
+
+.globl dummy
+.type dummy, at function
+dummy:
+  callq target
+  retq
+.size dummy, .-dummy
+
+.globl target
+.type target, at function
+target:
+  retq
+.size target, .-target

``````````

</details>


https://github.com/llvm/llvm-project/pull/216587


More information about the llvm-commits mailing list