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

Jinjie Huang via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 16 11:34:09 PDT 2026


https://github.com/Jinjie-Huang updated https://github.com/llvm/llvm-project/pull/216587

>From a83459aeb4d8bdc13f86427a660a77eac747fe50 Mon Sep 17 00:00:00 2001
From: huangjinjie <huangjinjie at bytedance.com>
Date: Mon, 17 Aug 2026 02:33:46 +0800
Subject: [PATCH] use pic code for x86 icp

---
 bolt/lib/Passes/IndirectCallPromotion.cpp |  5 ++
 bolt/lib/Target/X86/X86MCPlusBuilder.cpp  | 69 ++++++++++++++++++++---
 bolt/test/X86/icp-pic.s                   | 48 ++++++++++++++++
 3 files changed, 115 insertions(+), 7 deletions(-)
 create mode 100644 bolt/test/X86/icp-pic.s

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..870da574d60fa 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,22 @@ 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());
+      }
+      // R10 and R11 are caller-saved under the x86-64 ABI, so the promoted
+      // sequence does not need to restore the selected scratch register after
+      // the call. Prefer R11 in PIC mode because R10 may carry the nest
+      // argument.
+      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 +3364,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



More information about the llvm-commits mailing list