Skip to content

Commit 640c1d3

Browse files
authored
[llvm] Support IFuncs on Darwin platforms (#73686)
... by lowering them as lazy resolve-on-first-use symbol resolvers. Note that this is subtly different timing than on ELF platforms, where ifunc resolution happens at load time. Since ld64 and ld-prime don't support all the cases we need for these, we lower them manually in the AsmPrinter.
1 parent 7c6b4be commit 640c1d3

File tree

10 files changed

+558
-58
lines changed

10 files changed

+558
-58
lines changed

llvm/docs/LangRef.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -959,10 +959,11 @@ IFuncs
959959
-------
960960

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

964-
IFuncs have a name and a resolver that is a function called by dynamic linker
965-
that returns address of another function associated with the name.
964+
On ELF platforms, IFuncs are resolved by the dynamic linker at load time. On
965+
Mach-O platforms, they are lowered in terms of ``.symbol_resolver`` functions,
966+
which lazily resolve the callee the first time they are called.
966967

967968
IFunc may have an optional :ref:`linkage type <linkage>` and an optional
968969
:ref:`visibility style <visibility>`.

llvm/include/llvm/CodeGen/AsmPrinter.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,26 @@ class AsmPrinter : public MachineFunctionPass {
599599
/// instructions in verbose mode.
600600
virtual void emitImplicitDef(const MachineInstr *MI) const;
601601

602+
/// getSubtargetInfo() cannot be used where this is needed because we don't
603+
/// have a MachineFunction when we're lowering a GlobalIFunc, and
604+
/// getSubtargetInfo requires one. Override the implementation in targets
605+
/// that support the Mach-O IFunc lowering.
606+
virtual const MCSubtargetInfo *getIFuncMCSubtargetInfo() const {
607+
return nullptr;
608+
}
609+
610+
virtual void emitMachOIFuncStubBody(Module &M, const GlobalIFunc &GI,
611+
MCSymbol *LazyPointer) {
612+
llvm_unreachable(
613+
"Mach-O IFunc lowering is not yet supported on this target");
614+
}
615+
616+
virtual void emitMachOIFuncStubHelperBody(Module &M, const GlobalIFunc &GI,
617+
MCSymbol *LazyPointer) {
618+
llvm_unreachable(
619+
"Mach-O IFunc lowering is not yet supported on this target");
620+
}
621+
602622
/// Emit N NOP instructions.
603623
void emitNops(unsigned N);
604624

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

616636
/// Return the MCSymbol for the specified ExternalSymbol.
617-
MCSymbol *GetExternalSymbolSymbol(StringRef Sym) const;
637+
MCSymbol *GetExternalSymbolSymbol(Twine Sym) const;
618638

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

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

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

Lines changed: 99 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "llvm/CodeGen/TargetLowering.h"
6060
#include "llvm/CodeGen/TargetOpcodes.h"
6161
#include "llvm/CodeGen/TargetRegisterInfo.h"
62+
#include "llvm/CodeGen/TargetSubtargetInfo.h"
6263
#include "llvm/Config/config.h"
6364
#include "llvm/IR/BasicBlock.h"
6465
#include "llvm/IR/Comdat.h"
@@ -2147,24 +2148,80 @@ void AsmPrinter::emitGlobalIFunc(Module &M, const GlobalIFunc &GI) {
21472148
assert(!TM.getTargetTriple().isOSBinFormatXCOFF() &&
21482149
"IFunc is not supported on AIX.");
21492150

2150-
MCSymbol *Name = getSymbol(&GI);
2151+
auto EmitLinkage = [&](MCSymbol *Sym) {
2152+
if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective())
2153+
OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);
2154+
else if (GI.hasWeakLinkage() || GI.hasLinkOnceLinkage())
2155+
OutStreamer->emitSymbolAttribute(Sym, MCSA_WeakReference);
2156+
else
2157+
assert(GI.hasLocalLinkage() && "Invalid ifunc linkage");
2158+
};
21512159

2152-
if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective())
2153-
OutStreamer->emitSymbolAttribute(Name, MCSA_Global);
2154-
else if (GI.hasWeakLinkage() || GI.hasLinkOnceLinkage())
2155-
OutStreamer->emitSymbolAttribute(Name, MCSA_WeakReference);
2156-
else
2157-
assert(GI.hasLocalLinkage() && "Invalid ifunc linkage");
2160+
if (TM.getTargetTriple().isOSBinFormatELF()) {
2161+
MCSymbol *Name = getSymbol(&GI);
2162+
EmitLinkage(Name);
2163+
OutStreamer->emitSymbolAttribute(Name, MCSA_ELF_TypeIndFunction);
2164+
emitVisibility(Name, GI.getVisibility());
2165+
2166+
// Emit the directives as assignments aka .set:
2167+
const MCExpr *Expr = lowerConstant(GI.getResolver());
2168+
OutStreamer->emitAssignment(Name, Expr);
2169+
MCSymbol *LocalAlias = getSymbolPreferLocal(GI);
2170+
if (LocalAlias != Name)
2171+
OutStreamer->emitAssignment(LocalAlias, Expr);
2172+
2173+
return;
2174+
}
21582175

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

2162-
// Emit the directives as assignments aka .set:
2163-
const MCExpr *Expr = lowerConstant(GI.getResolver());
2164-
OutStreamer->emitAssignment(Name, Expr);
2165-
MCSymbol *LocalAlias = getSymbolPreferLocal(GI);
2166-
if (LocalAlias != Name)
2167-
OutStreamer->emitAssignment(LocalAlias, Expr);
2179+
// On Darwin platforms, emit a manually-constructed .symbol_resolver that
2180+
// implements the symbol resolution duties of the IFunc.
2181+
//
2182+
// Normally, this would be handled by linker magic, but unfortunately there
2183+
// are a few limitations in ld64 and ld-prime's implementation of
2184+
// .symbol_resolver that mean we can't always use them:
2185+
//
2186+
// * resolvers cannot be the target of an alias
2187+
// * resolvers cannot have private linkage
2188+
// * resolvers cannot have linkonce linkage
2189+
// * resolvers cannot appear in executables
2190+
// * resolvers cannot appear in bundles
2191+
//
2192+
// This works around that by emitting a close approximation of what the
2193+
// linker would have done.
2194+
2195+
MCSymbol *LazyPointer =
2196+
GetExternalSymbolSymbol(GI.getName() + ".lazy_pointer");
2197+
MCSymbol *StubHelper = GetExternalSymbolSymbol(GI.getName() + ".stub_helper");
2198+
2199+
OutStreamer->switchSection(OutContext.getObjectFileInfo()->getDataSection());
2200+
2201+
const DataLayout &DL = M.getDataLayout();
2202+
emitAlignment(Align(DL.getPointerSize()));
2203+
OutStreamer->emitLabel(LazyPointer);
2204+
emitVisibility(LazyPointer, GI.getVisibility());
2205+
OutStreamer->emitValue(MCSymbolRefExpr::create(StubHelper, OutContext), 8);
2206+
2207+
OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection());
2208+
2209+
const TargetSubtargetInfo *STI =
2210+
TM.getSubtargetImpl(*GI.getResolverFunction());
2211+
const TargetLowering *TLI = STI->getTargetLowering();
2212+
Align TextAlign(TLI->getMinFunctionAlignment());
2213+
2214+
MCSymbol *Stub = getSymbol(&GI);
2215+
EmitLinkage(Stub);
2216+
OutStreamer->emitCodeAlignment(TextAlign, getIFuncMCSubtargetInfo());
2217+
OutStreamer->emitLabel(Stub);
2218+
emitVisibility(Stub, GI.getVisibility());
2219+
emitMachOIFuncStubBody(M, GI, LazyPointer);
2220+
2221+
OutStreamer->emitCodeAlignment(TextAlign, getIFuncMCSubtargetInfo());
2222+
OutStreamer->emitLabel(StubHelper);
2223+
emitVisibility(StubHelper, GI.getVisibility());
2224+
emitMachOIFuncStubHelperBody(M, GI, LazyPointer);
21682225
}
21692226

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

2371+
// Print aliases in topological order, that is, for each alias a = b,
2372+
// b must be printed before a.
2373+
// This is because on some targets (e.g. PowerPC) linker expects aliases in
2374+
// such an order to generate correct TOC information.
2375+
SmallVector<const GlobalAlias *, 16> AliasStack;
2376+
SmallPtrSet<const GlobalAlias *, 16> AliasVisited;
2377+
for (const auto &Alias : M.aliases()) {
2378+
if (Alias.hasAvailableExternallyLinkage())
2379+
continue;
2380+
for (const GlobalAlias *Cur = &Alias; Cur;
2381+
Cur = dyn_cast<GlobalAlias>(Cur->getAliasee())) {
2382+
if (!AliasVisited.insert(Cur).second)
2383+
break;
2384+
AliasStack.push_back(Cur);
2385+
}
2386+
for (const GlobalAlias *AncestorAlias : llvm::reverse(AliasStack))
2387+
emitGlobalAlias(M, *AncestorAlias);
2388+
AliasStack.clear();
2389+
}
2390+
2391+
// IFuncs must come before deubginfo in case the backend decides to emit them
2392+
// as actual functions, since on Mach-O targets, we cannot create regular
2393+
// sections after DWARF.
2394+
for (const auto &IFunc : M.ifuncs())
2395+
emitGlobalIFunc(M, IFunc);
2396+
23142397
// Finalize debug and EH information.
23152398
for (const HandlerInfo &HI : Handlers) {
23162399
NamedRegionTimer T(HI.TimerName, HI.TimerDescription, HI.TimerGroupName,
@@ -2350,28 +2433,6 @@ bool AsmPrinter::doFinalization(Module &M) {
23502433
}
23512434
}
23522435

2353-
// Print aliases in topological order, that is, for each alias a = b,
2354-
// b must be printed before a.
2355-
// This is because on some targets (e.g. PowerPC) linker expects aliases in
2356-
// such an order to generate correct TOC information.
2357-
SmallVector<const GlobalAlias *, 16> AliasStack;
2358-
SmallPtrSet<const GlobalAlias *, 16> AliasVisited;
2359-
for (const auto &Alias : M.aliases()) {
2360-
if (Alias.hasAvailableExternallyLinkage())
2361-
continue;
2362-
for (const GlobalAlias *Cur = &Alias; Cur;
2363-
Cur = dyn_cast<GlobalAlias>(Cur->getAliasee())) {
2364-
if (!AliasVisited.insert(Cur).second)
2365-
break;
2366-
AliasStack.push_back(Cur);
2367-
}
2368-
for (const GlobalAlias *AncestorAlias : llvm::reverse(AliasStack))
2369-
emitGlobalAlias(M, *AncestorAlias);
2370-
AliasStack.clear();
2371-
}
2372-
for (const auto &IFunc : M.ifuncs())
2373-
emitGlobalIFunc(M, IFunc);
2374-
23752436
GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>();
23762437
assert(MI && "AsmPrinter didn't require GCModuleInfo?");
23772438
for (GCModuleInfo::iterator I = MI->end(), E = MI->begin(); I != E; )
@@ -3745,7 +3806,7 @@ MCSymbol *AsmPrinter::getSymbolWithGlobalValueBase(const GlobalValue *GV,
37453806
}
37463807

37473808
/// Return the MCSymbol for the specified ExternalSymbol.
3748-
MCSymbol *AsmPrinter::GetExternalSymbolSymbol(StringRef Sym) const {
3809+
MCSymbol *AsmPrinter::GetExternalSymbolSymbol(Twine Sym) const {
37493810
SmallString<60> NameStr;
37503811
Mangler::getNameWithPrefix(NameStr, Sym, getDataLayout());
37513812
return OutContext.getOrCreateSymbol(NameStr);

llvm/lib/IR/Verifier.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,13 +2229,11 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
22292229
}
22302230

22312231
// Check EVEX512 feature.
2232-
if (MaxParameterWidth >= 512 && Attrs.hasFnAttr("target-features")) {
2233-
Triple T(M.getTargetTriple());
2234-
if (T.isX86()) {
2235-
StringRef TF = Attrs.getFnAttr("target-features").getValueAsString();
2236-
Check(!TF.contains("+avx512f") || !TF.contains("-evex512"),
2237-
"512-bit vector arguments require 'evex512' for AVX512", V);
2238-
}
2232+
if (MaxParameterWidth >= 512 && Attrs.hasFnAttr("target-features") &&
2233+
TT.isX86()) {
2234+
StringRef TF = Attrs.getFnAttr("target-features").getValueAsString();
2235+
Check(!TF.contains("+avx512f") || !TF.contains("-evex512"),
2236+
"512-bit vector arguments require 'evex512' for AVX512", V);
22392237
}
22402238

22412239
checkUnsignedBaseTenFuncAttr(Attrs, "patchable-function-prefix", V);

0 commit comments

Comments
 (0)