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