Skip to content

Add ifunc support for Windows on AArch64. #111962

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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() }];
}
Expand Down Expand Up @@ -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]>;
Expand Down
19 changes: 13 additions & 6 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}];
}

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
4 changes: 3 additions & 1 deletion clang/test/CodeGen/attr-ifunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down
65 changes: 65 additions & 0 deletions clang/test/CodeGen/ifunc-win.c
Original file line number Diff line number Diff line change
@@ -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 )?}}

5 changes: 5 additions & 0 deletions clang/test/CodeGen/ifunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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")));
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down
Loading