[clang] [compiler-rt] [llvm] [AIX] Implement the ifunc attribute. (PR #153049)
Wael Yehia via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 15 17:53:22 PDT 2025
https://github.com/w2yehia updated https://github.com/llvm/llvm-project/pull/153049
>From 81a9c979385b5d368fcdcf36332ad506d97e1365 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 3 Sep 2025 19:22:15 +0000
Subject: [PATCH 1/6] [IR] enable attaching metadata on ifuncs
In PR #153049, we have a use case of attaching the !associated metadata to an ifunc.
Since an ifunc is similar to a function declaration, it seems natural to allow metadata on ifuncs.
Currently, the metadata API allows adding Metadata to llvm::Values, so the in-memory IR allows for
metadata on ifuncs, but the IR reader/writer is not aware of that.
Teach the IR parser and writer to support metadata on ifuncs, and update documentation.
---
llvm/docs/LangRef.rst | 7 ++++---
llvm/lib/AsmParser/LLParser.cpp | 3 +++
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 3 +++
llvm/lib/Bitcode/Writer/ValueEnumerator.cpp | 6 ++++++
llvm/lib/IR/AsmWriter.cpp | 6 ++++++
llvm/test/Assembler/metadata.ll | 8 ++++++++
6 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 6ba3759080cc3..d6b472af033f8 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1020,13 +1020,14 @@ On ELF platforms, IFuncs are resolved by the dynamic linker at load time. On
Mach-O platforms, they are lowered in terms of ``.symbol_resolver`` functions,
which lazily resolve the callee the first time they are called.
-IFunc may have an optional :ref:`linkage type <linkage>` and an optional
-:ref:`visibility style <visibility>`.
+IFunc may have an optional :ref:`linkage type <linkage>`, an optional
+:ref:`visibility style <visibility>`, an option partition, and an optional
+list of attached :ref:`metadata <metadata>`.
Syntax::
@<Name> = [Linkage] [PreemptionSpecifier] [Visibility] ifunc <IFuncTy>, <ResolverTy>* @<Resolver>
- [, partition "name"]
+ [, partition "name"] (, !name !N)*
.. _langref_comdats:
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 1bc2906f63b07..8739b24d4b74b 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1258,6 +1258,9 @@ bool LLParser::parseAliasOrIFunc(const std::string &Name, unsigned NameID,
GV->setPartition(Lex.getStrVal());
if (parseToken(lltok::StringConstant, "expected partition string"))
return true;
+ } else if (!IsAlias && Lex.getKind() == lltok::MetadataVar) {
+ if (parseGlobalObjectMetadataAttachment(*GI.get()))
+ return true;
} else {
return tokError("unknown alias or ifunc property!");
}
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index a3f825408d0c2..d9e138edb8ce2 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -2630,6 +2630,9 @@ void ModuleBitcodeWriter::writeModuleMetadata() {
for (const Function &F : M)
if (F.isDeclaration() && F.hasMetadata())
AddDeclAttachedMetadata(F);
+ for (const GlobalIFunc &GI : M.ifuncs())
+ if (GI.hasMetadata())
+ AddDeclAttachedMetadata(GI);
// FIXME: Only store metadata for declarations here, and move data for global
// variable definitions to a separate block (PR28134).
for (const GlobalVariable &GV : M.globals())
diff --git a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp
index e133abe577c22..f497c574ee75d 100644
--- a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp
+++ b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp
@@ -495,6 +495,12 @@ ValueEnumerator::ValueEnumerator(const Module &M,
EnumerateMetadata(&F, Op);
}
}
+ for (const GlobalIFunc &GIF : M.ifuncs()) {
+ MDs.clear();
+ GIF.getAllMetadata(MDs);
+ for (const auto &I : MDs)
+ EnumerateMetadata(nullptr, I.second);
+ }
// Optimize constant ordering.
OptimizeConstants(FirstConstant, Values.size());
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index dc6d599fa9585..690dac4e6133b 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -1078,6 +1078,7 @@ void SlotTracker::processModule() {
for (const GlobalIFunc &I : TheModule->ifuncs()) {
if (!I.hasName())
CreateModuleSlot(&I);
+ processGlobalObjectMetadata(I);
}
// Add metadata used by named metadata.
@@ -4077,6 +4078,11 @@ void AssemblyWriter::printIFunc(const GlobalIFunc *GI) {
printEscapedString(GI->getPartition(), Out);
Out << '"';
}
+ SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
+ GI->getAllMetadata(MDs);
+ if (!MDs.empty()) {
+ printMetadataAttachments(MDs, ", ");
+ }
printInfoComment(*GI);
Out << '\n';
diff --git a/llvm/test/Assembler/metadata.ll b/llvm/test/Assembler/metadata.ll
index 5b62bfafa6d7d..b1fb720eb31f9 100644
--- a/llvm/test/Assembler/metadata.ll
+++ b/llvm/test/Assembler/metadata.ll
@@ -5,6 +5,14 @@
; CHECK-UNMAT: @global = global i32 0, !foo [[M2:![0-9]+]], !foo [[M3:![0-9]+]], !baz [[M3]]
@global = global i32 0, !foo !2, !foo !3, !baz !3
+; CHECK-UNMAT: @ifunc_func = ifunc void (...), ptr @resolver, !foo [[M2]]
+ at ifunc_func = ifunc void (...), ptr @resolver, !foo !2
+
+define internal ptr @resolver() {
+entry:
+ ret ptr @test
+}
+
; CHECK-LABEL: @test
; CHECK: ret void, !foo [[M0:![0-9]+]], !bar [[M1:![0-9]+]]
define void @test() !dbg !1 {
>From 28bb942fa3b10ccbfc6a9a37e2bd58b240009cca Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Mon, 15 Sep 2025 22:34:05 +0000
Subject: [PATCH 2/6] call visitGlobalValue from visitGlobalIFunc
---
llvm/lib/IR/Verifier.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 1d3c379f461fa..82be6f203cae2 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -997,6 +997,8 @@ void Verifier::visitGlobalAlias(const GlobalAlias &GA) {
}
void Verifier::visitGlobalIFunc(const GlobalIFunc &GI) {
+ visitGlobalValue(GI);
+
Check(GlobalIFunc::isValidLinkage(GI.getLinkage()),
"IFunc should have private, internal, linkonce, weak, linkonce_odr, "
"weak_odr, or external linkage!",
>From cdd55c191a5425714d58940f67d38d944cd51959 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Tue, 9 Sep 2025 19:27:03 +0000
Subject: [PATCH 3/6] accept ifunc attribute on AI
---
clang/include/clang/Basic/TargetInfo.h | 2 +
llvm/include/llvm/CodeGen/AsmPrinter.h | 2 +-
.../CodeGen/TargetLoweringObjectFileImpl.h | 2 +-
.../llvm/Target/TargetLoweringObjectFile.h | 2 +-
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 2 -
.../CodeGen/TargetLoweringObjectFileImpl.cpp | 16 +-
llvm/lib/Target/PowerPC/CMakeLists.txt | 1 +
llvm/lib/Target/PowerPC/PPC.h | 2 +
llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 227 +++++++++++++++++-
.../Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp | 123 ++++++++++
llvm/lib/Target/PowerPC/PPCTargetMachine.cpp | 4 +
.../llvm/lib/Target/PowerPC/BUILD.gn | 1 +
12 files changed, 366 insertions(+), 18 deletions(-)
create mode 100644 llvm/lib/Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index ce4677e540226..ffeab27b67911 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1545,6 +1545,8 @@ class TargetInfo : public TransferrableTargetInfo,
return true;
if (getTriple().getArch() == llvm::Triple::ArchType::avr)
return true;
+ if (getTriple().isOSAIX())
+ return true;
return getTriple().isOSBinFormatELF() &&
((getTriple().isOSLinux() && !getTriple().isMusl()) ||
getTriple().isOSFreeBSD());
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 91c014236f6cb..e77e070fb5459 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -984,7 +984,7 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
virtual void emitModuleCommandLines(Module &M);
GCMetadataPrinter *getOrCreateGCPrinter(GCStrategy &S);
- void emitGlobalIFunc(Module &M, const GlobalIFunc &GI);
+ virtual void emitGlobalIFunc(Module &M, const GlobalIFunc &GI);
/// This method decides whether the specified basic block requires a label.
bool shouldEmitLabelForBasicBlock(const MachineBasicBlock &MBB) const;
diff --git a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
index fe450b3c1a3a2..fb04228205df0 100644
--- a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
+++ b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
@@ -289,7 +289,7 @@ class TargetLoweringObjectFileXCOFF : public TargetLoweringObjectFile {
static XCOFF::StorageClass getStorageClassForGlobal(const GlobalValue *GV);
MCSection *
- getSectionForFunctionDescriptor(const Function *F,
+ getSectionForFunctionDescriptor(const GlobalObject *F,
const TargetMachine &TM) const override;
MCSection *getSectionForTOCEntry(const MCSymbol *Sym,
const TargetMachine &TM) const override;
diff --git a/llvm/include/llvm/Target/TargetLoweringObjectFile.h b/llvm/include/llvm/Target/TargetLoweringObjectFile.h
index 397239b1685fb..b3d21a2230a69 100644
--- a/llvm/include/llvm/Target/TargetLoweringObjectFile.h
+++ b/llvm/include/llvm/Target/TargetLoweringObjectFile.h
@@ -267,7 +267,7 @@ class LLVM_ABI TargetLoweringObjectFile : public MCObjectFileInfo {
/// On targets that use separate function descriptor symbols, return a section
/// for the descriptor given its symbol. Use only with defined functions.
virtual MCSection *
- getSectionForFunctionDescriptor(const Function *F,
+ getSectionForFunctionDescriptor(const GlobalObject *F,
const TargetMachine &TM) const {
return nullptr;
}
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 23a3543e9ebec..f835bcda83d47 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -2425,8 +2425,6 @@ void AsmPrinter::emitGlobalAlias(const Module &M, const GlobalAlias &GA) {
}
void AsmPrinter::emitGlobalIFunc(Module &M, const GlobalIFunc &GI) {
- assert(!TM.getTargetTriple().isOSBinFormatXCOFF() &&
- "IFunc is not supported on AIX.");
auto EmitLinkage = [&](MCSymbol *Sym) {
if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective())
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index d19ef923ef740..6d04f8715cd5e 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2382,7 +2382,8 @@ MCSymbol *
TargetLoweringObjectFileXCOFF::getTargetSymbol(const GlobalValue *GV,
const TargetMachine &TM) const {
// We always use a qualname symbol for a GV that represents
- // a declaration, a function descriptor, or a common symbol.
+ // a declaration, a function descriptor, or a common symbol. An IFunc is
+ // lowered as a function, so it has an entry point and a descriptor.
// If a GV represents a GlobalVariable and -fdata-sections is enabled, we
// also return a qualname so that a label symbol could be avoided.
// It is inherently ambiguous when the GO represents the address of a
@@ -2401,6 +2402,11 @@ TargetLoweringObjectFileXCOFF::getTargetSymbol(const GlobalValue *GV,
SectionForGlobal(GVar, SectionKind::getData(), TM))
->getQualNameSymbol();
+ if (isa<GlobalIFunc>(GO))
+ return static_cast<const MCSectionXCOFF *>(
+ getSectionForFunctionDescriptor(GO, TM))
+ ->getQualNameSymbol();
+
SectionKind GOKind = getKindForGlobal(GO, TM);
if (GOKind.isText())
return static_cast<const MCSectionXCOFF *>(
@@ -2673,7 +2679,7 @@ TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(const GlobalValue *GV) {
MCSymbol *TargetLoweringObjectFileXCOFF::getFunctionEntryPointSymbol(
const GlobalValue *Func, const TargetMachine &TM) const {
- assert((isa<Function>(Func) ||
+ assert((isa<Function>(Func) || isa<GlobalIFunc>(Func) ||
(isa<GlobalAlias>(Func) &&
isa_and_nonnull<Function>(
cast<GlobalAlias>(Func)->getAliaseeObject()))) &&
@@ -2690,7 +2696,7 @@ MCSymbol *TargetLoweringObjectFileXCOFF::getFunctionEntryPointSymbol(
// undefined symbols gets treated as csect with XTY_ER property.
if (((TM.getFunctionSections() && !Func->hasSection()) ||
Func->isDeclarationForLinker()) &&
- isa<Function>(Func)) {
+ (isa<Function>(Func) || isa<GlobalIFunc>(Func))) {
return getContext()
.getXCOFFSection(
NameStr, SectionKind::getText(),
@@ -2704,7 +2710,9 @@ MCSymbol *TargetLoweringObjectFileXCOFF::getFunctionEntryPointSymbol(
}
MCSection *TargetLoweringObjectFileXCOFF::getSectionForFunctionDescriptor(
- const Function *F, const TargetMachine &TM) const {
+ const GlobalObject *F, const TargetMachine &TM) const {
+ assert((isa<Function>(F) || isa<GlobalIFunc>(F)) &&
+ "F must be a function or ifunc object.");
SmallString<128> NameStr;
getNameWithPrefix(NameStr, F, TM);
return getContext().getXCOFFSection(
diff --git a/llvm/lib/Target/PowerPC/CMakeLists.txt b/llvm/lib/Target/PowerPC/CMakeLists.txt
index 3808a26a0b92a..120eee586ac9a 100644
--- a/llvm/lib/Target/PowerPC/CMakeLists.txt
+++ b/llvm/lib/Target/PowerPC/CMakeLists.txt
@@ -42,6 +42,7 @@ add_llvm_target(PowerPCCodeGen
PPCMachineScheduler.cpp
PPCMacroFusion.cpp
PPCMIPeephole.cpp
+ PPCPrepareIFuncsOnAIX.cpp
PPCRegisterInfo.cpp
PPCSelectionDAGInfo.cpp
PPCSubtarget.cpp
diff --git a/llvm/lib/Target/PowerPC/PPC.h b/llvm/lib/Target/PowerPC/PPC.h
index 124dac4584312..6cab4616c8b03 100644
--- a/llvm/lib/Target/PowerPC/PPC.h
+++ b/llvm/lib/Target/PowerPC/PPC.h
@@ -53,6 +53,7 @@ class ModulePass;
FunctionPass *createPPCPreEmitPeepholePass();
FunctionPass *createPPCExpandAtomicPseudoPass();
FunctionPass *createPPCCTRLoopsPass();
+ ModulePass *createPPCPrepareIFuncsOnAIXPass();
void LowerPPCMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
AsmPrinter &AP);
bool LowerPPCMachineOperandToMCOperand(const MachineOperand &MO,
@@ -78,6 +79,7 @@ class ModulePass;
void initializePPCExpandAtomicPseudoPass(PassRegistry &);
void initializePPCCTRLoopsPass(PassRegistry &);
void initializePPCDAGToDAGISelLegacyPass(PassRegistry &);
+ void initializePPCPrepareIFuncsOnAIXPass(PassRegistry &);
void initializePPCLinuxAsmPrinterPass(PassRegistry &);
void initializePPCAIXAsmPrinterPass(PassRegistry &);
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 2ab2c147be0ec..d7f4ae3fd0087 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -100,6 +100,13 @@ static cl::opt<bool> EnableSSPCanaryBitInTB(
"aix-ssp-tb-bit", cl::init(false),
cl::desc("Enable Passing SSP Canary info in Trackback on AIX"), cl::Hidden);
+static cl::list<std::string> IFuncLocal(
+ "ifunc-local",
+ llvm::cl::desc("a comma separated list of ifunc function names that are "
+ "guaranteed to resolve to a module-local function. "
+ "-ifunc-local=1 will apply to all ifuncs in the CU."),
+ cl::CommaSeparated, cl::Hidden);
+
// Specialize DenseMapInfo to allow
// std::pair<const MCSymbol *, PPCMCExpr::Specifier> in DenseMap.
// This specialization is needed here because that type is used as keys in the
@@ -305,6 +312,8 @@ class PPCAIXAsmPrinter : public PPCAsmPrinter {
void emitTTypeReference(const GlobalValue *GV, unsigned Encoding) override;
void emitModuleCommandLines(Module &M) override;
+
+ void emitGlobalIFunc(Module &M, const GlobalIFunc &GI) override;
};
} // end anonymous namespace
@@ -768,6 +777,16 @@ static MCSymbol *getMCSymbolForTOCPseudoMO(const MachineOperand &MO,
}
}
+static PPCAsmPrinter::TOCEntryType
+getTOCEntryTypeForLinkage(GlobalValue::LinkageTypes Linkage) {
+ if (Linkage == GlobalValue::ExternalLinkage ||
+ Linkage == GlobalValue::AvailableExternallyLinkage ||
+ Linkage == GlobalValue::ExternalWeakLinkage)
+ return PPCAsmPrinter::TOCType_GlobalExternal;
+
+ return PPCAsmPrinter::TOCType_GlobalInternal;
+}
+
static PPCAsmPrinter::TOCEntryType
getTOCEntryTypeForMO(const MachineOperand &MO) {
// Use the target flags to determine if this MO is Thread Local.
@@ -778,13 +797,7 @@ getTOCEntryTypeForMO(const MachineOperand &MO) {
switch (MO.getType()) {
case MachineOperand::MO_GlobalAddress: {
const GlobalValue *GlobalV = MO.getGlobal();
- GlobalValue::LinkageTypes Linkage = GlobalV->getLinkage();
- if (Linkage == GlobalValue::ExternalLinkage ||
- Linkage == GlobalValue::AvailableExternallyLinkage ||
- Linkage == GlobalValue::ExternalWeakLinkage)
- return PPCAsmPrinter::TOCType_GlobalExternal;
-
- return PPCAsmPrinter::TOCType_GlobalInternal;
+ return getTOCEntryTypeForLinkage(GlobalV->getLinkage());
}
case MachineOperand::MO_ConstantPoolIndex:
return PPCAsmPrinter::TOCType_ConstantPool;
@@ -2863,8 +2876,10 @@ void PPCAIXAsmPrinter::emitFunctionDescriptor() {
static_cast<MCSymbolXCOFF *>(CurrentFnDescSym)->getRepresentedCsect());
// Emit aliasing label for function descriptor csect.
- for (const GlobalAlias *Alias : GOAliasMap[&MF->getFunction()])
- OutStreamer->emitLabel(getSymbol(Alias));
+ if (MF) // TODO MF is unset when processing an ifunc, handle it better than
+ // this.
+ for (const GlobalAlias *Alias : GOAliasMap[&MF->getFunction()])
+ OutStreamer->emitLabel(getSymbol(Alias));
// Emit function entry point address.
OutStreamer->emitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext),
@@ -2882,6 +2897,12 @@ void PPCAIXAsmPrinter::emitFunctionDescriptor() {
}
void PPCAIXAsmPrinter::emitFunctionEntryLabel() {
+ if (!MF) { // TODO: MF is unset when processing an ifunc, handle it better.
+ if (!TM.getFunctionSections())
+ PPCAsmPrinter::emitFunctionEntryLabel();
+ return;
+ }
+
// For functions without user defined section, it's not necessary to emit the
// label when we have individual function in its own csect.
if (!TM.getFunctionSections() || MF->getFunction().hasSection())
@@ -3361,6 +3382,194 @@ void PPCAIXAsmPrinter::emitModuleCommandLines(Module &M) {
OutStreamer->emitXCOFFCInfoSym(".GCC.command.line", RSOS.str());
}
+static bool TOCRestoreNeeded(const GlobalIFunc &GI) {
+ auto IsLocalFunc = [&](const Value *V) {
+ if (!isa<Function>(V))
+ return false;
+ auto *F = cast<Function>(V);
+
+ // static functions are local
+ if (F->getLinkage() == GlobalValue::InternalLinkage)
+ return true;
+ // for now, declarations we treat as potentially non-local
+ if (F->isDeclarationForLinker())
+ return false;
+ // hidden visibility definitions cannot be preempted, so treat as local.
+ if (F->getVisibility() == GlobalValue::HiddenVisibility)
+ return true;
+
+ return false;
+ };
+
+ if (!IFuncLocal.empty()) {
+ ArrayRef<std::string> List = IFuncLocal;
+ // special case of -ifunc-local=1
+ if (List.size() == 1 && List[0].compare("1") == 0)
+ return false;
+ StringRef IFuncName = GI.getName();
+ if (any_of(List, [&](const std::string &Element) {
+ return Element.size() == IFuncName.size() &&
+ Element.compare(IFuncName.data()) == 0;
+ }))
+ return false;
+ }
+
+ // if one of the return values of the resolver function is not a
+ // local function, then we have to conservatively do a TOC save/restore.
+ auto *Resolver = GI.getResolverFunction();
+ for (auto &BB : *Resolver) {
+ if (auto *Ret = dyn_cast<ReturnInst>(BB.getTerminator())) {
+ Value *RV = Ret->getReturnValue();
+ assert(RV);
+ // return &foo_p9;
+ if (auto *F = dyn_cast<Function>(RV)) {
+ if (!IsLocalFunc(F))
+ return true;
+ } else if (auto *I = dyn_cast<Instruction>(RV)) {
+ // return isP9 ? foo_p9 : foo_default;
+ if (auto *SI = dyn_cast<SelectInst>(I)) {
+ if (!IsLocalFunc(SI->getTrueValue()) ||
+ !IsLocalFunc(SI->getFalseValue()))
+ return true;
+ } else if (auto *PN = dyn_cast<PHINode>(I)) {
+ for (unsigned i = 1, e = PN->getNumIncomingValues(); i != e; ++i)
+ if (!IsLocalFunc(PN->getIncomingValue(i)))
+ return true;
+ } else
+ return true;
+ } else
+ return true;
+ }
+ }
+ // all return values where local functions, so no TOC save/restore needed.
+ return false;
+}
+/*
+ * .csect .foo[PR],5
+ * .globl foo[DS]
+ * .globl .foo[PR]
+ * .lglobl ifunc_sec.foo[RW]
+ * .align 4
+ * .csect foo[DS],2
+ * .vbyte 4, .foo[PR]
+ * .vbyte 4, TOC[TC0]
+ * .vbyte 4, 0
+ * .csect .foo[PR],5
+ * .ref ifunc_sec.foo[RW]
+ * lwz 12, L..C3(2)
+ * lwz 12, 0(12)
+ * mtctr 12
+ * bctr
+ * # -- End function
+ */
+void PPCAIXAsmPrinter::emitGlobalIFunc(Module &M, const GlobalIFunc &GI) {
+ // Set the Subtarget to that of the resolver.
+ const TargetSubtargetInfo *STI =
+ TM.getSubtargetImpl(*GI.getResolverFunction());
+ bool IsPPC64 = static_cast<const PPCSubtarget *>(STI)->isPPC64();
+
+ // Create syms and sections that are part of the ifunc implementation:
+ // - Function descriptor symbol foo[RW]
+ // - Function entry symbol .foo[PR]
+ // - ifunc_sec variable (that registers the ifunc's descriptor and resolver)
+ MCSectionXCOFF *FnDescSec = static_cast<MCSectionXCOFF *>(
+ getObjFileLowering().getSectionForFunctionDescriptor(&GI, TM));
+ FnDescSec->setAlignment(Align(IsPPC64 ? 8 : 4));
+
+ CurrentFnDescSym = FnDescSec->getQualNameSymbol();
+
+ CurrentFnSym = getObjFileLowering().getFunctionEntryPointSymbol(&GI, TM);
+
+ MCSymbol *IFuncUpdateSym = nullptr;
+ if (MDNode *MD = GI.getMetadata(LLVMContext::MD_associated)) {
+ const ValueAsMetadata *VAM = cast<ValueAsMetadata>(MD->getOperand(0).get());
+ const GlobalVariable *IFuncUpdateGV = cast<GlobalVariable>(VAM->getValue());
+ IFuncUpdateSym = getSymbol(IFuncUpdateGV);
+ }
+
+ // Start codegen:
+ if (TM.getFunctionSections())
+ OutStreamer->switchSection(
+ static_cast<MCSymbolXCOFF *>(CurrentFnSym)->getRepresentedCsect());
+ else
+ OutStreamer->switchSection(getObjFileLowering().getTextSection());
+
+ // generate linkage for foo and .foo
+ emitLinkage(&GI, CurrentFnDescSym);
+ emitLinkage(&GI, CurrentFnSym);
+
+ // declare the "ifunc_sec.foo[RW]" as an internal symbol
+ if (IFuncUpdateSym)
+ OutStreamer->emitXCOFFSymbolLinkageWithVisibility(
+ IFuncUpdateSym, MCSA_LGlobal, MCSA_Invalid);
+
+ // .align 4
+ Align Alignment(STI->getTargetLowering()->getMinFunctionAlignment());
+ emitAlignment(Alignment, nullptr);
+
+ // generate foo's function descriptor
+ emitFunctionDescriptor();
+
+ emitFunctionEntryLabel();
+
+ // back to .foo[PR]
+ // .ref ifunc_sec.foo[RW]
+ if (IFuncUpdateSym)
+ OutStreamer->emitXCOFFRefDirective(IFuncUpdateSym);
+
+ // vvvvvv TEMPORARY: TO BE REMOVED AFTER upstream PR 151569 lands vvvvv
+ // .ref .__init_ifuncs[PR]
+ if (MDNode *MD = GI.getMetadata(LLVMContext::MD_associated)) {
+ const ValueAsMetadata *VAM = cast<ValueAsMetadata>(MD->getOperand(0).get());
+ const GlobalVariable *IFuncUpdateGV = cast<GlobalVariable>(VAM->getValue());
+ MD = IFuncUpdateGV->getMetadata(LLVMContext::MD_associated);
+ if (MD) {
+ const ValueAsMetadata *VAM =
+ cast<ValueAsMetadata>(MD->getOperand(0).get());
+ const Function *InitIFuncDecl = cast<Function>(VAM->getValue());
+ OutStreamer->emitXCOFFRefDirective(
+ getObjFileLowering().getFunctionEntryPointSymbol(InitIFuncDecl, TM));
+ }
+ }
+ // ^^^^^^ TEMPORARY ^^^^^
+
+ // generate the code for .foo now:
+ if (TOCRestoreNeeded(GI)) {
+ reportFatalUsageError(
+ "unimplmented: TOC register save/restore needed for function " +
+ Twine(GI.getName()) +
+ ", check if -mllvm -ifunc-local=... applies to your case");
+ return;
+ }
+
+ // lwz 12, L..C3(2)
+ auto FnDescTOCEntryType = getTOCEntryTypeForLinkage(GI.getLinkage());
+ auto *FnDescTOCEntrySym =
+ lookUpOrCreateTOCEntry(CurrentFnDescSym, FnDescTOCEntryType);
+ auto *Exp = MCSymbolRefExpr::create(FnDescTOCEntrySym, OutContext);
+ // Exp = getTOCEntryLoadingExprForXCOFF(MOSymbol, Exp, VK);// TODO: need this?
+ // need this uncommented
+ OutStreamer->emitInstruction(MCInstBuilder(IsPPC64 ? PPC::LD : PPC::LWZ)
+ .addReg(PPC::X12)
+ .addExpr(Exp)
+ .addReg(PPC::X2),
+ *Subtarget);
+
+ // lwz 12, 0(12)
+ OutStreamer->emitInstruction(MCInstBuilder(IsPPC64 ? PPC::LD : PPC::LWZ)
+ .addReg(PPC::X12)
+ .addImm(0)
+ .addReg(PPC::X12),
+ *Subtarget);
+ // mtctr 12
+ OutStreamer->emitInstruction(
+ MCInstBuilder(IsPPC64 ? PPC::MTCTR8 : PPC::MTCTR).addReg(PPC::X12),
+ *Subtarget);
+ // bctr
+ OutStreamer->emitInstruction(MCInstBuilder(IsPPC64 ? PPC::BCTR8 : PPC::BCTR),
+ *Subtarget);
+}
+
char PPCAIXAsmPrinter::ID = 0;
INITIALIZE_PASS(PPCAIXAsmPrinter, "ppc-aix-asm-printer",
diff --git a/llvm/lib/Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp b/llvm/lib/Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp
new file mode 100644
index 0000000000000..098412554721c
--- /dev/null
+++ b/llvm/lib/Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp
@@ -0,0 +1,123 @@
+//===-- PPCPrepareIFuncsOnAIX.cpp - Prepare for ifunc lowering in codegen ===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass generates...
+//
+//===----------------------------------------------------------------------===//
+
+#include "PPC.h"
+#include "PPCSubtarget.h"
+#include "PPCTargetMachine.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include <cassert>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "ppc-prep-ifunc-aix"
+
+STATISTIC(NumIFuncs, "Number of IFuncs prepared");
+
+namespace {
+class PPCPrepareIFuncsOnAIX : public ModulePass {
+public:
+ static char ID;
+
+ PPCPrepareIFuncsOnAIX() : ModulePass(ID) {}
+
+ bool runOnModule(Module &M) override;
+
+ StringRef getPassName() const override {
+ return "PPC Prepare for AIX IFunc lowering";
+ }
+};
+} // namespace
+
+char PPCPrepareIFuncsOnAIX::ID = 0;
+
+INITIALIZE_PASS(PPCPrepareIFuncsOnAIX, DEBUG_TYPE,
+ "PPC Prepare for AIX IFunc lowering", false, false)
+
+ModulePass *llvm::createPPCPrepareIFuncsOnAIXPass() {
+ return new PPCPrepareIFuncsOnAIX();
+}
+
+// @foo = ifunc i32 (), ptr @foo_resolver, !associated !0
+// define ptr @foo_resolver() {
+// ...
+//
+// %struct.IFUNC_PAIR = type { ptr, ptr }
+// @update_foo = internal global %struct.IFUNC_PAIR { ptr @foo, ptr
+// @foo_resolver }, section "ifunc_sec", align 8, !associated !1 declare void
+// @__init_ifuncs(...)
+//
+// !0 = !{ptr @update_foo}
+// !1 = !{ptr @__init_ifuncs}
+bool PPCPrepareIFuncsOnAIX::runOnModule(Module &M) {
+ if (M.ifuncs().empty())
+ return false;
+
+ const DataLayout &DL = M.getDataLayout();
+ LLVMContext &Ctx = M.getContext();
+ auto *PtrTy = PointerType::getUnqual(Ctx);
+ StringRef IFuncUpdatePrefix = "__update_";
+ StringRef IFuncUpdateSectionName = "ifunc_sec";
+ StructType *IFuncPairType = StructType::get(PtrTy, PtrTy);
+
+ StringRef IFuncConstructorName = "__init_ifuncs";
+ auto *IFuncConstructorFnType =
+ FunctionType::get(Type::getVoidTy(Ctx), {}, /*isVarArg=*/false);
+ auto *IFuncConstructorDecl =
+ Function::Create(IFuncConstructorFnType, GlobalValue::ExternalLinkage,
+ IFuncConstructorName, M);
+
+ for (GlobalIFunc &IFunc : M.ifuncs()) {
+ LLVM_DEBUG(dbgs() << "doing ifunc " << IFunc.getName() << "\n");
+ // @update_foo = internal global { ptr @foo, ptr @foo_resolver }, section
+ // "ifunc_sec", align 8
+ std::string Name = (Twine(IFuncUpdatePrefix) + IFunc.getName()).str();
+ auto *GV = new GlobalVariable(M, IFuncPairType, /*isConstant*/ false,
+ GlobalValue::PrivateLinkage, nullptr, Name);
+ GV->setAlignment(DL.getPointerPrefAlignment());
+ GV->setVisibility(GlobalValue::DefaultVisibility);
+ GV->setSection(IFuncUpdateSectionName);
+
+ // Note that on AIX, the address of a function is the address of it's
+ // function descriptor, which is what these two values end up being
+ // in assembly.
+ Constant *InitVals[] = {&IFunc, IFunc.getResolver()};
+ GV->setInitializer(ConstantStruct::get(IFuncPairType, InitVals));
+
+ // Associate liveness of function foo with the liveness of update_foo.
+ IFunc.setMetadata(LLVMContext::MD_associated,
+ MDNode::get(Ctx, ValueAsMetadata::get(GV)));
+ // Make function foo depend on the constructor that calls each ifunc's
+ // resolver and updaTes the ifunc's function descriptor with the result.
+ // Note: technically, we can associate both the update_foo variable and
+ // the constructor function to function foo, but only one MD_associated
+ // is allowed on an llvm::Value, so associate the constructor to update_foo
+ // here.
+ GV->setMetadata(
+ LLVMContext::MD_associated,
+ MDNode::get(Ctx, ValueAsMetadata::get(IFuncConstructorDecl)));
+
+ MDNode *MD = GV->getMetadata(LLVMContext::MD_associated);
+ assert(MD);
+ LLVM_DEBUG(MD->dump());
+ MD = IFunc.getMetadata(LLVMContext::MD_associated);
+ assert(MD);
+ LLVM_DEBUG(MD->dump());
+ }
+ LLVM_DEBUG(M.dump());
+
+ return true;
+}
diff --git a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
index b5c6ac111dff0..4c84523e52975 100644
--- a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
+++ b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
@@ -145,6 +145,7 @@ LLVMInitializePowerPCTarget() {
initializeGlobalISel(PR);
initializePPCCTRLoopsPass(PR);
initializePPCDAGToDAGISelLegacyPass(PR);
+ initializePPCPrepareIFuncsOnAIXPass(PR);
initializePPCLinuxAsmPrinterPass(PR);
initializePPCAIXAsmPrinterPass(PR);
}
@@ -489,6 +490,9 @@ void PPCPassConfig::addIRPasses() {
addPass(createLICMPass());
}
+ if (TM->getTargetTriple().isOSAIX())
+ addPass(createPPCPrepareIFuncsOnAIXPass());
+
TargetPassConfig::addIRPasses();
}
diff --git a/llvm/utils/gn/secondary/llvm/lib/Target/PowerPC/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Target/PowerPC/BUILD.gn
index ea3615cee392a..85c922ec3bc41 100644
--- a/llvm/utils/gn/secondary/llvm/lib/Target/PowerPC/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/lib/Target/PowerPC/BUILD.gn
@@ -84,6 +84,7 @@ static_library("LLVMPowerPCCodeGen") {
"PPCMachineScheduler.cpp",
"PPCMacroFusion.cpp",
"PPCPreEmitPeephole.cpp",
+ "PPCPrepareIFuncsOnAIX.cpp",
"PPCReduceCRLogicals.cpp",
"PPCRegisterInfo.cpp",
"PPCSelectionDAGInfo.cpp",
>From 789f850ae19d0b316a880ab1f318592c4e7f9f08 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Tue, 9 Sep 2025 20:30:47 +0000
Subject: [PATCH 4/6] enable tests
---
clang/test/CodeGen/attr-ifunc.c | 2 ++
clang/test/CodeGen/attr-ifunc.cpp | 2 ++
clang/test/CodeGen/ifunc.c | 4 +++-
clang/test/SemaCXX/ifunc-has-attribute.cpp | 1 +
4 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/clang/test/CodeGen/attr-ifunc.c b/clang/test/CodeGen/attr-ifunc.c
index c9e70b17a8302..55d1866c17a69 100644
--- a/clang/test/CodeGen/attr-ifunc.c
+++ b/clang/test/CodeGen/attr-ifunc.c
@@ -4,6 +4,8 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx -verify -emit-llvm-only %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -verify -emit-llvm-only %s
// RUN: %clang_cc1 -triple aarch64-pc-windows-msvcu -verify -emit-llvm-only %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -verify -emit-llvm-only %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -verify -emit-llvm-only -DCHECK_ALIASES %s
#if defined(_WIN32) && !defined(__aarch64__)
void foo(void) {}
diff --git a/clang/test/CodeGen/attr-ifunc.cpp b/clang/test/CodeGen/attr-ifunc.cpp
index 9e6cd7312122d..601fad94530bd 100644
--- a/clang/test/CodeGen/attr-ifunc.cpp
+++ b/clang/test/CodeGen/attr-ifunc.cpp
@@ -1,9 +1,11 @@
// RUN: %clang_cc1 -triple x86_64-linux -verify -emit-llvm-only %s
// RUN: %clang_cc1 -triple x86_64-apple-macosx -verify -emit-llvm-only %s
// RUN: %clang_cc1 -triple arm64-apple-macosx -verify -emit-llvm-only %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -verify -emit-llvm-only %s
// RUN: not %clang_cc1 -triple x86_64-linux -emit-llvm-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: not %clang_cc1 -triple x86_64-apple-macosx -emit-llvm-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: not %clang_cc1 -triple arm64-apple-macosx -emit-llvm-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -triple powerpc64-ibm-aix-xcoff -emit-llvm-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
void *f1_ifunc(void) { return nullptr; }
void f1(void) __attribute__((ifunc("f1_ifunc")));
diff --git a/clang/test/CodeGen/ifunc.c b/clang/test/CodeGen/ifunc.c
index 7d21f742e8676..c346f81947cde 100644
--- a/clang/test/CodeGen/ifunc.c
+++ b/clang/test/CodeGen/ifunc.c
@@ -16,6 +16,8 @@
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -O2 -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=thread -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -O2 -emit-llvm -o - %s | FileCheck %s
/// The ifunc is emitted before its resolver.
@@ -65,7 +67,7 @@ extern void hoo(int) __attribute__ ((ifunc("hoo_ifunc")));
// AVR: @goo = ifunc void (), ptr addrspace(1) @goo_ifunc
// AVR: @hoo = ifunc void (i16), ptr addrspace(1) @hoo_ifunc
-// CHECK: call i32 @foo(i32
+// CHECK: call {{(signext )?}}i32 @foo(i32
// CHECK: call void @goo()
// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @goo_ifunc() #[[#GOO_IFUNC:]] {
diff --git a/clang/test/SemaCXX/ifunc-has-attribute.cpp b/clang/test/SemaCXX/ifunc-has-attribute.cpp
index 242f3b621745f..913bc40ffee44 100644
--- a/clang/test/SemaCXX/ifunc-has-attribute.cpp
+++ b/clang/test/SemaCXX/ifunc-has-attribute.cpp
@@ -2,6 +2,7 @@
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-apple-macosx -verify %s -DSUPPORTED=1
// RUN: %clang_cc1 -emit-llvm-only -triple arm64-apple-macosx -verify %s -DSUPPORTED=1
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-pc-win32 -verify %s -DNOT_SUPPORTED=1
+// RUN: %clang_cc1 -emit-llvm-only -triple powerpc64-ibm-aix-xcoff -verify %s -DSUPPORTED=1
// expected-no-diagnostics
>From 0a6e81516eebe0e7a893ae073c8615503cd00bc7 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 10 Sep 2025 03:14:54 +0000
Subject: [PATCH 5/6] Add ppc/init_ifuncs.c to the builtins library and pass
the linker option -bdbg:namedsects:ss by default on AIX
---
clang/lib/Driver/ToolChains/AIX.cpp | 23 +++-------------------
compiler-rt/lib/builtins/CMakeLists.txt | 10 ++++++++++
compiler-rt/lib/builtins/ppc/init_ifuncs.c | 14 +++++++++++++
3 files changed, 27 insertions(+), 20 deletions(-)
create mode 100644 compiler-rt/lib/builtins/ppc/init_ifuncs.c
diff --git a/clang/lib/Driver/ToolChains/AIX.cpp b/clang/lib/Driver/ToolChains/AIX.cpp
index 066b59305fe3f..8111179d3de92 100644
--- a/clang/lib/Driver/ToolChains/AIX.cpp
+++ b/clang/lib/Driver/ToolChains/AIX.cpp
@@ -146,26 +146,9 @@ void aix::Linker::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-bforceimprw");
}
- // PGO instrumentation generates symbols belonging to special sections, and
- // the linker needs to place all symbols in a particular section together in
- // memory; the AIX linker does that under an option.
- if (Args.hasFlag(options::OPT_fprofile_arcs, options::OPT_fno_profile_arcs,
- false) ||
- Args.hasFlag(options::OPT_fprofile_generate,
- options::OPT_fno_profile_generate, false) ||
- Args.hasFlag(options::OPT_fprofile_generate_EQ,
- options::OPT_fno_profile_generate, false) ||
- Args.hasFlag(options::OPT_fprofile_instr_generate,
- options::OPT_fno_profile_instr_generate, false) ||
- Args.hasFlag(options::OPT_fprofile_instr_generate_EQ,
- options::OPT_fno_profile_instr_generate, false) ||
- Args.hasFlag(options::OPT_fcs_profile_generate,
- options::OPT_fno_profile_generate, false) ||
- Args.hasFlag(options::OPT_fcs_profile_generate_EQ,
- options::OPT_fno_profile_generate, false) ||
- Args.hasArg(options::OPT_fcreate_profile) ||
- Args.hasArg(options::OPT_coverage))
- CmdArgs.push_back("-bdbg:namedsects:ss");
+ // ifunc support, which is ON by default, generates named sections.
+ CmdArgs.push_back("-bdbg:namedsects:ss");
+
if (Arg *A =
Args.getLastArg(clang::driver::options::OPT_mxcoff_build_id_EQ)) {
diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt
index 1dadb6a810efb..2db8d158649b9 100644
--- a/compiler-rt/lib/builtins/CMakeLists.txt
+++ b/compiler-rt/lib/builtins/CMakeLists.txt
@@ -784,6 +784,16 @@ if (NOT OS_NAME MATCHES "AIX")
${powerpc64_SOURCES}
)
endif()
+if (OS_NAME MATCHES "AIX")
+ set(powerpc_SOURCES
+ ppc/init_ifuncs.c
+ ${powerpc_SOURCES}
+ )
+ set(powerpc64_SOURCES
+ ppc/init_ifuncs.c
+ ${powerpc64_SOURCES}
+ )
+endif()
set(powerpc64le_SOURCES ${powerpc64_SOURCES})
set(riscv_SOURCES
diff --git a/compiler-rt/lib/builtins/ppc/init_ifuncs.c b/compiler-rt/lib/builtins/ppc/init_ifuncs.c
new file mode 100644
index 0000000000000..0f743c2ad05d4
--- /dev/null
+++ b/compiler-rt/lib/builtins/ppc/init_ifuncs.c
@@ -0,0 +1,14 @@
+typedef void* Ptr;
+typedef struct { Ptr addr, toc, env; } Descr;
+typedef struct { Descr* desc; Ptr (*resolver)(); } IFUNC_PAIR;
+
+extern IFUNC_PAIR __start_ifunc_sec, __stop_ifunc_sec;
+
+__attribute__((constructor))
+void __init_ifuncs() {
+ for (IFUNC_PAIR *pair = &__start_ifunc_sec;
+ pair != &__stop_ifunc_sec;
+ pair++)
+ pair->desc->addr = ((Descr*)(pair->resolver()))->addr;
+}
+
>From 26eff05917df371b4640e870eae0c62d10b147a7 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 10 Sep 2025 14:25:15 +0000
Subject: [PATCH 6/6] create a zero-length entry in the ifunc_sec section to
satisfy the start/stop symbol references when user code does not use ifuncs
---
compiler-rt/lib/builtins/ppc/init_ifuncs.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/builtins/ppc/init_ifuncs.c b/compiler-rt/lib/builtins/ppc/init_ifuncs.c
index 0f743c2ad05d4..b48e80fabc84e 100644
--- a/compiler-rt/lib/builtins/ppc/init_ifuncs.c
+++ b/compiler-rt/lib/builtins/ppc/init_ifuncs.c
@@ -2,12 +2,20 @@ typedef void* Ptr;
typedef struct { Ptr addr, toc, env; } Descr;
typedef struct { Descr* desc; Ptr (*resolver)(); } IFUNC_PAIR;
+// A zero-length entry in section "ifunc_sec" to satisfy the __start_ifunc_sec
+// and __stop_ifunc_sec references in this file, when no user code has any.
+__attribute__((section("ifunc_sec"))) static int dummy_ifunc_sec[0];
+
extern IFUNC_PAIR __start_ifunc_sec, __stop_ifunc_sec;
__attribute__((constructor))
void __init_ifuncs() {
- for (IFUNC_PAIR *pair = &__start_ifunc_sec;
- pair != &__stop_ifunc_sec;
+ void *volatile ref = &dummy_ifunc_sec; // hack to keep dummy_ifunc_sec alive
+
+ // hack to prevent compiler from assuming __start_ifunc_sec and
+ // __stop_ifunc_sec occupy different addresses.
+ IFUNC_PAIR *volatile volatile_end = &__stop_ifunc_sec;
+ for (IFUNC_PAIR *pair = &__start_ifunc_sec, *end = volatile_end; pair != end;
pair++)
pair->desc->addr = ((Descr*)(pair->resolver()))->addr;
}
More information about the llvm-commits
mailing list