================ @@ -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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits