[clang] [compiler-rt] [llvm] [AIX] Implement the ifunc attribute. (PR #153049)
Wael Yehia via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 10 08:31:52 PDT 2025
================
@@ -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");
----------------
w2yehia wrote:
should we have the ifunc-local option?
Disadvantages of having this flag: if used on the wrong function (that is not local) the user will get bad/randome runtime behavior
https://github.com/llvm/llvm-project/pull/153049
More information about the llvm-commits
mailing list