Skip to content

Commit adb5956

Browse files
authored
[clang][SYCL] Align device binary (un)registration with CUDA/HIP/OpenMP (#217173)
The SYCL offload wrapper still registers and unregisters the device binary the way the offloading runtimes did before 421085f (#86830): a constructor and a destructor, both at priority 1. That commit moved OpenMP/CUDA/HIP off a destructor and priority 1, and the reasons apply to SYCL equally. With no destructor left to emit, wrapSYCLBinaries() only ever hands back one function, so shrink its out-parameter to a single Function *. This also lets CodeGenModule::Release() emit the SYCL constructor next to the CUDA one instead of ahead of registerGlobalDtorsWithAtExit(), where it had to sit only because AddGlobalDtor() additions are dropped after that call. co-authored by claude
1 parent 3a7967c commit adb5956

8 files changed

Lines changed: 55 additions & 47 deletions

File tree

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,15 +1177,6 @@ void CodeGenModule::Release() {
11771177
else
11781178
EmitCXXGlobalInitFunc();
11791179
EmitCXXGlobalCleanUpFunc();
1180-
if (LangOpts.SYCLIsHost && !CodeGenOpts.OffloadBinaryToEmbedFile.empty()) {
1181-
auto [SYCLCtorFunction, SYCLDtorFunction] = embedSYCLDeviceBinary();
1182-
if (SYCLCtorFunction) {
1183-
// A static initializer may launch a kernel, so the device binaries have
1184-
// to be registered before any of them run, hence a priority.
1185-
AddGlobalCtor(SYCLCtorFunction, /*Priority=*/1);
1186-
AddGlobalDtor(SYCLDtorFunction, /*Priority=*/1);
1187-
}
1188-
}
11891180
registerGlobalDtorsWithAtExit();
11901181
EmitCXXThreadLocalInitFunc();
11911182
if (ObjCRuntime)
@@ -1195,6 +1186,12 @@ void CodeGenModule::Release() {
11951186
if (llvm::Function *CudaCtorFunction = CUDARuntime->finalizeModule())
11961187
AddGlobalCtor(CudaCtorFunction);
11971188
}
1189+
if (LangOpts.SYCLIsHost && !CodeGenOpts.OffloadBinaryToEmbedFile.empty()) {
1190+
if (llvm::Function *SYCLCtorFunction = embedSYCLDeviceBinary())
1191+
// A static initializer may launch a kernel, so the device binary has to
1192+
// be registered before any of them run, hence a priority.
1193+
AddGlobalCtor(SYCLCtorFunction, /*Priority=*/101);
1194+
}
11981195
if (OpenMPRuntime) {
11991196
OpenMPRuntime->createOffloadEntriesAndInfoMetadata();
12001197
OpenMPRuntime->clear();

clang/lib/CodeGen/CodeGenModule.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,9 +2181,9 @@ class CodeGenModule : public CodeGenTypeCache {
21812181

21822182
/// Embed the finalized SYCL device binary named by -foffload-include-binary
21832183
/// into the host module.
2184-
/// \return the functions that register and unregister the binary with the
2185-
/// runtime, both null if the binary could not be read.
2186-
std::pair<llvm::Function *, llvm::Function *> embedSYCLDeviceBinary();
2184+
/// \return the function that registers the binary with the runtime, or null
2185+
/// if the binary could not be read.
2186+
llvm::Function *embedSYCLDeviceBinary();
21872187

21882188
/// Determine whether the definition must be emitted; if this returns \c
21892189
/// false, the definition can be emitted lazily if it's used.

clang/lib/CodeGen/CodeGenSYCL.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,24 +89,23 @@ void CodeGenModule::EmitSYCLKernelCaller(const FunctionDecl *KernelEntryPointFn,
8989
CGF.FinishFunction();
9090
}
9191

92-
std::pair<llvm::Function *, llvm::Function *>
93-
CodeGenModule::embedSYCLDeviceBinary() {
92+
llvm::Function *CodeGenModule::embedSYCLDeviceBinary() {
9493
StringRef FileName = getCodeGenOpts().OffloadBinaryToEmbedFile;
9594
auto BufferOrErr = getFileSystem()->getBufferForFile(FileName);
9695
if (std::error_code EC = BufferOrErr.getError()) {
9796
getDiags().Report(diag::err_cannot_open_file) << FileName << EC.message();
98-
return {nullptr, nullptr};
97+
return nullptr;
9998
}
10099
std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrErr.get());
101-
std::pair<llvm::Function *, llvm::Function *> RegistrationFuncs;
100+
llvm::Function *RegistrationFunc = nullptr;
102101
if (llvm::Error Err = llvm::offloading::wrapSYCLBinaries(
103102
getModule(),
104103
ArrayRef<char>(Buffer->getBufferStart(), Buffer->getBufferSize()),
105104
llvm::offloading::SYCLJITOptions(), /*IsFinalizedImage=*/true,
106-
&RegistrationFuncs)) {
105+
&RegistrationFunc)) {
107106
getDiags().Report(diag::err_fe_error_backend)
108107
<< llvm::toString(std::move(Err));
109-
return {nullptr, nullptr};
108+
return nullptr;
110109
}
111-
return RegistrationFuncs;
110+
return RegistrationFunc;
112111
}

clang/test/CodeGenSYCL/offload-include-binary.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
// REQUIRES: x86-registered-target
22

33
// Verify that -foffload-include-binary embeds the finalized SYCL device
4-
// binary into the host module and emits the registration/unregistration
5-
// constructors and destructors expected by the SYCL runtime.
4+
// binary into the host module and emits the constructor that registers it with
5+
// the SYCL runtime. Unregistration is done from 'atexit', so no global
6+
// destructor is emitted for it.
67
// The binary is already finalized, so it must not land in ".llvm.offloading".
78
// RUN: echo -n 'FAKE_SYCL_DEVICE_IMAGE' > %t.bin
89
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host \
910
// RUN: -foffload-include-binary %t.bin -emit-llvm %s -o - \
1011
// RUN: | FileCheck %s --implicit-check-not='.llvm.offloading' \
11-
// RUN: --implicit-check-not='llvm.global_ctors.'
12+
// RUN: --implicit-check-not='llvm.global_ctors.' \
13+
// RUN: --implicit-check-not='llvm.global_dtors'
1214

13-
// The registration functions have to merge into the constructor and destructor
14-
// lists the rest of the translation unit contributes to, so object emission
15-
// must succeed for a translation unit that has its own static initializers.
15+
// The registration function has to merge into the constructor list the rest of
16+
// the translation unit contributes to, so object emission must succeed for a
17+
// translation unit that has its own static initializers.
1618
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host \
1719
// RUN: -foffload-include-binary %t.bin -emit-obj %s -o %t.o
1820

@@ -39,11 +41,11 @@ void f() {}
3941
// CHECK: @.sycl_offloading.binary = internal unnamed_addr constant [22 x i8] c"FAKE_SYCL_DEVICE_IMAGE", section ".sycl_fatbin"
4042
// CHECK: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }]
4143
// CHECK-SAME: i32 65535, ptr @_GLOBAL__sub_I_
42-
// CHECK-SAME: i32 1, ptr @sycl.descriptor_reg
43-
// CHECK: @llvm.global_dtors = {{.*}}@sycl.descriptor_unreg
44+
// CHECK-SAME: i32 101, ptr @sycl.descriptor_reg
4445
// CHECK: define internal void @sycl.descriptor_reg()
4546
// CHECK-NEXT: entry:
4647
// CHECK-NEXT: call void @__sycl_register_lib(ptr @.sycl_offloading.binary, i64 22)
48+
// CHECK-NEXT: call i32 @atexit(ptr @sycl.descriptor_unreg)
4749
// CHECK-NEXT: ret void
4850
// CHECK: define internal void @sycl.descriptor_unreg()
4951
// CHECK-NEXT: entry:

clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper-image.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@
316316
// SYCL: define internal void @sycl.descriptor_reg() section ".text.startup" {
317317
// SYCL-NEXT: entry:
318318
// SYCL-NEXT: call void @__sycl_register_lib(ptr @.sycl_offloading.binary, i64 0)
319+
// SYCL-NEXT: %0 = call i32 @atexit(ptr @sycl.descriptor_unreg)
319320
// SYCL-NEXT: ret void
320321
// SYCL-NEXT: }
321322

llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,14 @@ struct SYCLJITOptions {
7070
/// use by a runtime for JIT compilation. Not used for AOT.
7171
/// \param IsFinalizedImage True when \p Buffer holds an already finalized
7272
/// device image, which must not be device-linked again.
73-
/// \param RegistrationFuncs When given, receives the functions that register
74-
/// and unregister the binary with the runtime instead of them being appended
75-
/// to llvm.global_ctors and llvm.global_dtors. A caller has to add them
76-
/// to those lists itself.
77-
LLVM_ABI llvm::Error wrapSYCLBinaries(
78-
llvm::Module &M, llvm::ArrayRef<char> Buffer,
79-
SYCLJITOptions Options = SYCLJITOptions(), bool IsFinalizedImage = false,
80-
std::pair<llvm::Function *, llvm::Function *> *RegistrationFuncs = nullptr);
73+
/// \param RegistrationFunc When given, receives the function that registers the
74+
/// binary with the runtime instead of it being appended to llvm.global_ctors.
75+
/// A caller has to add it to that list itself.
76+
LLVM_ABI llvm::Error
77+
wrapSYCLBinaries(llvm::Module &M, llvm::ArrayRef<char> Buffer,
78+
SYCLJITOptions Options = SYCLJITOptions(),
79+
bool IsFinalizedImage = false,
80+
llvm::Function **RegistrationFunc = nullptr);
8181

8282
} // namespace offloading
8383
} // namespace llvm

llvm/lib/Frontend/Offloading/OffloadWrapper.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -676,13 +676,25 @@ class SYCLWrapper {
676676
FunctionCallee RegFuncC =
677677
M.getOrInsertFunction("__sycl_register_lib", RegFuncTy);
678678

679+
FunctionType *AtExitTy =
680+
FunctionType::get(Type::getInt32Ty(C), PtrTy, /*isVarArg=*/false);
681+
FunctionCallee AtExit = M.getOrInsertFunction("atexit", AtExitTy);
682+
683+
Function *UnregFunc = createUnregisterFunction(Start, Size);
684+
679685
IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
680686
Builder.CreateCall(RegFuncC, {Start, Size});
687+
688+
// Unregister with 'atexit'. The handler is installed after
689+
// __sycl_register_lib has brought the runtime's own exit-time cleanup into
690+
// the atexit chain, so it is ordered ahead of that cleanup.
691+
Builder.CreateCall(AtExit, UnregFunc);
681692
Builder.CreateRetVoid();
682693

683694
return Func;
684695
}
685696

697+
private:
686698
Function *createUnregisterFunction(Constant *Start, Constant *Size) {
687699
FunctionType *FuncTy =
688700
FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
@@ -705,7 +717,6 @@ class SYCLWrapper {
705717
return Func;
706718
}
707719

708-
private:
709720
Module &M;
710721
LLVMContext &C;
711722
SYCLJITOptions Options;
@@ -753,20 +764,18 @@ Error offloading::wrapHIPBinary(Module &M, ArrayRef<char> Image,
753764
return Error::success();
754765
}
755766

756-
Error llvm::offloading::wrapSYCLBinaries(
757-
llvm::Module &M, ArrayRef<char> Buffer, SYCLJITOptions Options,
758-
bool IsFinalizedImage,
759-
std::pair<Function *, Function *> *RegistrationFuncs) {
767+
Error llvm::offloading::wrapSYCLBinaries(llvm::Module &M, ArrayRef<char> Buffer,
768+
SYCLJITOptions Options,
769+
bool IsFinalizedImage,
770+
Function **RegistrationFunc) {
760771
SYCLWrapper W(M, Options, IsFinalizedImage);
761772
auto [Start, Size] = W.embedBinary(Buffer);
762773
Function *RegisterFunc = W.createRegisterFatbinFunction(Start, Size);
763-
Function *UnregisterFunc = W.createUnregisterFunction(Start, Size);
764-
if (RegistrationFuncs) {
765-
*RegistrationFuncs = {RegisterFunc, UnregisterFunc};
774+
if (RegistrationFunc) {
775+
*RegistrationFunc = RegisterFunc;
766776
return Error::success();
767777
}
768778

769-
appendToGlobalCtors(M, RegisterFunc, /*Priority*/ 1);
770-
appendToGlobalDtors(M, UnregisterFunc, /*Priority*/ 1);
779+
appendToGlobalCtors(M, RegisterFunc, /*Priority=*/101);
771780
return Error::success();
772781
}

llvm/test/tools/llvm-offload-wrapper/offload-wrapper.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,12 @@
125125
; RUN: llvm-dis %t.bc -o - | FileCheck %s --check-prefix=SYCL
126126

127127
; SYCL: @.sycl_offloading.binary = internal unnamed_addr constant [[[SIZE:[0-9]+]] x i8] c"{{.*}}", section ".llvm.offloading"
128-
; SYCL-NEXT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @sycl.descriptor_reg, ptr null }]
129-
; SYCL-NEXT: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @sycl.descriptor_unreg, ptr null }]
128+
; SYCL-NEXT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @sycl.descriptor_reg, ptr null }]
130129

131130
; SYCL: define internal void @sycl.descriptor_reg() section ".text.startup" {
132131
; SYCL-NEXT: entry:
133132
; SYCL-NEXT: call void @__sycl_register_lib(ptr @.sycl_offloading.binary, i64 [[SIZE]])
133+
; SYCL-NEXT: %0 = call i32 @atexit(ptr @sycl.descriptor_unreg)
134134
; SYCL-NEXT: ret void
135135
; SYCL-NEXT: }
136136

0 commit comments

Comments
 (0)