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 <wye...@ca.ibm.com>
Date: Wed, 3 Sep 2025 19:22:15 +0000
Subject: [PATCH 1/8] [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]]
+@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 <wye...@ca.ibm.com>
Date: Mon, 15 Sep 2025 22:34:05 +0000
Subject: [PATCH 2/8] 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 cfd36f1e979a1785ed41a86c41135897de62a5dd Mon Sep 17 00:00:00 2001
From: Wael Yehia <wye...@ca.ibm.com>
Date: Thu, 18 Sep 2025 02:44:04 +0000
Subject: [PATCH 3/8] disallow !dbg and !prof metadata on ifuncs

---
 llvm/lib/IR/Verifier.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 82be6f203cae2..59e3b31f8e291 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -999,6 +999,16 @@ void Verifier::visitGlobalAlias(const GlobalAlias &GA) {
 void Verifier::visitGlobalIFunc(const GlobalIFunc &GI) {
   visitGlobalValue(GI);
 
+  SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
+  GI.getAllMetadata(MDs);
+  for (const auto &I : MDs) {
+    CheckDI(I.first != LLVMContext::MD_dbg,
+            "an ifunc may not have a !dbg attachment", &GI);
+    Check(I.first != LLVMContext::MD_prof,
+          "an ifunc may not have a !prof attachment", &GI);
+    visitMDNode(*I.second, AreDebugLocsAllowed::No);
+  }
+
   Check(GlobalIFunc::isValidLinkage(GI.getLinkage()),
         "IFunc should have private, internal, linkonce, weak, linkonce_odr, "
         "weak_odr, or external linkage!",

>From da604330c86033b681782905379041e3eb52fc26 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wye...@ca.ibm.com>
Date: Tue, 9 Sep 2025 19:27:03 +0000
Subject: [PATCH 4/8] accept ifunc attribute on AIX

---
 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 620a66e2b5e416cf91d9a60efac1c55787393184 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wye...@ca.ibm.com>
Date: Tue, 9 Sep 2025 20:30:47 +0000
Subject: [PATCH 5/8] 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 6d25c4756272694fabaf6788c9d63b5c07ef879b Mon Sep 17 00:00:00 2001
From: Wael Yehia <wye...@ca.ibm.com>
Date: Wed, 10 Sep 2025 03:14:54 +0000
Subject: [PATCH 6/8] 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 c32dedc2293e7cab13258a93ebf6d3f5adbacc43 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wye...@ca.ibm.com>
Date: Wed, 10 Sep 2025 14:25:15 +0000
Subject: [PATCH 7/8] 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;
 }

>From 629fe41ea5af555f712a6f633ce09ce55980574c Mon Sep 17 00:00:00 2001
From: Wael Yehia <wye...@ca.ibm.com>
Date: Thu, 18 Sep 2025 03:00:07 +0000
Subject: [PATCH 8/8] use -bdbg:namedsects:ss on AIX 7.2 and above

---
 clang/lib/Driver/ToolChains/AIX.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/AIX.cpp 
b/clang/lib/Driver/ToolChains/AIX.cpp
index 8111179d3de92..3630390b1ce81 100644
--- a/clang/lib/Driver/ToolChains/AIX.cpp
+++ b/clang/lib/Driver/ToolChains/AIX.cpp
@@ -146,9 +146,9 @@ void aix::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
     CmdArgs.push_back("-bforceimprw");
   }
 
-  // ifunc support, which is ON by default, generates named sections.
-  CmdArgs.push_back("-bdbg:namedsects:ss");
-
+  // PGO and ifunc support depends on the named sections linker feature.
+  if (Triple.getOSVersion() >= VersionTuple(7, 2))
+    CmdArgs.push_back("-bdbg:namedsects:ss");
 
   if (Arg *A =
           Args.getLastArg(clang::driver::options::OPT_mxcoff_build_id_EQ)) {

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to