Skip to content

Commit 3f40ad7

Browse files
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
1 parent 9f06129 commit 3f40ad7

File tree

7 files changed

+100
-8
lines changed

7 files changed

+100
-8
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,9 @@ def TargetELF : TargetSpec {
477477
def TargetELFOrMachO : TargetSpec {
478478
let ObjectFormats = ["ELF", "MachO"];
479479
}
480+
def TargetIFuncSupport : TargetSpec {
481+
let CustomCode = [{ Target.supportsIFunc() }];
482+
}
480483
def TargetWindowsArm64EC : TargetSpec {
481484
let CustomCode = [{ Target.getTriple().isWindowsArm64EC() }];
482485
}
@@ -1855,7 +1858,7 @@ def IBOutletCollection : InheritableAttr {
18551858
let Documentation = [Undocumented];
18561859
}
18571860

1858-
def IFunc : Attr, TargetSpecificAttr<TargetELFOrMachO> {
1861+
def IFunc : Attr, TargetSpecificAttr<TargetIFuncSupport> {
18591862
let Spellings = [GCC<"ifunc">];
18601863
let Args = [StringArgument<"Resolver">];
18611864
let Subjects = SubjectList<[Function]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6051,12 +6051,19 @@ declared entity. The entity must not have weak linkage; for example, in C++,
60516051
it cannot be applied to a declaration if a definition at that location would be
60526052
considered inline.
60536053

6054-
Not all targets support this attribute. ELF target support depends on both the
6055-
linker and runtime linker, and is available in at least lld 4.0 and later,
6056-
binutils 2.20.1 and later, glibc v2.11.1 and later, and FreeBSD 9.1 and later.
6057-
Mach-O targets support it, but with slightly different semantics: the resolver
6058-
is run at first call, instead of at load time by the runtime linker. Targets
6059-
other than ELF and Mach-O currently do not support this attribute.
6054+
Not all targets support this attribute:
6055+
6056+
- ELF target support depends on both the linker and runtime linker, and is
6057+
available in at least lld 4.0 and later, binutils 2.20.1 and later, glibc
6058+
v2.11.1 and later, and FreeBSD 9.1 and later.
6059+
- Mach-O targets support it, but with slightly different semantics: the resolver
6060+
is run at first call, instead of at load time by the runtime linker.
6061+
- Windows target supports it on AArch64, but with different semantics: the
6062+
``ifunc`` is replaced with a global function pointer, and the call is replaced
6063+
with an indirect call. The function pointer is initialized by a constructor
6064+
that calls the resolver.
6065+
- Baremetal target supports it on AVR.
6066+
- Other targets currently do not support this attribute.
60606067
}];
60616068
}
60626069

clang/include/clang/Basic/TargetInfo.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,10 @@ class TargetInfo : public TransferrableTargetInfo,
14971497
bool supportsIFunc() const {
14981498
if (getTriple().isOSBinFormatMachO())
14991499
return true;
1500+
if (getTriple().isOSWindows() && getTriple().isAArch64())
1501+
return true;
1502+
if (getTriple().getArch() == llvm::Triple::ArchType::avr)
1503+
return true;
15001504
return getTriple().isOSBinFormatELF() &&
15011505
((getTriple().isOSLinux() && !getTriple().isMusl()) ||
15021506
getTriple().isOSFreeBSD());

clang/test/CodeGen/attr-ifunc.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// RUN: %clang_cc1 -triple x86_64-linux -verify -emit-llvm-only -DCHECK_ALIASES %s
33
// RUN: %clang_cc1 -triple x86_64-linux -verify -emit-llvm-only %s
44
// RUN: %clang_cc1 -triple x86_64-apple-macosx -verify -emit-llvm-only %s
5+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -verify -emit-llvm-only %s
6+
// RUN: %clang_cc1 -triple aarch64-pc-windows-msvcu -verify -emit-llvm-only %s
57

6-
#if defined(_WIN32)
8+
#if defined(_WIN32) && !defined(__aarch64__)
79
void foo(void) {}
810
void bar(void) __attribute__((ifunc("foo")));
911
// expected-warning@-1 {{unknown attribute 'ifunc' ignored}}

clang/test/CodeGen/ifunc-win.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -emit-llvm -o - %s | FileCheck %s
2+
// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -O2 -emit-llvm -o - %s | FileCheck %s
3+
// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -fsanitize=thread -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
4+
// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
5+
6+
int foo(int) __attribute__ ((ifunc("foo_ifunc")));
7+
8+
static int f1(int i) {
9+
return i + 1;
10+
}
11+
12+
static int f2(int i) {
13+
return i + 2;
14+
}
15+
16+
typedef int (*foo_t)(int);
17+
18+
volatile int global;
19+
20+
static foo_t foo_ifunc(void) {
21+
return global ? f1 : f2;
22+
}
23+
24+
int bar(void) {
25+
return foo(1);
26+
}
27+
28+
extern void goo(void);
29+
30+
void bar2(void) {
31+
goo();
32+
}
33+
34+
extern void goo(void) __attribute__ ((ifunc("goo_ifunc")));
35+
36+
void* goo_ifunc(void) {
37+
return 0;
38+
}
39+
40+
/// The ifunc is emitted after its resolver.
41+
void *hoo_ifunc(void) { return 0; }
42+
extern void hoo(int) __attribute__ ((ifunc("hoo_ifunc")));
43+
44+
/// ifunc on Windows is lowered to global pointers and an indirect call.
45+
// CHECK: @global = dso_local global i32 0, align 4
46+
// CHECK: {{.*}} = internal{{.*}}global{{.*}}poison, align 8
47+
/// Register the constructor for initialisation.
48+
// CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 10, ptr @{{.*}}, ptr null }]
49+
50+
// CHECK-LABEL: @bar()
51+
// CHECK %0 = load ptr, ptr @0, align 8
52+
// CHECK %call = call i32 %0(i32 noundef 1)
53+
54+
// CHECK-LABEL: @bar2()
55+
// CHECK %0 = load ptr, ptr getelementptr inbounds ([3 x ptr], ptr @0, i32 0, i32 1), align 8
56+
// CHECK call void %0()
57+
58+
// CHECK: define internal void @{{.*}}()
59+
60+
// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @goo_ifunc() {{(local_unnamed_addr )?}}
61+
62+
// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @hoo_ifunc() {{(local_unnamed_addr )?}}
63+
64+
// SAN: define internal {{(fastcc )?}}{{(noundef )?}}nonnull ptr @foo_ifunc() {{(unnamed_addr )?}}
65+

clang/test/CodeGen/ifunc.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
// RUN: %clang_cc1 -triple arm64-apple-macosx -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
1313
// RUN: %clang_cc1 -triple x86_64-apple-macosx -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
1414
// RUN: %clang_cc1 -triple avr-unknown-unknown -emit-llvm -o - %s | FileCheck %s --check-prefix=AVR
15+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -o - %s | FileCheck %s
16+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -O2 -emit-llvm -o - %s | FileCheck %s
17+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=thread -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
18+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
19+
1520

1621
/// The ifunc is emitted before its resolver.
1722
int foo(int) __attribute__ ((ifunc("foo_ifunc")));

llvm/lib/Target/AArch64/AArch64TargetMachine.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include "llvm/TargetParser/Triple.h"
5050
#include "llvm/Transforms/CFGuard.h"
5151
#include "llvm/Transforms/Scalar.h"
52+
#include "llvm/Transforms/Utils/LowerIFunc.h"
5253
#include "llvm/Transforms/Vectorize/LoopIdiomVectorize.h"
5354
#include <memory>
5455
#include <optional>
@@ -570,6 +571,11 @@ void AArch64TargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) {
570571
[=](LoopPassManager &LPM, OptimizationLevel Level) {
571572
LPM.addPass(LoopIdiomVectorizePass());
572573
});
574+
if (getTargetTriple().isOSWindows())
575+
PB.registerPipelineEarlySimplificationEPCallback(
576+
[](ModulePassManager &PM, OptimizationLevel, ThinOrFullLTOPhase) {
577+
PM.addPass(LowerIFuncPass());
578+
});
573579
}
574580

575581
TargetTransformInfo

0 commit comments

Comments
 (0)