[clang] [llvm] Add ifunc support for Windows on AArch64. (PR #111962)

Daniel Kiss via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 8 03:41:31 PST 2024


https://github.com/DanielKristofKiss updated https://github.com/llvm/llvm-project/pull/111962

>From 122864160f098392c4afd1728eb924e7e26e70d9 Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.kiss at arm.com>
Date: Tue, 8 Oct 2024 22:58:37 +0200
Subject: [PATCH 1/6] Add ifuncs support for Windows.

---
 clang/include/clang/Basic/Attr.td             |  5 +-
 clang/include/clang/Basic/AttrDocs.td         |  6 +-
 clang/include/clang/Basic/TargetInfo.h        |  4 ++
 clang/test/CodeGen/attr-ifunc.c               |  4 +-
 clang/test/CodeGen/ifunc-win.c                | 69 +++++++++++++++++++
 clang/test/CodeGen/ifunc.c                    |  5 ++
 .../Target/AArch64/AArch64TargetMachine.cpp   |  6 ++
 7 files changed, 95 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGen/ifunc-win.c

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index ec3d6e0079f630..c0a540d3890487 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() }];
 }
@@ -1847,7 +1850,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 b1512e22ee2dd4..5179f30400345d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5800,8 +5800,10 @@ 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.
+is run at first call, instead of at load time by the runtime linker. Windows on
+AArch64 the ``ifunc`` is replaced with global function pointer and the call is
+replaced by an indirect call. The pointer is initialized by a contructor that
+calls the resolver. 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 57783850606290..9d2d7be7cfbd78 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1505,6 +1505,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..d8a94df90fc603
--- /dev/null
+++ b/clang/test/CodeGen/ifunc-win.c
@@ -0,0 +1,69 @@
+// 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
+
+/// The ifunc is emitted before its resolver.
+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")));
+
+/// No ifunc on Windows, lowerred to global pointers.
+// CHECK: @global = dso_local global i32 0, align 4
+// CHECK: {{.*}} = internal{{.*}}global{{.*}}poison, align 8
+/// Register the contructor 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 @{{.*}}() #[[#CONSTRUCTOR:]] {
+// CHECK-DAG: attributes #[[#CONSTRUCTOR]] = {{{.*}} disable_sanitizer_instrumentation {{.*}}
+
+// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @goo_ifunc() {{(local_unnamed_addr )?}}#[[#GOO_IFUNC:]] {
+
+// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @hoo_ifunc() {{(local_unnamed_addr )?}}#[[#GOO_IFUNC]] {
+
+// SAN: define internal {{(fastcc )?}}{{(noundef )?}}nonnull ptr @foo_ifunc() {{(unnamed_addr )?}}#[[#FOO_IFUNC:]] {
+
+// SAN-DAG: attributes #[[#GOO_IFUNC]] = {{{.*}} disable_sanitizer_instrumentation {{.*}}
+// SAN-DAG: attributes #[[#FOO_IFUNC]] = {{{.*}} disable_sanitizer_instrumentation {{.*}}
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 7b0ae23358673e..a5c7c51627a4d5 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 <memory>
 #include <optional>
@@ -566,6 +567,11 @@ void AArch64TargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) {
       [=](LoopPassManager &LPM, OptimizationLevel Level) {
         LPM.addPass(LoopIdiomVectorizePass());
       });
+  if (getTargetTriple().isOSWindows())
+    PB.registerPipelineEarlySimplificationEPCallback(
+        [](ModulePassManager &PM, OptimizationLevel Level) {
+          PM.addPass(LowerIFuncPass());
+        });
 }
 
 TargetTransformInfo

>From c9a799386856f533e3706206063dcb6bb9d43435 Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.kiss at arm.com>
Date: Mon, 14 Oct 2024 21:13:41 +0200
Subject: [PATCH 2/6] reword the attribute documentation

---
 clang/include/clang/Basic/AttrDocs.td | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 5179f30400345d..e9b0c14e5f061d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5796,14 +5796,17 @@ 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. Windows on
-AArch64 the ``ifunc`` is replaced with global function pointer and the call is
-replaced by an indirect call. The pointer is initialized by a contructor that
-calls the resolver. Other targets 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.
+- Other targets currently do not support this attribute.
   }];
 }
 

>From 9e0b509b3d8cf4a07f2846a7354e7c70014e9288 Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.kiss at arm.com>
Date: Mon, 14 Oct 2024 21:15:08 +0200
Subject: [PATCH 3/6] drop codegen dependency on santizer patch

---
 clang/test/CodeGen/ifunc-win.c | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/clang/test/CodeGen/ifunc-win.c b/clang/test/CodeGen/ifunc-win.c
index d8a94df90fc603..7c9d6f6516c70f 100644
--- a/clang/test/CodeGen/ifunc-win.c
+++ b/clang/test/CodeGen/ifunc-win.c
@@ -3,7 +3,6 @@
 // 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
 
-/// The ifunc is emitted before its resolver.
 int foo(int) __attribute__ ((ifunc("foo_ifunc")));
 
 static int f1(int i) {
@@ -42,10 +41,10 @@ void* goo_ifunc(void) {
 void *hoo_ifunc(void) { return 0; }
 extern void hoo(int) __attribute__ ((ifunc("hoo_ifunc")));
 
-/// No ifunc on Windows, lowerred to global pointers.
+/// 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 contructor for initialisation.
+/// 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()
@@ -56,14 +55,11 @@ extern void hoo(int) __attribute__ ((ifunc("hoo_ifunc")));
 // 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 @{{.*}}() #[[#CONSTRUCTOR:]] {
-// CHECK-DAG: attributes #[[#CONSTRUCTOR]] = {{{.*}} disable_sanitizer_instrumentation {{.*}}
+// CHECK: define internal void @{{.*}}()
 
-// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @goo_ifunc() {{(local_unnamed_addr )?}}#[[#GOO_IFUNC:]] {
+// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @goo_ifunc() {{(local_unnamed_addr )?}}
 
-// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @hoo_ifunc() {{(local_unnamed_addr )?}}#[[#GOO_IFUNC]] {
+// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @hoo_ifunc() {{(local_unnamed_addr )?}}
 
-// SAN: define internal {{(fastcc )?}}{{(noundef )?}}nonnull ptr @foo_ifunc() {{(unnamed_addr )?}}#[[#FOO_IFUNC:]] {
+// SAN: define internal {{(fastcc )?}}{{(noundef )?}}nonnull ptr @foo_ifunc() {{(unnamed_addr )?}}
 
-// SAN-DAG: attributes #[[#GOO_IFUNC]] = {{{.*}} disable_sanitizer_instrumentation {{.*}}
-// SAN-DAG: attributes #[[#FOO_IFUNC]] = {{{.*}} disable_sanitizer_instrumentation {{.*}}

>From e416733138777e6fba05d37efd712431687c5677 Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.kiss at arm.com>
Date: Tue, 15 Oct 2024 11:58:31 +0200
Subject: [PATCH 4/6] fix doc gen

---
 clang/include/clang/Basic/AttrDocs.td | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index e9b0c14e5f061d..677d8b19471336 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5797,6 +5797,7 @@ 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.

>From 9489668cbb1c445e6ead721573f110d66b37e5c5 Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.kiss at arm.com>
Date: Fri, 8 Nov 2024 12:33:47 +0100
Subject: [PATCH 5/6] Add AVR to the docs

---
 clang/include/clang/Basic/AttrDocs.td | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index f74f1a63d4a9a3..db2d9661a6c272 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5993,6 +5993,7 @@ Not all targets support this attribute:
   ``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.
   }];
 }

>From 7793cf3595b887017935b30cb7b8d903725c2bc5 Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.kiss at arm.com>
Date: Fri, 8 Nov 2024 12:35:28 +0100
Subject: [PATCH 6/6] rebase sideeffect

---
 llvm/lib/Target/AArch64/AArch64TargetMachine.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
index 29089794336613..99f5885677787b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
@@ -575,7 +575,7 @@ void AArch64TargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) {
       });
   if (getTargetTriple().isOSWindows())
     PB.registerPipelineEarlySimplificationEPCallback(
-        [](ModulePassManager &PM, OptimizationLevel Level) {
+        [](ModulePassManager &PM, OptimizationLevel, ThinOrFullLTOPhase) {
           PM.addPass(LowerIFuncPass());
         });
 }



More information about the cfe-commits mailing list