diff options
Diffstat (limited to 'llvm/lib')
| -rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 12 | ||||
| -rw-r--r-- | llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp | 17 | ||||
| -rw-r--r-- | llvm/lib/LTO/LTOModule.cpp | 7 | ||||
| -rw-r--r-- | llvm/lib/Target/PowerPC/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | llvm/lib/Target/PowerPC/PPC.h | 2 | ||||
| -rw-r--r-- | llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 294 | ||||
| -rw-r--r-- | llvm/lib/Target/PowerPC/PPCISelLowering.cpp | 1 | ||||
| -rw-r--r-- | llvm/lib/Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp | 114 | ||||
| -rw-r--r-- | llvm/lib/Target/PowerPC/PPCTargetMachine.cpp | 4 |
9 files changed, 419 insertions, 33 deletions
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index f58e4b8..59cd885 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -2558,9 +2558,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()) OutStreamer->emitSymbolAttribute(Sym, MCSA_Global); @@ -2905,6 +2902,15 @@ bool AsmPrinter::doFinalization(Module &M) { // sections after DWARF. for (const auto &IFunc : M.ifuncs()) emitGlobalIFunc(M, IFunc); + if (TM.getTargetTriple().isOSBinFormatXCOFF() && hasDebugInfo()) { + // Emit section end. This is used to tell the debug line section where the + // end is for a text section if we don't use .loc to represent the debug + // line. + auto *Sec = OutContext.getObjectFileInfo()->getTextSection(); + OutStreamer->switchSectionNoPrint(Sec); + MCSymbol *Sym = Sec->getEndSymbol(OutContext); + OutStreamer->emitLabel(Sym); + } // Finalize debug and EH information. for (auto &Handler : Handlers) diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp index fac0a17..f8f549f 100644 --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2392,7 +2392,9 @@ 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 special trampoline function which 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 @@ -2411,6 +2413,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 *>( @@ -2683,7 +2690,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()))) && @@ -2700,7 +2707,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(), @@ -2714,7 +2721,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/LTO/LTOModule.cpp b/llvm/lib/LTO/LTOModule.cpp index 9d5b0f5..e821abb 100644 --- a/llvm/lib/LTO/LTOModule.cpp +++ b/llvm/lib/LTO/LTOModule.cpp @@ -401,7 +401,7 @@ void LTOModule::addDefinedFunctionSymbol(ModuleSymbolTable::Symbol Sym) { } auto *GV = cast<GlobalValue *>(Sym); - assert((isa<Function>(GV) || + assert((isa<Function>(GV) || isa<GlobalIFunc>(GV) || (isa<GlobalAlias>(GV) && isa<Function>(cast<GlobalAlias>(GV)->getAliasee()))) && "Not function or function alias"); @@ -611,6 +611,11 @@ void LTOModule::parseSymbols() { continue; } + if (getTargetTriple().isOSBinFormatXCOFF() && isa<GlobalIFunc>(GV)) { + addDefinedFunctionSymbol(Sym); + continue; + } + assert(isa<GlobalAlias>(GV)); if (isa<Function>(cast<GlobalAlias>(GV)->getAliasee())) diff --git a/llvm/lib/Target/PowerPC/CMakeLists.txt b/llvm/lib/Target/PowerPC/CMakeLists.txt index 53d56501..b7601bf 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 a7cd5cd..88a54ac3 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 45025f4..64102e7 100644 --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -48,6 +48,7 @@ #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Module.h" +#include "llvm/IR/PatternMatch.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" @@ -83,6 +84,7 @@ using namespace llvm; using namespace llvm::XCOFF; +using namespace PatternMatch; #define DEBUG_TYPE "asmprinter" @@ -101,6 +103,19 @@ 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::opt<bool> IFuncLocalIfProven( + "ifunc-local-if-proven", cl::init(false), + cl::desc("During ifunc lowering, the compiler assumes the resolver returns " + "dso-local functions and bails out if non-local functions are " + "detected; this flag flips the assumption: resolver returns " + "preemptible functions unless the compiler can prove all paths " + "return local functions."), + cl::Hidden); + +// this flag is used for testing only as it might generate bad code. +static cl::opt<bool> IFuncWarnInsteadOfError("test-ifunc-warn-noerror", + cl::init(false), cl::ReallyHidden); + // 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 @@ -308,6 +323,8 @@ public: void emitModuleCommandLines(Module &M) override; void emitRefMetadata(const GlobalObject *); + + void emitGlobalIFunc(Module &M, const GlobalIFunc &GI) override; }; } // end anonymous namespace @@ -780,6 +797,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. // If we don't do this it comes out as Global. @@ -789,13 +816,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; @@ -2909,8 +2930,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)); + // An Ifunc doesn't have a corresponding machine function. + if (MF) + for (const GlobalAlias *Alias : GOAliasMap[&MF->getFunction()]) + OutStreamer->emitLabel(getSymbol(Alias)); // Emit function entry point address. OutStreamer->emitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext), @@ -2930,11 +2953,14 @@ void PPCAIXAsmPrinter::emitFunctionDescriptor() { void PPCAIXAsmPrinter::emitFunctionEntryLabel() { // 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()) + if (!TM.getFunctionSections() || (MF && MF->getFunction().hasSection())) PPCAsmPrinter::emitFunctionEntryLabel(); - const Function *F = &MF->getFunction(); + // an ifunc does not have an associated MachineFunction + if (!MF) + return; + const Function *F = &MF->getFunction(); // Emit aliasing label for function entry point label. for (const GlobalAlias *Alias : GOAliasMap[F]) OutStreamer->emitLabel( @@ -3280,17 +3306,6 @@ void PPCAIXAsmPrinter::emitInstruction(const MachineInstr *MI) { } bool PPCAIXAsmPrinter::doFinalization(Module &M) { - // Do streamer related finalization for DWARF. - if (hasDebugInfo()) { - // Emit section end. This is used to tell the debug line section where the - // end is for a text section if we don't use .loc to represent the debug - // line. - auto *Sec = OutContext.getObjectFileInfo()->getTextSection(); - OutStreamer->switchSectionNoPrint(Sec); - MCSymbol *Sym = Sec->getEndSymbol(OutContext); - OutStreamer->emitLabel(Sym); - } - for (MCSymbol *Sym : ExtSymSDNodeSymbols) OutStreamer->emitSymbolAttribute(Sym, MCSA_Extern); return PPCAsmPrinter::doFinalization(M); @@ -3385,12 +3400,15 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV, void PPCAIXAsmPrinter::emitRefMetadata(const GlobalObject *GO) { SmallVector<MDNode *> MDs; GO->getMetadata(LLVMContext::MD_implicit_ref, MDs); - assert(MDs.size() && "Expected asscoiated metadata nodes"); + assert(MDs.size() && "Expected !implicit.ref metadata nodes"); for (const MDNode *MD : MDs) { const ValueAsMetadata *VAM = cast<ValueAsMetadata>(MD->getOperand(0).get()); const GlobalValue *GV = cast<GlobalValue>(VAM->getValue()); - MCSymbol *Referenced = TM.getSymbol(GV); + MCSymbol *Referenced = + isa<Function>(GV) + ? getObjFileLowering().getFunctionEntryPointSymbol(GV, TM) + : TM.getSymbol(GV); OutStreamer->emitXCOFFRefDirective(Referenced); } } @@ -3426,6 +3444,234 @@ void PPCAIXAsmPrinter::emitModuleCommandLines(Module &M) { OutStreamer->emitXCOFFCInfoSym(".GCC.command.line", RSOS.str()); } +static bool TOCRestoreNeededForCallToImplementation(const GlobalIFunc &GI) { + enum class IsLocal { + Unknown, // Structure of the llvm::Value is not one of the recognizable + // structures, and so it's unknown if the llvm::Value is the + // address of a local function at runtime. + True, // We can statically prove that all runtime values of the + // llvm::Value is an address of a local function. + False // We can statically prove that one of the runtime values of the + // llvm::Value is the address of a non-local function; it could be + // the case that at runtime the non-local function is never + // selected but we don't care. + }; + auto Combine = [](IsLocal LHS, IsLocal RHS) -> IsLocal { + if (LHS == IsLocal::False || RHS == IsLocal::False) + return IsLocal::False; + if (LHS == IsLocal::True && RHS == IsLocal::True) + return IsLocal::True; + return IsLocal::Unknown; + }; + + // Query if the given function is local to the load module. + auto IsLocalFunc = [](const Function *F) -> IsLocal { + bool Result = F->isDSOLocal(); + LLVM_DEBUG(dbgs() << F->getName() << " is " + << (Result ? "dso_local\n" : "not dso_local\n")); + return Result ? IsLocal::True : IsLocal::False; + }; + + // Recursive walker that visits certain patterns that make up the given Value, + // and returns + // - false if at least one non-local function was seen, + // - otherwise, return unknown if some unrecognizable pattern was seen, + // - otherwise, return true (which means only recognizable patterns were seen + // and all possible values are local functions). + std::function<IsLocal(const Value *)> ValueIsALocalFunc = + [&IsLocalFunc, &Combine, &ValueIsALocalFunc](const Value *V) -> IsLocal { + if (auto *F = dyn_cast<Function>(V)) + return IsLocalFunc(F); + if (!isa<Instruction>(V)) + return IsLocal::Unknown; + + auto *I = cast<Instruction>(V); + // return isP9 ? foo_p9 : foo_default; + if (auto *SI = dyn_cast<SelectInst>(I)) + return Combine(ValueIsALocalFunc(SI->getTrueValue()), + ValueIsALocalFunc(SI->getFalseValue())); + else if (auto *PN = dyn_cast<PHINode>(I)) { + IsLocal Res = IsLocal::True; + for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) { + Res = Combine(Res, ValueIsALocalFunc(PN->getIncomingValue(i))); + if (Res == IsLocal::False) + return Res; + } + return Res; + } + // clang-format off + // @switch.table.resolve_foo = private unnamed_addr constant [3 x ptr] [ptr @foo_static, ptr @foo_hidden, ptr @foo_protected] + // %switch.gep = getelementptr inbounds nuw ptr, ptr @switch.table, i64 %2 + // V = load ptr, ptr %switch.gep, + // clang-format on + else if (auto *Op = getPointerOperand(I)) { + while (isa<GEPOperator>(Op)) + Op = cast<GEPOperator>(Op)->getPointerOperand(); + + if (!isa<GlobalVariable>(Op)) + return IsLocal::Unknown; + auto *GV = dyn_cast<GlobalVariable>(Op); + if (!GV->hasInitializer() || !isa<ConstantArray>(GV->getInitializer())) + return IsLocal::Unknown; + auto *Init = cast<ConstantArray>(GV->getInitializer()); + IsLocal Res = IsLocal::True; + for (unsigned Idx = 0, End = Init->getNumOperands(); Idx != End; ++Idx) { + Res = Combine(Res, ValueIsALocalFunc(Init->getOperand(Idx))); + if (Res == IsLocal::False) + return Res; + } + return Res; + } + return IsLocal::Unknown; + }; + + auto *Resolver = GI.getResolverFunction(); + // If the resolver is preemptible then we cannot rely on its implementation. + if (IsLocalFunc(Resolver) == IsLocal::False && IFuncLocalIfProven) + return true; + + // 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. + IsLocal Res = IsLocal::True; + for (auto &BB : *Resolver) { + if (!isa<ReturnInst>(BB.getTerminator())) + continue; + auto *Ret = cast<ReturnInst>(BB.getTerminator()); + Value *RV = Ret->getReturnValue(); + assert(RV); + Res = Combine(Res, ValueIsALocalFunc(RV)); + if (Res == IsLocal::False) + break; + } + // no TOC save/restore needed if either all functions were local or we're + // being optimistic and no preemptible functions were seen. + if (Res == IsLocal::True || (Res == IsLocal::Unknown && !IFuncLocalIfProven)) + return false; + return true; +} +/* + * .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..foo_desc(2) # load foo's descriptor address + * lwz 11, 8(12) # load the env pointer (for non-C/C++ functions) + * lwz 12, 0(12) # load foo.addr + * mtctr 12 + * bctr # branch to CR without setting LR so that callee + * # returns to the caller of .foo + * # -- 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] + MCSectionXCOFF *FnDescSec = static_cast<MCSectionXCOFF *>( + getObjFileLowering().getSectionForFunctionDescriptor(&GI, TM)); + FnDescSec->setAlignment(Align(IsPPC64 ? 8 : 4)); + + CurrentFnDescSym = FnDescSec->getQualNameSymbol(); + + CurrentFnSym = getObjFileLowering().getFunctionEntryPointSymbol(&GI, TM); + + // Start codegen: + if (TM.getFunctionSections()) + OutStreamer->switchSection( + static_cast<MCSymbolXCOFF *>(CurrentFnSym)->getRepresentedCsect()); + else + OutStreamer->switchSection(getObjFileLowering().getTextSection()); + + if (GI.hasMetadata(LLVMContext::MD_implicit_ref)) + emitRefMetadata(&GI); + + // generate linkage for foo and .foo + emitLinkage(&GI, CurrentFnDescSym); + emitLinkage(&GI, CurrentFnSym); + + // .align 4 + Align Alignment(STI->getTargetLowering()->getMinFunctionAlignment()); + emitAlignment(Alignment, nullptr); + + // generate foo's function descriptor + emitFunctionDescriptor(); + + emitFunctionEntryLabel(); + + // generate the code for .foo now: + if (TOCRestoreNeededForCallToImplementation(GI)) { + Twine Msg = "unimplemented: TOC register save/restore needed for ifunc \"" + + Twine(GI.getName()) + + "\", because couldn't prove all candidates " + "are static or hidden/protected visibility definitions"; + if (!IFuncWarnInsteadOfError) + reportFatalUsageError(Msg); + else + dbgs() << Msg << "\n"; + } + + auto FnDescTOCEntryType = getTOCEntryTypeForLinkage(GI.getLinkage()); + auto *FnDescTOCEntrySym = + lookUpOrCreateTOCEntry(CurrentFnDescSym, FnDescTOCEntryType); + + if (TM.getCodeModel() == CodeModel::Large) { + // addis 12, L..foo_desc@u(2) + // lwz 12, L..foo_desc@l(12) + auto *Exp_U = symbolWithSpecifier(FnDescTOCEntrySym, PPC::S_U); + OutStreamer->emitInstruction(MCInstBuilder(PPC::ADDIS) + .addReg(PPC::X12) + .addReg(PPC::X2) + .addExpr(Exp_U), + *Subtarget); + auto *Exp_L = symbolWithSpecifier(FnDescTOCEntrySym, PPC::S_L); + OutStreamer->emitInstruction(MCInstBuilder(IsPPC64 ? PPC::LD : PPC::LWZ) + .addReg(PPC::X12) + .addExpr(Exp_L) + .addReg(PPC::X12), + *Subtarget); + } else { + // lwz 12, L..foo_desc(2) + auto *Exp = MCSymbolRefExpr::create(FnDescTOCEntrySym, OutContext); + // Exp = getTOCEntryLoadingExprForXCOFF(MOSymbol, Exp, VK); + // TODO: do we need to uncomment this? + OutStreamer->emitInstruction(MCInstBuilder(IsPPC64 ? PPC::LD : PPC::LWZ) + .addReg(PPC::X12) + .addExpr(Exp) + .addReg(PPC::X2), + *Subtarget); + } + // lwz 11, 8(12) + OutStreamer->emitInstruction(MCInstBuilder(IsPPC64 ? PPC::LD : PPC::LWZ) + .addReg(PPC::X11) + .addImm(IsPPC64 ? 16 : 8) + .addReg(PPC::X12), + *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/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp index 6c87d1c..4119f91 100644 --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -5438,7 +5438,6 @@ static SDValue transformCallee(const SDValue &Callee, SelectionDAG &DAG, const GlobalValue *GV = cast<GlobalAddressSDNode>(Callee)->getGlobal(); if (Subtarget.isAIXABI()) { - assert(!isa<GlobalIFunc>(GV) && "IFunc is not supported on AIX."); return getAIXFuncEntryPointSymbolSDNode(GV); } return DAG.getTargetGlobalAddress(GV, dl, Callee.getValueType(), 0, diff --git a/llvm/lib/Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp b/llvm/lib/Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp new file mode 100644 index 0000000..5b6a6d6 --- /dev/null +++ b/llvm/lib/Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp @@ -0,0 +1,114 @@ +//===-- 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(); +} + +// For each ifunc `foo` with a resolver `foo_resolver`, create a global variable +// `__update_foo` in the `ifunc_sec` section, representing the pair: +// { ptr @foo, ptr @foo_resolver } +// The compiler arranges for the constructor function `__init_ifuncs` to be +// included on the link step. The constructor walks the `ifunc_sec` section, +// calling the resolver function and storing the result in foo's descriptor. +// On AIX, the address of a function is the address of its descriptor, so the +// constructor accesses foo's descriptor from the first field of the pair. +// +// Since the global `__update_foo` is unreferenced, it's liveness needs to be +// associated to the liveness of ifunc `foo` +// +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 = cast<Function>( + M.getOrInsertFunction(IFuncConstructorName, IFuncConstructorFnType) + .getCallee()); + + for (GlobalIFunc &IFunc : M.ifuncs()) { + NumIFuncs++; + LLVM_DEBUG(dbgs() << "expanding ifunc " << IFunc.getName() << "\n"); + // @__update_foo = private global { ptr @foo, ptr @foo_resolver }, + // section "ifunc_sec" + 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->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)); + + // Liveness of __update_foo is dependent on liveness of ifunc foo. + IFunc.setMetadata(LLVMContext::MD_implicit_ref, + MDNode::get(Ctx, ValueAsMetadata::get(GV))); + + // An implicit.ref creates linkage dependency, so make function foo require + // the constructor that calls each ifunc's resolver and saves the result in + // the ifunc's function descriptor. + IFunc.addMetadata( + LLVMContext::MD_implicit_ref, + *MDNode::get(Ctx, ValueAsMetadata::get(IFuncConstructorDecl))); + } + + return true; +} diff --git a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp index 4ff489d..ccfb5391 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); } @@ -438,6 +439,9 @@ void PPCPassConfig::addIRPasses() { addPass(createLICMPass()); } + if (TM->getTargetTriple().isOSAIX()) + addPass(createPPCPrepareIFuncsOnAIXPass()); + TargetPassConfig::addIRPasses(); } |
