diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 24cfb5ddb6d4c..89aa4380d7c57 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -477,6 +477,9 @@ def TargetELF : TargetSpec { def TargetELFOrMachO : TargetSpec { let ObjectFormats = ["ELF", "MachO"]; } +def TargetIFuncSupport : TargetSpec { + let CustomCode = [{ Target.supportsIFunc() }]; +} def TargetWindowsArm64EC : TargetSpec { let CustomCode = [{ Target.getTriple().isWindowsArm64EC() }]; } @@ -1855,7 +1858,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 23c8eb2d163c8..db2d9661a6c27 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -5982,12 +5982,19 @@ declared entity. The entity must not have weak linkage; for example, in C++, it cannot be applied to a declaration if a definition at that location would be 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. -Mach-O 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 Mach-O currently do not support this attribute. +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. +- Mach-O targets support it, but with slightly different semantics: the resolver + is run at first call, instead of at load time by the runtime linker. +- Windows target supports it on AArch64, but with different semantics: the + ``ifunc`` is replaced with a global function pointer, and the call is replaced + with an indirect call. The function pointer is initialized by a constructor + that calls the resolver. +- Baremetal target supports it on AVR. +- Other targets currently do not support this attribute. }]; } diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 25eda907d20a7..2b3552854e1f9 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1497,6 +1497,10 @@ class TargetInfo : public TransferrableTargetInfo, bool supportsIFunc() const { if (getTriple().isOSBinFormatMachO()) return true; + if (getTriple().isOSWindows() && getTriple().isAArch64()) + return true; + if (getTriple().getArch() == llvm::Triple::ArchType::avr) + return true; return getTriple().isOSBinFormatELF() && ((getTriple().isOSLinux() && !getTriple().isMusl()) || getTriple().isOSFreeBSD()); diff --git a/clang/test/CodeGen/attr-ifunc.c b/clang/test/CodeGen/attr-ifunc.c index 24d66433ae090..c9e70b17a8302 100644 --- a/clang/test/CodeGen/attr-ifunc.c +++ b/clang/test/CodeGen/attr-ifunc.c @@ -2,8 +2,10 @@ // RUN: %clang_cc1 -triple x86_64-linux -verify -emit-llvm-only -DCHECK_ALIASES %s // RUN: %clang_cc1 -triple x86_64-linux -verify -emit-llvm-only %s // RUN: %clang_cc1 -triple x86_64-apple-macosx -verify -emit-llvm-only %s +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -verify -emit-llvm-only %s +// RUN: %clang_cc1 -triple aarch64-pc-windows-msvcu -verify -emit-llvm-only %s -#if defined(_WIN32) +#if defined(_WIN32) && !defined(__aarch64__) void foo(void) {} void bar(void) __attribute__((ifunc("foo"))); // expected-warning@-1 {{unknown attribute 'ifunc' ignored}} diff --git a/clang/test/CodeGen/ifunc-win.c b/clang/test/CodeGen/ifunc-win.c new file mode 100644 index 0000000000000..7c9d6f6516c70 --- /dev/null +++ b/clang/test/CodeGen/ifunc-win.c @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -O2 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -fsanitize=thread -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN +// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN + +int foo(int) __attribute__ ((ifunc("foo_ifunc"))); + +static int f1(int i) { + return i + 1; +} + +static int f2(int i) { + return i + 2; +} + +typedef int (*foo_t)(int); + +volatile int global; + +static foo_t foo_ifunc(void) { + return global ? f1 : f2; +} + +int bar(void) { + return foo(1); +} + +extern void goo(void); + +void bar2(void) { + goo(); +} + +extern void goo(void) __attribute__ ((ifunc("goo_ifunc"))); + +void* goo_ifunc(void) { + return 0; +} + +/// The ifunc is emitted after its resolver. +void *hoo_ifunc(void) { return 0; } +extern void hoo(int) __attribute__ ((ifunc("hoo_ifunc"))); + +/// ifunc on Windows is lowered to global pointers and an indirect call. +// CHECK: @global = dso_local global i32 0, align 4 +// CHECK: {{.*}} = internal{{.*}}global{{.*}}poison, align 8 +/// Register the constructor for initialisation. +// CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 10, ptr @{{.*}}, ptr null }] + +// CHECK-LABEL: @bar() +// CHECK %0 = load ptr, ptr @0, align 8 +// CHECK %call = call i32 %0(i32 noundef 1) + +// CHECK-LABEL: @bar2() +// CHECK %0 = load ptr, ptr getelementptr inbounds ([3 x ptr], ptr @0, i32 0, i32 1), align 8 +// CHECK call void %0() + +// CHECK: define internal void @{{.*}}() + +// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @goo_ifunc() {{(local_unnamed_addr )?}} + +// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @hoo_ifunc() {{(local_unnamed_addr )?}} + +// SAN: define internal {{(fastcc )?}}{{(noundef )?}}nonnull ptr @foo_ifunc() {{(unnamed_addr )?}} + diff --git a/clang/test/CodeGen/ifunc.c b/clang/test/CodeGen/ifunc.c index 2849246f93dc3..7d21f742e8676 100644 --- a/clang/test/CodeGen/ifunc.c +++ b/clang/test/CodeGen/ifunc.c @@ -12,6 +12,11 @@ // RUN: %clang_cc1 -triple arm64-apple-macosx -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN // RUN: %clang_cc1 -triple x86_64-apple-macosx -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN // RUN: %clang_cc1 -triple avr-unknown-unknown -emit-llvm -o - %s | FileCheck %s --check-prefix=AVR +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -O2 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=thread -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN + /// The ifunc is emitted before its resolver. int foo(int) __attribute__ ((ifunc("foo_ifunc"))); diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp index e24a874e74970..99f5885677787 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -51,6 +51,7 @@ #include "llvm/TargetParser/Triple.h" #include "llvm/Transforms/CFGuard.h" #include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/LowerIFunc.h" #include "llvm/Transforms/Vectorize/LoopIdiomVectorize.h" #include #include @@ -572,6 +573,11 @@ void AArch64TargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { [=](LoopPassManager &LPM, OptimizationLevel Level) { LPM.addPass(LoopIdiomVectorizePass()); }); + if (getTargetTriple().isOSWindows()) + PB.registerPipelineEarlySimplificationEPCallback( + [](ModulePassManager &PM, OptimizationLevel, ThinOrFullLTOPhase) { + PM.addPass(LowerIFuncPass()); + }); } TargetTransformInfo