Author: Jon Roelofs Date: 2023-11-28T10:39:44-08:00 New Revision: bc152095691b32d1ad8539dfd60f5089df5eed8d
URL: https://github.com/llvm/llvm-project/commit/bc152095691b32d1ad8539dfd60f5089df5eed8d DIFF: https://github.com/llvm/llvm-project/commit/bc152095691b32d1ad8539dfd60f5089df5eed8d.diff LOG: [𝘀𝗽𝗿] initial version Created using spr 1.3.4 Added: llvm/test/CodeGen/AArch64/GlobalISel/call-lowering-ifunc.ll llvm/test/CodeGen/AArch64/ifunc-asm.ll llvm/test/Verifier/ifunc-macho.ll Modified: llvm/docs/LangRef.rst llvm/include/llvm/CodeGen/AsmPrinter.h llvm/lib/CodeGen/GlobalISel/CallLowering.cpp llvm/lib/IR/Verifier.cpp llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp llvm/lib/Target/X86/X86AsmPrinter.cpp llvm/lib/Target/X86/X86AsmPrinter.h llvm/test/CodeGen/AArch64/addrsig-macho.ll llvm/test/CodeGen/X86/ifunc-asm.ll Removed: ################################################################################ diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index e448c5ed5c5d947..cb222e979db29d4 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 <linkage>` and an optional :ref:`visibility style <visibility>`. diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index 2731ef452c79cbb..48fa6c478464c73 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 2527b1431289677..e0080b145d4f995 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<Function>(CalleeV)) + if (const GlobalIFunc *IF = dyn_cast<GlobalIFunc>(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<Function>(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 5560c037aa3ee6b..94e76a43bf38d6d 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 f4d3a85f34c88da..2dab8e126c9abd0 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<IFuncLowering> 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<AArch64AsmPrinter> X(getTheAArch64leTarget()); diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp index 73c7450620966cd..5241aa6e1c0eade 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -530,6 +530,34 @@ 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); +} + 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 c81651cf7f2f0e6..47f3b7c00c99ddb 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<MCStreamer> 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 000000000000000..8e51845c2faa9c0 --- /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 360876fccaad34e..980b0e7bc446695 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 000000000000000..fbc0f74cee46baa --- /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 4b380c8ae330135..76efda71153207d 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 000000000000000..2e2166645983ac3 --- /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 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits