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

Daniel Kiss via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 11 00:44:32 PDT 2024


https://github.com/DanielKristofKiss created https://github.com/llvm/llvm-project/pull/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 and #111960

>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] 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



More information about the cfe-commits mailing list