Skip to content

[llvm] Support IFuncs on Darwin platforms #73686

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -959,10 +959,11 @@ IFuncs
-------

IFuncs, like as aliases, don't create any new data or func. They are just a new
symbol that dynamic linker resolves at runtime by calling a resolver function.
symbol that is resolved at runtime by calling a resolver function.

IFuncs have a name and a resolver that is a function called by dynamic linker
that returns address of another function associated with the name.
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>`.
Expand Down
23 changes: 22 additions & 1 deletion llvm/include/llvm/CodeGen/AsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,26 @@ class AsmPrinter : public MachineFunctionPass {
/// instructions in verbose mode.
virtual void emitImplicitDef(const MachineInstr *MI) const;

/// getSubtargetInfo() cannot be used where this is needed because we don't
/// have a MachineFunction when we're lowering a GlobalIFunc, and
/// getSubtargetInfo requires one. Override the implementation in targets
/// that support the Mach-O IFunc lowering.
virtual const MCSubtargetInfo *getIFuncMCSubtargetInfo() const {
return nullptr;
}

virtual void emitMachOIFuncStubBody(Module &M, const GlobalIFunc &GI,
MCSymbol *LazyPointer) {
llvm_unreachable(
"Mach-O IFunc lowering is not yet supported on this target");
}

virtual void emitMachOIFuncStubHelperBody(Module &M, const GlobalIFunc &GI,
MCSymbol *LazyPointer) {
llvm_unreachable(
"Mach-O IFunc lowering is not yet supported on this target");
}

/// Emit N NOP instructions.
void emitNops(unsigned N);

Expand All @@ -614,7 +634,7 @@ class AsmPrinter : public MachineFunctionPass {
StringRef Suffix) const;

/// Return the MCSymbol for the specified ExternalSymbol.
MCSymbol *GetExternalSymbolSymbol(StringRef Sym) const;
MCSymbol *GetExternalSymbolSymbol(Twine Sym) const;

/// Return the symbol for the specified jump table entry.
MCSymbol *GetJTISymbol(unsigned JTID, bool isLinkerPrivate = false) const;
Expand Down Expand Up @@ -884,6 +904,7 @@ class AsmPrinter : public MachineFunctionPass {
void emitGlobalAlias(Module &M, const GlobalAlias &GA);
void emitGlobalIFunc(Module &M, const GlobalIFunc &GI);

private:
/// This method decides whether the specified basic block requires a label.
bool shouldEmitLabelForBasicBlock(const MachineBasicBlock &MBB) const;

Expand Down
137 changes: 99 additions & 38 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/Config/config.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Comdat.h"
Expand Down Expand Up @@ -2147,24 +2148,80 @@ void AsmPrinter::emitGlobalIFunc(Module &M, const GlobalIFunc &GI) {
assert(!TM.getTargetTriple().isOSBinFormatXCOFF() &&
"IFunc is not supported on AIX.");

MCSymbol *Name = getSymbol(&GI);
auto EmitLinkage = [&](MCSymbol *Sym) {
if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective())
OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);
else if (GI.hasWeakLinkage() || GI.hasLinkOnceLinkage())
OutStreamer->emitSymbolAttribute(Sym, MCSA_WeakReference);
else
assert(GI.hasLocalLinkage() && "Invalid ifunc linkage");
};

if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective())
OutStreamer->emitSymbolAttribute(Name, MCSA_Global);
else if (GI.hasWeakLinkage() || GI.hasLinkOnceLinkage())
OutStreamer->emitSymbolAttribute(Name, MCSA_WeakReference);
else
assert(GI.hasLocalLinkage() && "Invalid ifunc linkage");
if (TM.getTargetTriple().isOSBinFormatELF()) {
MCSymbol *Name = getSymbol(&GI);
EmitLinkage(Name);
OutStreamer->emitSymbolAttribute(Name, MCSA_ELF_TypeIndFunction);
emitVisibility(Name, GI.getVisibility());

// Emit the directives as assignments aka .set:
const MCExpr *Expr = lowerConstant(GI.getResolver());
OutStreamer->emitAssignment(Name, Expr);
MCSymbol *LocalAlias = getSymbolPreferLocal(GI);
if (LocalAlias != Name)
OutStreamer->emitAssignment(LocalAlias, Expr);

return;
}

OutStreamer->emitSymbolAttribute(Name, MCSA_ELF_TypeIndFunction);
emitVisibility(Name, GI.getVisibility());
if (!TM.getTargetTriple().isOSBinFormatMachO() || !getIFuncMCSubtargetInfo())
llvm::report_fatal_error("IFuncs are not supported on this platform");

// Emit the directives as assignments aka .set:
const MCExpr *Expr = lowerConstant(GI.getResolver());
OutStreamer->emitAssignment(Name, Expr);
MCSymbol *LocalAlias = getSymbolPreferLocal(GI);
if (LocalAlias != Name)
OutStreamer->emitAssignment(LocalAlias, Expr);
// On Darwin platforms, emit a manually-constructed .symbol_resolver that
// implements the symbol resolution duties of the IFunc.
//
// Normally, this would be handled by linker magic, but unfortunately there
// are a few limitations in ld64 and ld-prime's implementation of
// .symbol_resolver that mean we can't always use them:
//
// * resolvers cannot be the target of an alias
// * resolvers cannot have private linkage
// * resolvers cannot have linkonce linkage
// * resolvers cannot appear in executables
// * resolvers cannot appear in bundles
//
// This works around that by emitting a close approximation of what the
// linker would have done.

MCSymbol *LazyPointer =
GetExternalSymbolSymbol(GI.getName() + ".lazy_pointer");
MCSymbol *StubHelper = GetExternalSymbolSymbol(GI.getName() + ".stub_helper");

OutStreamer->switchSection(OutContext.getObjectFileInfo()->getDataSection());

const DataLayout &DL = M.getDataLayout();
emitAlignment(Align(DL.getPointerSize()));
OutStreamer->emitLabel(LazyPointer);
emitVisibility(LazyPointer, GI.getVisibility());
OutStreamer->emitValue(MCSymbolRefExpr::create(StubHelper, OutContext), 8);

OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection());

const TargetSubtargetInfo *STI =
TM.getSubtargetImpl(*GI.getResolverFunction());
const TargetLowering *TLI = STI->getTargetLowering();
Align TextAlign(TLI->getMinFunctionAlignment());

MCSymbol *Stub = getSymbol(&GI);
EmitLinkage(Stub);
OutStreamer->emitCodeAlignment(TextAlign, getIFuncMCSubtargetInfo());
OutStreamer->emitLabel(Stub);
emitVisibility(Stub, GI.getVisibility());
emitMachOIFuncStubBody(M, GI, LazyPointer);

OutStreamer->emitCodeAlignment(TextAlign, getIFuncMCSubtargetInfo());
OutStreamer->emitLabel(StubHelper);
emitVisibility(StubHelper, GI.getVisibility());
emitMachOIFuncStubHelperBody(M, GI, LazyPointer);
}

void AsmPrinter::emitRemarksSection(remarks::RemarkStreamer &RS) {
Expand Down Expand Up @@ -2311,6 +2368,32 @@ bool AsmPrinter::doFinalization(Module &M) {
// through user plugins.
emitStackMaps();

// Print aliases in topological order, that is, for each alias a = b,
// b must be printed before a.
// This is because on some targets (e.g. PowerPC) linker expects aliases in
// such an order to generate correct TOC information.
SmallVector<const GlobalAlias *, 16> AliasStack;
SmallPtrSet<const GlobalAlias *, 16> AliasVisited;
for (const auto &Alias : M.aliases()) {
if (Alias.hasAvailableExternallyLinkage())
continue;
for (const GlobalAlias *Cur = &Alias; Cur;
Cur = dyn_cast<GlobalAlias>(Cur->getAliasee())) {
if (!AliasVisited.insert(Cur).second)
break;
AliasStack.push_back(Cur);
}
for (const GlobalAlias *AncestorAlias : llvm::reverse(AliasStack))
emitGlobalAlias(M, *AncestorAlias);
AliasStack.clear();
}

// IFuncs must come before deubginfo in case the backend decides to emit them
// as actual functions, since on Mach-O targets, we cannot create regular
// sections after DWARF.
for (const auto &IFunc : M.ifuncs())
emitGlobalIFunc(M, IFunc);

// Finalize debug and EH information.
for (const HandlerInfo &HI : Handlers) {
NamedRegionTimer T(HI.TimerName, HI.TimerDescription, HI.TimerGroupName,
Expand Down Expand Up @@ -2350,28 +2433,6 @@ bool AsmPrinter::doFinalization(Module &M) {
}
}

// Print aliases in topological order, that is, for each alias a = b,
// b must be printed before a.
// This is because on some targets (e.g. PowerPC) linker expects aliases in
// such an order to generate correct TOC information.
SmallVector<const GlobalAlias *, 16> AliasStack;
SmallPtrSet<const GlobalAlias *, 16> AliasVisited;
for (const auto &Alias : M.aliases()) {
if (Alias.hasAvailableExternallyLinkage())
continue;
for (const GlobalAlias *Cur = &Alias; Cur;
Cur = dyn_cast<GlobalAlias>(Cur->getAliasee())) {
if (!AliasVisited.insert(Cur).second)
break;
AliasStack.push_back(Cur);
}
for (const GlobalAlias *AncestorAlias : llvm::reverse(AliasStack))
emitGlobalAlias(M, *AncestorAlias);
AliasStack.clear();
}
for (const auto &IFunc : M.ifuncs())
emitGlobalIFunc(M, IFunc);

GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>();
assert(MI && "AsmPrinter didn't require GCModuleInfo?");
for (GCModuleInfo::iterator I = MI->end(), E = MI->begin(); I != E; )
Expand Down Expand Up @@ -3745,7 +3806,7 @@ MCSymbol *AsmPrinter::getSymbolWithGlobalValueBase(const GlobalValue *GV,
}

/// Return the MCSymbol for the specified ExternalSymbol.
MCSymbol *AsmPrinter::GetExternalSymbolSymbol(StringRef Sym) const {
MCSymbol *AsmPrinter::GetExternalSymbolSymbol(Twine Sym) const {
SmallString<60> NameStr;
Mangler::getNameWithPrefix(NameStr, Sym, getDataLayout());
return OutContext.getOrCreateSymbol(NameStr);
Expand Down
12 changes: 5 additions & 7 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2229,13 +2229,11 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
}

// Check EVEX512 feature.
if (MaxParameterWidth >= 512 && Attrs.hasFnAttr("target-features")) {
Triple T(M.getTargetTriple());
if (T.isX86()) {
StringRef TF = Attrs.getFnAttr("target-features").getValueAsString();
Check(!TF.contains("+avx512f") || !TF.contains("-evex512"),
"512-bit vector arguments require 'evex512' for AVX512", V);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drive-by fix. We don't need to create a Triple, since we already have one.

if (MaxParameterWidth >= 512 && Attrs.hasFnAttr("target-features") &&
TT.isX86()) {
StringRef TF = Attrs.getFnAttr("target-features").getValueAsString();
Check(!TF.contains("+avx512f") || !TF.contains("-evex512"),
"512-bit vector arguments require 'evex512' for AVX512", V);
}

checkUnsignedBaseTenFuncAttr(Attrs, "patchable-function-prefix", V);
Expand Down
Loading