[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