[clang] 3f40ad7 - Add ifunc support for Windows on AArch64. (#111962)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 14 05:45:20 PST 2024
Author: Daniel Kiss
Date: 2024-11-14T14:45:15+01:00
New Revision: 3f40ad7ba83ecf6f374039191ae7ceeb1f5fe831
URL: https://github.com/llvm/llvm-project/commit/3f40ad7ba83ecf6f374039191ae7ceeb1f5fe831
DIFF: https://github.com/llvm/llvm-project/commit/3f40ad7ba83ecf6f374039191ae7ceeb1f5fe831.diff
LOG: Add ifunc support for Windows on AArch64. (#111962)
On Windows there is no platform support for ifunc but we could lower
them to global function pointers.
This also enables FMV for Windows with Clang and Compiler-rt.
Depends on #111961
Added:
clang/test/CodeGen/ifunc-win.c
Modified:
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/TargetInfo.h
clang/test/CodeGen/attr-ifunc.c
clang/test/CodeGen/ifunc.c
llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b3c357ec906a23..6035a563d5fce7 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<TargetELFOrMachO> {
+def IFunc : Attr, TargetSpecificAttr<TargetIFuncSupport> {
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 8044eb80a44a72..2fdceca163ee63 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6051,12 +6051,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
diff erent 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
diff erent 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
diff erent 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 25eda907d20a7b..2b3552854e1f9b 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 24d66433ae090f..c9e70b17a83023 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 at -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 00000000000000..7c9d6f6516c70f
--- /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 2849246f93dc3b..7d21f742e86765 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 e0e559b457f6d6..c588f2ccf42f02 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
@@ -49,6 +49,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 <memory>
#include <optional>
@@ -570,6 +571,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
More information about the cfe-commits
mailing list