diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 03ed6accf700c..cef9f5578fa2b 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -455,6 +455,9 @@ def TargetMicrosoftCXXABI : TargetArch<["x86", "x86_64", "arm", "thumb", "aarch6 def TargetELF : TargetSpec { let ObjectFormats = ["ELF"]; } +def TargetELFOrMachO : TargetSpec { + let ObjectFormats = ["ELF", "MachO"]; +} def TargetSupportsInitPriority : TargetSpec { let CustomCode = [{ !Target.getTriple().isOSzOS() }]; @@ -1665,7 +1668,7 @@ def IBOutletCollection : InheritableAttr { let Documentation = [Undocumented]; } -def IFunc : Attr, TargetSpecificAttr { +def IFunc : Attr, TargetSpecificAttr { let Spellings = [GCC<"ifunc">]; let Args = [StringArgument<"Resolver">]; let Subjects = SubjectList<[Function]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index be74535e28d8a..4c4c4eb606fb0 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -5408,7 +5408,9 @@ considered inline. Not all targets support this attribute. ELF target support depends on both the linker and runtime linker, and is available in at least lld 4.0 and later, binutils 2.20.1 and later, glibc v2.11.1 and later, and FreeBSD 9.1 and later. -Non-ELF targets currently do not support this attribute. +MachO targets support it, but with slightly different semantics: the resolver is +run at first call, instead of at load time by the runtime linker. Targets other +than ELF and MachO currently do not support this attribute. }]; } diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 41f3c2e403cbe..1fe2a18cd5dc9 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1424,6 +1424,8 @@ class TargetInfo : public TransferrableTargetInfo, /// Identify whether this target supports IFuncs. bool supportsIFunc() const { + if (getTriple().isOSBinFormatMachO()) + return true; return getTriple().isOSBinFormatELF() && ((getTriple().isOSLinux() && !getTriple().isMusl()) || getTriple().isOSFreeBSD()); diff --git a/clang/test/CodeGen/attr-target-mv-va-args.c b/clang/test/CodeGen/attr-target-mv-va-args.c index 96821c610235b..dbf5a74205c4c 100644 --- a/clang/test/CodeGen/attr-target-mv-va-args.c +++ b/clang/test/CodeGen/attr-target-mv-va-args.c @@ -3,6 +3,7 @@ // RUN: %clang_cc1 -triple x86_64-windows-pc -emit-llvm %s -o - | FileCheck %s --check-prefixes=NO-IFUNC,WINDOWS // RUN: %clang_cc1 -triple x86_64-linux-musl -emit-llvm %s -o - | FileCheck %s --check-prefixes=NO-IFUNC,NO-IFUNC-ELF // RUN: %clang_cc1 -triple x86_64-fuchsia -emit-llvm %s -o - | FileCheck %s --check-prefixes=NO-IFUNC,NO-IFUNC-ELF +// RUN: %clang_cc1 -triple x86_64-apple-macho -emit-llvm %s -o - | FileCheck %s --check-prefix=IFUNC-MACHO int __attribute__((target("sse4.2"))) foo(int i, ...) { return 0; } int __attribute__((target("arch=sandybridge"))) foo(int i, ...); int __attribute__((target("arch=ivybridge"))) foo(int i, ...) {return 1;} @@ -30,6 +31,24 @@ int bar(void) { // IFUNC-ELF: ret ptr @foo // IFUNC-ELF: declare i32 @foo.arch_sandybridge(i32 noundef, ...) +// IFUNC-MACHO: @foo.ifunc = weak_odr ifunc i32 (i32, ...), ptr @foo.resolver +// IFUNC-MACHO: define{{.*}} i32 @foo.sse4.2(i32 noundef %i, ...) +// IFUNC-MACHO: ret i32 0 +// IFUNC-MACHO: define{{.*}} i32 @foo.arch_ivybridge(i32 noundef %i, ...) +// IFUNC-MACHO: ret i32 1 +// IFUNC-MACHO: define{{.*}} i32 @foo(i32 noundef %i, ...) +// IFUNC-MACHO: ret i32 2 +// IFUNC-MACHO: define{{.*}} i32 @bar() +// IFUNC-MACHO: call i32 (i32, ...) @foo.ifunc(i32 noundef 1, i32 noundef 97, double +// IFUNC-MACHO: call i32 (i32, ...) @foo.ifunc(i32 noundef 2, double noundef 2.2{{[0-9Ee+]+}}, ptr noundef + +// IFUNC-MACHO: define weak_odr ptr @foo.resolver() +// IFUNC-MACHO: ret ptr @foo.arch_sandybridge +// IFUNC-MACHO: ret ptr @foo.arch_ivybridge +// IFUNC-MACHO: ret ptr @foo.sse4.2 +// IFUNC-MACHO: ret ptr @foo +// IFUNC-MACHO: declare i32 @foo.arch_sandybridge(i32 noundef, ...) + // NO-IFUNC: define dso_local i32 @foo.sse4.2(i32 noundef %i, ...) // NO-IFUNC: ret i32 0 // NO-IFUNC: define dso_local i32 @foo.arch_ivybridge(i32 noundef %i, ...) diff --git a/clang/test/CodeGen/ifunc.c b/clang/test/CodeGen/ifunc.c index 0b0a0549620f8..99d60dc0ea85d 100644 --- a/clang/test/CodeGen/ifunc.c +++ b/clang/test/CodeGen/ifunc.c @@ -3,6 +3,10 @@ // RUN: %clang_cc1 -triple i386-unknown-linux-gnu -fsanitize=thread -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN // RUN: %clang_cc1 -triple i386-unknown-linux-gnu -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN // RUN: %clang_cc1 -triple i386-unknown-linux-gnu -fsanitize=memory -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN +// RUN: %clang_cc1 -triple x86_64-apple-macosx -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx -O2 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx -fsanitize=thread -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=MACSAN +// RUN: %clang_cc1 -triple x86_64-apple-macosx -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=MACSAN int foo(int) __attribute__ ((ifunc("foo_ifunc"))); @@ -44,9 +48,13 @@ void* goo_ifunc(void) { // CHECK: call void @goo() // SAN: define internal nonnull ptr @foo_ifunc() #[[#FOO_IFUNC:]] { +// MACSAN: define internal nonnull ptr @foo_ifunc() #[[#FOO_IFUNC:]] { // SAN: define dso_local noalias ptr @goo_ifunc() #[[#GOO_IFUNC:]] { +// MACSAN: define noalias ptr @goo_ifunc() #[[#GOO_IFUNC:]] { // SAN-DAG: attributes #[[#FOO_IFUNC]] = {{{.*}} disable_sanitizer_instrumentation {{.*}} +// MACSAN-DAG: attributes #[[#FOO_IFUNC]] = {{{.*}} disable_sanitizer_instrumentation {{.*}} // SAN-DAG: attributes #[[#GOO_IFUNC]] = {{{.*}} disable_sanitizer_instrumentation {{.*}} +// MACSAN-DAG: attributes #[[#GOO_IFUNC]] = {{{.*}} disable_sanitizer_instrumentation {{.*}} diff --git a/compiler-rt/lib/builtins/cpu_model.c b/compiler-rt/lib/builtins/cpu_model.c index b0ec5e51e96d4..b0306f5f36baa 100644 --- a/compiler-rt/lib/builtins/cpu_model.c +++ b/compiler-rt/lib/builtins/cpu_model.c @@ -948,6 +948,8 @@ _Bool __aarch64_have_lse_atomics #if defined(__has_include) #if __has_include() #include +#define HAVE_SYS_AUXV +#endif #if __has_include() #include @@ -961,6 +963,8 @@ typedef struct __ifunc_arg_t { #if __has_include() #include +#include HAVE_SYS_HWCAP +#endif #if defined(__ANDROID__) #include @@ -997,6 +1001,9 @@ typedef struct __ifunc_arg_t { #ifndef HWCAP_SHA2 #define HWCAP_SHA2 (1 << 6) #endif +#ifndef HWCAP_CRC32 +#define HWCAP_CRC32 (1 << 7) +#endif #ifndef HWCAP_ATOMICS #define HWCAP_ATOMICS (1 << 8) #endif @@ -1149,6 +1156,7 @@ typedef struct __ifunc_arg_t { if (__system_property_get("ro.arch", arch) > 0 && \ strncmp(arch, "exynos9810", sizeof("exynos9810") - 1) == 0) +#if !defined(__APPLE__) static void CONSTRUCTOR_ATTRIBUTE init_have_lse_atomics(void) { #if defined(__FreeBSD__) unsigned long hwcap; @@ -1162,7 +1170,7 @@ static void CONSTRUCTOR_ATTRIBUTE init_have_lse_atomics(void) { zx_status_t status = _zx_system_get_features(ZX_FEATURE_KIND_CPU, &features); __aarch64_have_lse_atomics = status == ZX_OK && (features & ZX_ARM64_FEATURE_ISA_ATOMICS) != 0; -#else +#elif defined(HAVE_SYS_AUXV) unsigned long hwcap = getauxval(AT_HWCAP); _Bool result = (hwcap & HWCAP_ATOMICS) != 0; #if defined(__ANDROID__) @@ -1180,8 +1188,11 @@ static void CONSTRUCTOR_ATTRIBUTE init_have_lse_atomics(void) { } #endif // defined(__ANDROID__) __aarch64_have_lse_atomics = result; +#else +#error No support for checking for lse atomics on this platfrom yet. #endif // defined(__FreeBSD__) } +#endif // !defined(__APPLE__) #if !defined(DISABLE_AARCH64_FMV) // CPUFeatures must correspond to the same AArch64 features in @@ -1259,6 +1270,64 @@ struct { // As features grows new fields could be added } __aarch64_cpu_features __attribute__((visibility("hidden"), nocommon)); +#if defined(__APPLE__) +#include +#if TARGET_OS_OSX || TARGET_OS_IPHONE +#include +#include + +static bool isKnownAndSupported(const char *name) { + int32_t val = 0; + size_t size = sizeof(val); + if (sysctlbyname(name, &val, &size, NULL, 0)) + return false; + return val; +} + +void __init_cpu_features_resolver(void) { + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + // https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics + static struct { + const char *sysctl_name; + enum CPUFeatures feature; + } Features[] = { + {"hw.optional.arm.FEAT_FlagM", FEAT_FLAGM}, + {"hw.optional.arm.FEAT_FlagM2", FEAT_FLAGM2}, + {"hw.optional.arm.FEAT_FHM", FEAT_FP16FML}, + {"hw.optional.arm.FEAT_DotProd", FEAT_DOTPROD}, + {"hw.optional.arm.FEAT_RDM", FEAT_RDM}, + {"hw.optional.arm.FEAT_LSE", FEAT_LSE}, + {"hw.optional.floatingpoint", FEAT_FP}, + {"hw.optional.AdvSIMD", FEAT_SIMD}, + {"hw.optional.armv8_crc32", FEAT_CRC}, + {"hw.optional.arm.FEAT_SHA1", FEAT_SHA1}, + {"hw.optional.arm.FEAT_SHA256", FEAT_SHA2}, + {"hw.optional.armv8_2_sha3", FEAT_SHA3}, + {"hw.optional.arm.FEAT_AES", FEAT_AES}, + {"hw.optional.arm.FEAT_PMULL", FEAT_PMULL}, + {"hw.optional.arm.FEAT_FP16", FEAT_FP16}, + {"hw.optional.arm.FEAT_JSCVT", FEAT_JSCVT}, + {"hw.optional.arm.FEAT_FCMA", FEAT_FCMA}, + {"hw.optional.arm.FEAT_LRCPC", FEAT_RCPC}, + {"hw.optional.arm.FEAT_LRCPC2", FEAT_RCPC2}, + {"hw.optional.arm.FEAT_FRINTTS", FEAT_FRINTTS}, + {"hw.optional.arm.FEAT_I8MM", FEAT_I8MM}, + {"hw.optional.arm.FEAT_BF16", FEAT_BF16}, + {"hw.optional.arm.FEAT_SB", FEAT_SB}, + {"hw.optional.arm.FEAT_SSBS", FEAT_SSBS2}, + {"hw.optional.arm.FEAT_BTI", FEAT_BTI}, + }; + + for (size_t I = 0, E = sizeof(Features) / sizeof(Features[0]); I != E; ++I) + if (isKnownAndSupported(Features[I].sysctl_name)) + __aarch64_cpu_features.features |= (1ULL << Features[I].feature); + + __aarch64_cpu_features.features |= (1ULL << FEAT_INIT); + }); +} +#endif // TARGET_OS_OSX || TARGET_OS_IPHONE +#else // defined(__APPLE__) static void __init_cpu_features_constructor(unsigned long hwcap, const __ifunc_arg_t *arg) { #define setCPUFeature(F) __aarch64_cpu_features.features |= 1ULL << F @@ -1467,8 +1536,8 @@ void __init_cpu_features_resolver(unsigned long hwcap, } void CONSTRUCTOR_ATTRIBUTE __init_cpu_features(void) { - unsigned long hwcap; - unsigned long hwcap2; + unsigned long hwcap = 0; + unsigned long hwcap2 = 0; // CPU features already initialized. if (__aarch64_cpu_features.features) return; @@ -1478,7 +1547,7 @@ void CONSTRUCTOR_ATTRIBUTE __init_cpu_features(void) { res |= elf_aux_info(AT_HWCAP2, &hwcap2, sizeof hwcap2); if (res) return; -#else +#elif defined(HAVE_SYS_AUXV) #if defined(__ANDROID__) // Don't set any CPU features, // detection could be wrong on Exynos 9810. @@ -1486,6 +1555,8 @@ void CONSTRUCTOR_ATTRIBUTE __init_cpu_features(void) { #endif // defined(__ANDROID__) hwcap = getauxval(AT_HWCAP); hwcap2 = getauxval(AT_HWCAP2); +#else +#error No support for checking hwcap on this platform yet. #endif // defined(__FreeBSD__) __ifunc_arg_t arg; arg._size = sizeof(__ifunc_arg_t); @@ -1497,8 +1568,7 @@ void CONSTRUCTOR_ATTRIBUTE __init_cpu_features(void) { #undef setCPUFeature #undef IF_EXYNOS9810 } +#endif // defined(__APPLE__) #endif // !defined(DISABLE_AARCH64_FMV) #endif // defined(__has_include) -#endif // __has_include() -#endif // __has_include() #endif // defined(__aarch64__) diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index e448c5ed5c5d9..cb222e979db29 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -934,10 +934,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 +MachO platforms, they are lowered in terms of ``.symbol_resolver``s, which +lazily resolve the callee the first time they are called. IFunc may have an optional :ref:`linkage type ` and an optional :ref:`visibility style `. diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index 2731ef452c79c..48fa6c478464c 100644 --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -882,7 +882,11 @@ class AsmPrinter : public MachineFunctionPass { GCMetadataPrinter *getOrCreateGCPrinter(GCStrategy &S); void emitGlobalAlias(Module &M, const GlobalAlias &GA); - void emitGlobalIFunc(Module &M, const GlobalIFunc &GI); + +protected: + virtual 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; diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp index 2527b14312896..e0080b145d4f9 100644 --- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp @@ -144,7 +144,12 @@ bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, const CallBase &CB, // Try looking through a bitcast from one function type to another. // Commonly happens with calls to objc_msgSend(). const Value *CalleeV = CB.getCalledOperand()->stripPointerCasts(); - if (const Function *F = dyn_cast(CalleeV)) + if (const GlobalIFunc *IF = dyn_cast(CalleeV); + IF && MF.getTarget().getTargetTriple().isOSBinFormatMachO()) { + // ld64 requires that .symbol_resolvers to be called via a stub, so these + // must always be a diret call. + Info.Callee = MachineOperand::CreateGA(IF, 0); + } else if (const Function *F = dyn_cast(CalleeV)) Info.Callee = MachineOperand::CreateGA(F, 0); else Info.Callee = MachineOperand::CreateReg(GetCalleeReg(), false); diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 5560c037aa3ee..94e76a43bf38d 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -959,6 +959,7 @@ void Verifier::visitGlobalIFunc(const GlobalIFunc &GI) { GlobalIFunc::getResolverFunctionType(GI.getValueType()); Check(ResolverTy == ResolverFuncTy->getPointerTo(GI.getAddressSpace()), "IFunc resolver has incorrect type", &GI); + } void Verifier::visitNamedMDNode(const NamedMDNode &NMD) { @@ -2239,13 +2240,10 @@ 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); - } + 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); diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index f4d3a85f34c88..2dab8e126c9ab 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -30,6 +30,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/BinaryFormat/MachO.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/FaultMaps.h" #include "llvm/CodeGen/MachineBasicBlock.h" @@ -47,10 +48,12 @@ #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" @@ -68,6 +71,22 @@ using namespace llvm; namespace { +enum class IFuncLowering { SymbolResolverIfSupported, SymbolResolverAlways, SymbolResolverNever }; + +static cl::opt PreferredIFuncLowering( + "arm64-darwin-ifunc-symbol_resolver", cl::init(IFuncLowering::SymbolResolverNever), + cl::desc("Pick the lowering for ifuncs on darwin platforms"), cl::Hidden, + cl::values( + clEnumValN( + IFuncLowering::SymbolResolverIfSupported, "if_supported", + "Use .symbol_resolver's when known to be supported by the linker."), + clEnumValN(IFuncLowering::SymbolResolverAlways, "always", + "Always use .symbol_resolvers. NOTE: this might not be " + "supported by the linker in all cases."), + clEnumValN(IFuncLowering::SymbolResolverNever, "never", + "Use a manual lowering, doing what the linker would have " + "done, but in the compiler."))); + class AArch64AsmPrinter : public AsmPrinter { AArch64MCInstLower MCInstLowering; FaultMaps FM; @@ -198,6 +217,11 @@ class AArch64AsmPrinter : public AsmPrinter { bool shouldEmitWeakSwiftAsyncExtendedFramePointerFlags() const override { return ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags; } + + void emitGlobalIFunc(Module &M, const GlobalIFunc &GI) override; + + void emitLinkerSymbolResolver(Module &M, const GlobalIFunc &GI); + void emitManualSymbolResolver(Module &M, const GlobalIFunc &GI); }; } // end anonymous namespace @@ -1809,6 +1833,290 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { EmitToStreamer(*OutStreamer, TmpInst); } +void AArch64AsmPrinter::emitLinkerSymbolResolver(Module &M, + const GlobalIFunc &GI) { + OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection()); + + MCSymbol *Name = getSymbol(&GI); + + // NOTE: non-global .symbol_resolvers are not yet supported by Darwin linkers + + 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"); + + OutStreamer->emitCodeAlignment(Align(4), STI); + OutStreamer->emitLabel(Name); + OutStreamer->emitSymbolAttribute(Name, MCSA_SymbolResolver); + emitVisibility(Name, GI.getVisibility()); + + // ld-prime does not seem to support aliases of symbol resolvers, so we have to + // tail call the resolver manually. + OutStreamer->emitInstruction( + MCInstBuilder(AArch64::B) + .addOperand(MCOperand::createExpr(lowerConstant(GI.getResolver()))), + *STI); +} + +/// \brief 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. +void AArch64AsmPrinter::emitManualSymbolResolver(Module &M, + const GlobalIFunc &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"); + }; + + MCSymbol *LazyPointer = TM.getObjFileLowering()->getContext().getOrCreateSymbol( + "_" + GI.getName() + ".lazy_pointer"); + MCSymbol *StubHelper = + TM.getObjFileLowering()->getContext().getOrCreateSymbol( + "_" + GI.getName() + ".stub_helper"); + + OutStreamer->switchSection(OutContext.getObjectFileInfo()->getDataSection()); + + EmitLinkage(LazyPointer); + OutStreamer->emitLabel(LazyPointer); + emitVisibility(LazyPointer, GI.getVisibility()); + OutStreamer->emitValue(MCSymbolRefExpr::create(StubHelper, OutContext), 8); + + OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection()); + + MCSymbol *Stub = getSymbol(&GI); + + EmitLinkage(Stub); + OutStreamer->emitCodeAlignment(Align(4), STI); + OutStreamer->emitLabel(Stub); + emitVisibility(Stub, GI.getVisibility()); + + // adrp x16, lazy_pointer@GOTPAGE + // ldr x16, [x16, lazy_pointer@GOTPAGEOFF] + // ldr x16, [x16] + // br x16 + + { + MCInst Adrp; + Adrp.setOpcode(AArch64::ADRP); + Adrp.addOperand(MCOperand::createReg(AArch64::X16)); + MCOperand SymPage; + MCInstLowering.lowerOperand( + MachineOperand::CreateES(LazyPointer->getName().data() + 1, + AArch64II::MO_GOT | AArch64II::MO_PAGE), + SymPage); + Adrp.addOperand(SymPage); + OutStreamer->emitInstruction(Adrp, *STI); + } + + { + MCInst Ldr; + Ldr.setOpcode(AArch64::LDRXui); + Ldr.addOperand(MCOperand::createReg(AArch64::X16)); + Ldr.addOperand(MCOperand::createReg(AArch64::X16)); + MCOperand SymPageOff; + MCInstLowering.lowerOperand( + MachineOperand::CreateES(LazyPointer->getName().data() + 1, + AArch64II::MO_GOT | AArch64II::MO_PAGEOFF), + SymPageOff); + Ldr.addOperand(SymPageOff); + Ldr.addOperand(MCOperand::createImm(0)); + OutStreamer->emitInstruction(Ldr, *STI); + } + + OutStreamer->emitInstruction(MCInstBuilder(AArch64::LDRXui) + .addReg(AArch64::X16) + .addReg(AArch64::X16) + .addImm(0), *STI); + + OutStreamer->emitInstruction(MCInstBuilder(TM.getTargetTriple().isArm64e() + ? AArch64::BRAAZ + : AArch64::BR) + .addReg(AArch64::X16), + *STI); + + EmitLinkage(StubHelper); + OutStreamer->emitCodeAlignment(Align(4), STI); + OutStreamer->emitLabel(StubHelper); + emitVisibility(StubHelper, GI.getVisibility()); + + // stp fp, lr, [sp, #-16]! + // mov fp, sp + // stp x1, x0, [sp, #-16]! + // stp x3, x2, [sp, #-16]! + // stp x5, x4, [sp, #-16]! + // stp x7, x6, [sp, #-16]! + // stp d1, d0, [sp, #-16]! + // stp d3, d2, [sp, #-16]! + // stp d5, d4, [sp, #-16]! + // stp d7, d6, [sp, #-16]! + // bl _resolver + // adrp x16, lazy_pointer@GOTPAGE + // ldr x16, [x16, lazy_pointer@GOTPAGEOFF] + // str x0, [x16] + // mov x16, x0 + // ldp d7, d6, [sp], #16 + // ldp d5, d4, [sp], #16 + // ldp d3, d2, [sp], #16 + // ldp d1, d0, [sp], #16 + // ldp x7, x6, [sp], #16 + // ldp x5, x4, [sp], #16 + // ldp x3, x2, [sp], #16 + // ldp x1, x0, [sp], #16 + // ldp fp, lr, [sp], #16 + // br x16 + + OutStreamer->emitInstruction(MCInstBuilder(AArch64::STPXpre) + .addReg(AArch64::SP) + .addReg(AArch64::FP) + .addReg(AArch64::LR) + .addReg(AArch64::SP) + .addImm(-2), + *STI); + + OutStreamer->emitInstruction(MCInstBuilder(AArch64::ADDXri) + .addReg(AArch64::FP) + .addReg(AArch64::SP) + .addImm(0) + .addImm(0), + *STI); + + for (int I = 0; I != 4; ++I) + OutStreamer->emitInstruction(MCInstBuilder(AArch64::STPXpre) + .addReg(AArch64::SP) + .addReg(AArch64::X1 + 2 * I) + .addReg(AArch64::X0 + 2 * I) + .addReg(AArch64::SP) + .addImm(-2), + *STI); + + for (int I = 0; I != 4; ++I) + OutStreamer->emitInstruction(MCInstBuilder(AArch64::STPDpre) + .addReg(AArch64::SP) + .addReg(AArch64::D1 + 2 * I) + .addReg(AArch64::D0 + 2 * I) + .addReg(AArch64::SP) + .addImm(-2), + *STI); + + OutStreamer->emitInstruction( + MCInstBuilder(AArch64::BL) + .addOperand(MCOperand::createExpr(lowerConstant(GI.getResolver()))), + *STI); + + { + MCInst Adrp; + Adrp.setOpcode(AArch64::ADRP); + Adrp.addOperand(MCOperand::createReg(AArch64::X16)); + MCOperand SymPage; + MCInstLowering.lowerOperand( + MachineOperand::CreateES(LazyPointer->getName().data() + 1, + AArch64II::MO_GOT | AArch64II::MO_PAGE), + SymPage); + Adrp.addOperand(SymPage); + OutStreamer->emitInstruction(Adrp, *STI); + } + + { + MCInst Ldr; + Ldr.setOpcode(AArch64::LDRXui); + Ldr.addOperand(MCOperand::createReg(AArch64::X16)); + Ldr.addOperand(MCOperand::createReg(AArch64::X16)); + MCOperand SymPageOff; + MCInstLowering.lowerOperand( + MachineOperand::CreateES(LazyPointer->getName().data() + 1, + AArch64II::MO_GOT | AArch64II::MO_PAGEOFF), + SymPageOff); + Ldr.addOperand(SymPageOff); + Ldr.addOperand(MCOperand::createImm(0)); + OutStreamer->emitInstruction(Ldr, *STI); + } + + OutStreamer->emitInstruction(MCInstBuilder(AArch64::STRXui) + .addReg(AArch64::X0) + .addReg(AArch64::X16) + .addImm(0), + *STI); + + OutStreamer->emitInstruction(MCInstBuilder(AArch64::ADDXri) + .addReg(AArch64::X16) + .addReg(AArch64::X0) + .addImm(0) + .addImm(0), + *STI); + + for (int I = 3; I != -1; --I) + OutStreamer->emitInstruction(MCInstBuilder(AArch64::LDPDpost) + .addReg(AArch64::SP) + .addReg(AArch64::D1 + 2 * I) + .addReg(AArch64::D0 + 2 * I) + .addReg(AArch64::SP) + .addImm(2), + *STI); + + for (int I = 3; I != -1; --I) + OutStreamer->emitInstruction(MCInstBuilder(AArch64::LDPXpost) + .addReg(AArch64::SP) + .addReg(AArch64::X1 + 2 * I) + .addReg(AArch64::X0 + 2 * I) + .addReg(AArch64::SP) + .addImm(2), + *STI); + + OutStreamer->emitInstruction(MCInstBuilder(AArch64::LDPXpost) + .addReg(AArch64::SP) + .addReg(AArch64::FP) + .addReg(AArch64::LR) + .addReg(AArch64::SP) + .addImm(2), + *STI); + + OutStreamer->emitInstruction(MCInstBuilder(TM.getTargetTriple().isArm64e() + ? AArch64::BRAAZ + : AArch64::BR) + .addReg(AArch64::X16), + *STI); +} + +void AArch64AsmPrinter::emitGlobalIFunc(Module &M, const GlobalIFunc &GI) { + if (!TM.getTargetTriple().isOSBinFormatMachO()) + return AsmPrinter::emitGlobalIFunc(M, GI); + + switch (PreferredIFuncLowering) { + case IFuncLowering::SymbolResolverAlways: + return emitLinkerSymbolResolver(M, GI); + case IFuncLowering::SymbolResolverNever: + return emitManualSymbolResolver(M, GI); + case IFuncLowering::SymbolResolverIfSupported: + if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective()) + return emitLinkerSymbolResolver(M, GI); + else if (GI.hasWeakLinkage() || GI.hasLinkOnceLinkage()) + // NOTE: non-global .symbol_resolvers are not yet supported by Darwin + // linkers + return emitManualSymbolResolver(M, GI); + else + assert(GI.hasLocalLinkage() && "Invalid ifunc linkage"); + } +} + // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAArch64AsmPrinter() { RegisterAsmPrinter X(getTheAArch64leTarget()); diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp index 73c7450620966..37158900d2404 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -530,6 +530,36 @@ void X86AsmPrinter::PrintIntelMemReference(const MachineInstr *MI, O << ']'; } +void X86AsmPrinter::emitGlobalIFunc(Module &M, const GlobalIFunc &GI) { + if (!TM.getTargetTriple().isOSBinFormatMachO()) + return AsmPrinter::emitGlobalIFunc(M, GI); + + OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection()); + + MCSymbol *Name = getSymbol(&GI); + + 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"); + + OutStreamer->emitCodeAlignment(Align(16), Subtarget); + OutStreamer->emitLabel(Name); + OutStreamer->emitSymbolAttribute(Name, MCSA_SymbolResolver); + emitVisibility(Name, GI.getVisibility()); + + // ld64 does not seem to support aliases of symbol resolvers, so we have to + // tail call the resolver manually. + MCInst JMP; + JMP.setOpcode(X86::JMP_4); + JMP.addOperand(MCOperand::createExpr(lowerConstant(GI.getResolver()))); + OutStreamer->emitInstruction(JMP, *Subtarget); + + // FIXME: do the manual .symbol_resolver lowering that we did in AArch64AsmPrinter. +} + static bool printAsmMRegister(const X86AsmPrinter &P, const MachineOperand &MO, char Mode, raw_ostream &O) { Register Reg = MO.getReg(); diff --git a/llvm/lib/Target/X86/X86AsmPrinter.h b/llvm/lib/Target/X86/X86AsmPrinter.h index c81651cf7f2f0..47f3b7c00c99d 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.h +++ b/llvm/lib/Target/X86/X86AsmPrinter.h @@ -120,6 +120,7 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter { const char *Modifier); void PrintIntelMemReference(const MachineInstr *MI, unsigned OpNo, raw_ostream &O, const char *Modifier); + void emitGlobalIFunc(Module &M, const GlobalIFunc &GI) override; public: X86AsmPrinter(TargetMachine &TM, std::unique_ptr Streamer); diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/call-lowering-ifunc.ll b/llvm/test/CodeGen/AArch64/GlobalISel/call-lowering-ifunc.ll new file mode 100644 index 0000000000000..8e51845c2faa9 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/call-lowering-ifunc.ll @@ -0,0 +1,37 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +; RUN: llc -mtriple=aarch64-macho -global-isel -stop-after=irtranslator -verify-machineinstrs -o - %s | FileCheck %s --check-prefixes=MACHO,CHECK +; RUN: llc -mtriple=aarch64-elf -global-isel -stop-after=irtranslator -verify-machineinstrs -o - %s | FileCheck %s --check-prefixes=ELF,CHECK + +@foo_ifunc = ifunc i32 (i32), ptr @foo_resolver + +define internal ptr @foo_resolver() { + ; CHECK-LABEL: name: foo_resolver + ; CHECK: bb.1.entry: + ; CHECK-NEXT: [[C:%[0-9]+]]:_(p0) = G_CONSTANT i64 0 + ; CHECK-NEXT: $x0 = COPY [[C]](p0) + ; CHECK-NEXT: RET_ReallyLR implicit $x0 +entry: + ret ptr null +} + +define void @caller() { + ; MACHO-LABEL: name: caller + ; MACHO: bb.1.entry: + ; MACHO-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + ; MACHO-NEXT: BL @foo_ifunc, csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit-def $w0 + ; MACHO-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + ; MACHO-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0 + ; MACHO-NEXT: RET_ReallyLR + ; + ; ELF-LABEL: name: caller + ; ELF: bb.1.entry: + ; ELF-NEXT: [[GV:%[0-9]+]]:gpr64(p0) = G_GLOBAL_VALUE @foo_ifunc + ; ELF-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + ; ELF-NEXT: BLR [[GV]](p0), csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit-def $w0 + ; ELF-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + ; ELF-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0 + ; ELF-NEXT: RET_ReallyLR +entry: + %0 = call i32 @foo_ifunc() + ret void +} diff --git a/llvm/test/CodeGen/AArch64/addrsig-macho.ll b/llvm/test/CodeGen/AArch64/addrsig-macho.ll index 360876fccaad3..980b0e7bc4466 100644 --- a/llvm/test/CodeGen/AArch64/addrsig-macho.ll +++ b/llvm/test/CodeGen/AArch64/addrsig-macho.ll @@ -118,8 +118,8 @@ declare void @f3() unnamed_addr @a1 = alias i32, i32* @g1 @a2 = internal local_unnamed_addr alias i32, i32* @g2 -@i1 = ifunc void(), void()* ()* @f1 -@i2 = internal local_unnamed_addr ifunc void(), void()* ()* @f2 +@i1 = external ifunc void(), void()* ()* @f1 +@i2 = external local_unnamed_addr ifunc void(), void()* ()* @f2 declare void @llvm.dbg.value(metadata, metadata, metadata) diff --git a/llvm/test/CodeGen/AArch64/ifunc-asm.ll b/llvm/test/CodeGen/AArch64/ifunc-asm.ll new file mode 100644 index 0000000000000..fbc0f74cee46b --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ifunc-asm.ll @@ -0,0 +1,82 @@ +; RUN: llc -mtriple=arm64-unknown-linux-gnu %s -filetype=asm -o - | FileCheck %s --check-prefixes=ELF +; RUN: llc -mtriple=arm64-apple-darwin %s -filetype=asm -o - -arm64-darwin-ifunc-symbol_resolver=always | FileCheck %s --check-prefixes=MACHO,MACHO-LINKER +; RUN: llc -mtriple=arm64-apple-darwin %s -filetype=asm -o - -arm64-darwin-ifunc-symbol_resolver=if_supported | FileCheck %s --check-prefixes=MACHO,MACHO-DEFAULT +; RUN: llc -mtriple=arm64-apple-darwin %s -filetype=asm -o - -arm64-darwin-ifunc-symbol_resolver=never | FileCheck %s --check-prefixes=MACHO,MACHO-MANUAL +; RUN: llc -mtriple=arm64-apple-darwin %s -filetype=asm -o - | FileCheck %s --check-prefixes=MACHO,MACHO-MANUAL + +define internal ptr @foo_resolver() { +entry: + ret ptr null +} +; ELF: .type foo_resolver,@function +; ELF-NEXT: foo_resolver: + +; MACHO: .p2align 2 +; MACHO-NEXT: _foo_resolver + + +@foo_ifunc = ifunc i32 (i32), ptr @foo_resolver +; ELF: .globl foo_ifunc +; ELF-NEXT: .type foo_ifunc,@gnu_indirect_function +; ELF-NEXT: .set foo_ifunc, foo_resolver + +; MACHO-LINKER: .globl _foo_ifunc +; MACHO-LINKER-NEXT: .p2align 2 +; MACHO-LINKER-NEXT: _foo_ifunc: +; MACHO-LINKER-NEXT: .symbol_resolver _foo_ifunc +; MACHO-LINKER-NEXT: b _foo_resolver + +; MACHO-DEFAULT: .globl _foo_ifunc +; MACHO-DEFAULT-NEXT: .p2align 2 +; MACHO-DEFAULT-NEXT: _foo_ifunc: +; MACHO-DEFAULT-NEXT: .symbol_resolver _foo_ifunc +; MACHO-DEFAULT-NEXT: b _foo_resolver + +; MACHO-MANUAL: .section __DATA,__data +; MACHO-MANUAL-NEXT: .globl _foo_ifunc.lazy_pointer +; MACHO-MANUAL-NEXT: _foo_ifunc.lazy_pointer: +; MACHO-MANUAL-NEXT: .quad _foo_ifunc.stub_helper + +; MACHO-MANUAL: .section __TEXT,__text,regular,pure_instructions +; MACHO-MANUAL-NEXT: .globl _foo_ifunc +; MACHO-MANUAL-NEXT: .p2align 2 +; MACHO-MANUAL-NEXT: _foo_ifunc: +; MACHO-MANUAL-NEXT: adrp x16, _foo_ifunc.lazy_pointer@GOTPAGE +; MACHO-MANUAL-NEXT: ldr x16, [x16, _foo_ifunc.lazy_pointer@GOTPAGEOFF] +; MACHO-MANUAL-NEXT: ldr x16, [x16] +; MACHO-MANUAL-NEXT: br x16 +; MACHO-MANUAL-NEXT: .globl _foo_ifunc.stub_helper +; MACHO-MANUAL-NEXT: .p2align 2 +; MACHO-MANUAL-NEXT: _foo_ifunc.stub_helper: +; MACHO-MANUAL-NEXT: stp x29, x30, [sp, #-16]! +; MACHO-MANUAL-NEXT: mov x29, sp +; MACHO-MANUAL-NEXT: stp x1, x0, [sp, #-16]! +; MACHO-MANUAL-NEXT: stp x3, x2, [sp, #-16]! +; MACHO-MANUAL-NEXT: stp x5, x4, [sp, #-16]! +; MACHO-MANUAL-NEXT: stp x7, x6, [sp, #-16]! +; MACHO-MANUAL-NEXT: stp d1, d0, [sp, #-16]! +; MACHO-MANUAL-NEXT: stp d3, d2, [sp, #-16]! +; MACHO-MANUAL-NEXT: stp d5, d4, [sp, #-16]! +; MACHO-MANUAL-NEXT: stp d7, d6, [sp, #-16]! +; MACHO-MANUAL-NEXT: bl _foo_resolver +; MACHO-MANUAL-NEXT: adrp x16, _foo_ifunc.lazy_pointer@GOTPAGE +; MACHO-MANUAL-NEXT: ldr x16, [x16, _foo_ifunc.lazy_pointer@GOTPAGEOFF] +; MACHO-MANUAL-NEXT: str x0, [x16] +; MACHO-MANUAL-NEXT: add x16, x0, #0 +; MACHO-MANUAL-NEXT: ldp d7, d6, [sp], #16 +; MACHO-MANUAL-NEXT: ldp d5, d4, [sp], #16 +; MACHO-MANUAL-NEXT: ldp d3, d2, [sp], #16 +; MACHO-MANUAL-NEXT: ldp d1, d0, [sp], #16 +; MACHO-MANUAL-NEXT: ldp x7, x6, [sp], #16 +; MACHO-MANUAL-NEXT: ldp x5, x4, [sp], #16 +; MACHO-MANUAL-NEXT: ldp x3, x2, [sp], #16 +; MACHO-MANUAL-NEXT: ldp x1, x0, [sp], #16 +; MACHO-MANUAL-NEXT: ldp x29, x30, [sp], #16 +; MACHO-MANUAL-NEXT: br x16 + + +@weak_ifunc = weak ifunc i32 (i32), ptr @foo_resolver +; ELF: .type weak_ifunc,@gnu_indirect_function +; MACHO-LINKER: .symbol_resolver _weak_ifunc +; MACHO-MANUAL: _weak_ifunc.stub_helper: +; MACHO-DEFEAULT: _weak_ifunc.stub_helper: \ No newline at end of file diff --git a/llvm/test/CodeGen/X86/ifunc-asm.ll b/llvm/test/CodeGen/X86/ifunc-asm.ll index 4b380c8ae3301..76efda7115320 100644 --- a/llvm/test/CodeGen/X86/ifunc-asm.ll +++ b/llvm/test/CodeGen/X86/ifunc-asm.ll @@ -1,14 +1,24 @@ -; RUN: llvm-as < %s -o - | llc -filetype=asm | FileCheck %s +; RUN: llc -filetype=asm -mtriple=x86_64-unknown-linux-gnu %s -o - | FileCheck %s --check-prefixes=ELF +; RUN: llc -filetype=asm -mtriple=x86_64-apple-darwin %s -o - | FileCheck %s --check-prefixes=MACHO -target triple = "x86_64-unknown-linux-gnu" - -define internal ptr @foo_ifunc() { +define internal ptr @foo_resolver() { entry: ret ptr null } -; CHECK: .type foo_ifunc,@function -; CHECK-NEXT: foo_ifunc: +; ELF: .type foo_resolver,@function +; ELF-NEXT: foo_resolver: + +; MACHO: .p2align 4, 0x90 +; MACHO-NEXT: _foo_resolver + + +@foo_ifunc = ifunc i32 (i32), ptr @foo_resolver +; ELF: .globl foo_ifunc +; ELF-NEXT: .type foo_ifunc,@gnu_indirect_function +; ELF-NEXT: .set foo_ifunc, foo_resolver -@foo = ifunc i32 (i32), ptr @foo_ifunc -; CHECK: .type foo,@gnu_indirect_function -; CHECK-NEXT: .set foo, foo_ifunc +; MACHO: .globl _foo_ifunc +; MACHO-NEXT: .p2align 4, 0x90 +; MACHO-NEXT: _foo_ifunc: +; MACHO-NEXT: .symbol_resolver _foo_ifunc +; MACHO-NEXT: jmp _foo_resolver diff --git a/llvm/test/Verifier/ifunc-macho.ll b/llvm/test/Verifier/ifunc-macho.ll new file mode 100644 index 0000000000000..2e2166645983a --- /dev/null +++ b/llvm/test/Verifier/ifunc-macho.ll @@ -0,0 +1,42 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +target triple = "arm64-apple-ios" + +define ptr @resolver() { + ret ptr null +} + +@g = external global i32 +@inval_objtype = ifunc void (), ptr @g +; CHECK: IFunc must have a Function resolver + +declare ptr @resolver_decl() +@inval_resolver_decl = ifunc void (), ptr @resolver_decl +; CHECK: IFunc resolver must be a definition +; CHECK-NEXT: @inval_resolver_decl + +define available_externally ptr @resolver_linker_decl() { + ret ptr null +} +@inval_resolver_decl2 = ifunc void (), ptr @resolver_linker_decl +; CHECK: IFunc resolver must be a definition +; CHECK-NEXT: @inval_resolver_decl2 + +@ifunc_nonpointer_return_type = ifunc i32 (), ptr @resolver_returns_nonpointer +; CHECK: IFunc resolver must return a pointer +; CHECK-NEXT: ptr @ifunc_nonpointer_return_type + +define i32 @resolver_returns_nonpointer() { + ret i32 0 +} + +@valid_external = ifunc void (), ptr @resolver +; CHECK-NOT: valid_external + +@inval_linkonce = linkonce ifunc void (), ptr @resolver + +@inval_weak = weak ifunc void (), ptr @resolver + +@inval_weak_extern = extern_weak ifunc void (), ptr @resolver + +@inval_private = private ifunc void (), ptr @resolver