Skip to content
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
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ LANGOPT(GPUAllowDeviceInit, 1, 0, NotCompatible, "allowing device side global in
LANGOPT(GPUMaxThreadsPerBlock, 32, 1024, NotCompatible, "default max threads per block for kernel launch bounds for HIP")
LANGOPT(GPUDeferDiag, 1, 0, NotCompatible, "defer host/device related diagnostic messages for CUDA/HIP")
LANGOPT(GPUExcludeWrongSideOverloads, 1, 0, NotCompatible, "always exclude wrong side overloads in overloading resolution for CUDA/HIP")
LANGOPT(OffloadingNewDriver, 1, 0, NotCompatible, "use the new driver for generating offloading code.")

@sarnex sarnex Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is not new, nor is the below Options.td change.
It was previously upstream code but was removed when upstream removed the old driver in llvm/llvm-project@feea42c

We just pulled down that change a couple of days ago, I am just adding it back here because I want to use it to restrict this fix.

LANGOPT(OffloadViaLLVM, 1, 0, NotCompatible, "target LLVM/Offload as portable offloading runtime.")
LANGOPT(CUDANVCCABI, 1, 0, NotCompatible, "emit the NVCC-compatible host registration ABI for CUDA")
LANGOPT(SPIRVDefaultAddrSpaceIsGeneric, 1, 0, NotCompatible, "use generic address space as default address space for SPIR-V targets")
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Options/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,7 @@ def no_offload_arch_EQ

def offload_new_driver : Flag<["--"], "offload-new-driver">,
Visibility<[ClangOption, CC1Option]>, Group<f_Group>,
MarshallingInfoFlag<LangOpts<"OffloadingNewDriver">>,
HelpText<"Deprecated; the new driver is always used for offloading compilation.">;
def no_offload_new_driver : Flag<["--"], "no-offload-new-driver">,
Visibility<[ClangOption, CC1Option]>, Group<f_Group>,
Expand Down
18 changes: 13 additions & 5 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1310,11 +1310,19 @@ void CodeGenModule::Release() {
if (llvm::Function *CudaCtorFunction = CUDARuntime->finalizeModule())
AddGlobalCtor(CudaCtorFunction);
}
if (LangOpts.SYCLIsHost && !CodeGenOpts.OffloadBinaryToEmbedFile.empty()) {
if (llvm::Function *SYCLCtorFunction = embedSYCLDeviceBinary())
// A static initializer may launch a kernel, so the device binary has to
// be registered before any of them run, hence a priority.
AddGlobalCtor(SYCLCtorFunction, /*Priority=*/101);
if (LangOpts.SYCLIsHost) {
if (!CodeGenOpts.OffloadBinaryToEmbedFile.empty()) {
if (llvm::Function *SYCLCtorFunction = embedSYCLDeviceBinary())
// A static initializer may launch a kernel, so the device binary has to
// be registered before any of them run, hence a priority.
AddGlobalCtor(SYCLCtorFunction, /*Priority=*/101);
assert(CodeGenOpts.OffloadObjects.empty() && "Unexpected offload object");
} else if (LangOpts.OffloadingNewDriver &&
!CodeGenOpts.OffloadObjects.empty()) {
Comment thread
YuriPlyakhin marked this conversation as resolved.
// Only emit the registerlib reference if there has been a device compile
// (so OffloadObjects is not empty).
AddGlobalCtor(createSYCLRegisterLibFunc());
Comment thread
tahonermann marked this conversation as resolved.
}
}
if (OpenMPRuntime) {
OpenMPRuntime->createOffloadEntriesAndInfoMetadata();
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -2240,6 +2240,17 @@ class CodeGenModule : public CodeGenTypeCache {
/// if the binary could not be read.
llvm::Function *embedSYCLDeviceBinary();

/// Emit a per-module __sycl_registerlib_<unique_identifier> reference that
/// ties the host and device sides of a translation unit together. This is
/// used to force linking of static libraries: we emit a global constructor
/// that references (calls) the symbol, creating an undefined reference that
/// forces the corresponding device static library member (which defines the
/// matching symbol) to be pulled in at link time. The device-side definition
/// is emitted by clang-linker-wrapper, so this is new offload model only. The
/// unique identifier is what ties the two sides together.
/// \return the global constructor that calls registerlib function.
llvm::Function *createSYCLRegisterLibFunc();

/// Determine whether the definition must be emitted; if this returns \c
/// false, the definition can be emitted lazily if it's used.
bool MustBeEmitted(const ValueDecl *D);
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/CodeGen/CodeGenSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,24 @@ llvm::Function *CodeGenModule::embedSYCLDeviceBinary() {
}
return RegistrationFunc;
}

llvm::Function *CodeGenModule::createSYCLRegisterLibFunc() {
assert(!LangOpts.SYCLUniquePrefix.empty() &&
"Expected non-empty SYCL unique prefix");
std::string RegisterFuncName =
"__sycl_registerlib_" + LangOpts.SYCLUniquePrefix;
Comment thread
tahonermann marked this conversation as resolved.
llvm::FunctionType *RegisterFTy =
llvm::FunctionType::get(VoidTy, /*isVarArg=*/false);
// Declare the symbol and emit a global constructor that calls it.
llvm::FunctionCallee RegisterFunc =
getModule().getOrInsertFunction(RegisterFuncName, RegisterFTy);
llvm::Function *SYCLRegisterLibCtor = CreateGlobalInitOrCleanUpFunction(
RegisterFTy, "__sycl_registerlib_ctor",
getTypes().arrangeNullaryFunction(), SourceLocation());
llvm::BasicBlock *Entry =
llvm::BasicBlock::Create(getLLVMContext(), "entry", SYCLRegisterLibCtor);
llvm::IRBuilder<> Builder(Entry);
Builder.CreateCall(RegisterFunc);
Builder.CreateRetVoid();
return SYCLRegisterLibCtor;
}
15 changes: 11 additions & 4 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8052,11 +8052,18 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.addOptOutFlag(CmdArgs, options::OPT_fopenmp_extensions,
options::OPT_fno_openmp_extensions);
}
// Forward '-foffload-via-llvm' to code generation to target the LLVM/Offload
// runtime.
// Forward the offload runtime change to code generation, liboffload implies
// new driver. Otherwise, check if we should forward the new driver to change
// offloading code generation.
if (Args.hasFlag(options::OPT_foffload_via_llvm,
options::OPT_fno_offload_via_llvm, false))
CmdArgs.push_back("-foffload-via-llvm");
options::OPT_fno_offload_via_llvm, false)) {
CmdArgs.append({"--offload-new-driver", "-foffload-via-llvm"});
} else if (Args.hasFlag(options::OPT_offload_new_driver,
options::OPT_no_offload_new_driver,
(C.getActiveOffloadKinds() != Action::OFK_None &&
C.getActiveOffloadKinds() != Action::OFK_SYCL))) {
CmdArgs.push_back("--offload-new-driver");
Comment thread
YuriPlyakhin marked this conversation as resolved.
}

const XRayArgs &XRay = TC.getXRayArgs(Args);
XRay.addArgs(TC, Args, CmdArgs, InputType);
Expand Down
6 changes: 6 additions & 0 deletions clang/test/CodeGenSYCL/registerlib-host-no-device-code.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Verify that we don't emit __sycl_registerlib_<unique identifier> or __sycl_registerlib_ctor if -fembed-offload-object is not passed, as
// there is no device code in that case.

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host --offload-new-driver -fsycl-unique-prefix=uid42 -emit-llvm %s -o - | FileCheck %s

// CHECK-NOT: __sycl_
28 changes: 28 additions & 0 deletions clang/test/CodeGenSYCL/registerlib-host.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Check the per-translation-unit __sycl_registerlib_<id> reference that SYCL
// host code generation emits under the new offload driver. The id is the
// translation unit's -fsycl-unique-prefix, which the driver generates.

// RUN: touch %t.o

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host --offload-new-driver -fsycl-unique-prefix=uid42 -emit-llvm -fembed-offload-object=%t.o %s -o - | FileCheck %s

// Nothing is emitted without the new offload driver.
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host -fsycl-unique-prefix=uid42 -emit-llvm -fembed-offload-object=%t.o %s -o - | FileCheck %s --check-prefix=OLD

// Nor when the module embeds its device binary (-fno-sycl-rdc), as it then
// registers the binary itself.
// RUN: echo -n 'FAKE_SYCL_DEVICE_IMAGE' > %t.bin
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host --offload-new-driver -foffload-include-binary %t.bin -fsycl-unique-prefix=uid42 -emit-llvm %s -o - | FileCheck %s --check-prefix=EMBED --implicit-check-not=__sycl_registerlib_

// CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @__sycl_registerlib_ctor, ptr null }]
// CHECK: declare void @__sycl_registerlib_uid42()
// CHECK: define internal void @__sycl_registerlib_ctor()
// CHECK-NEXT: entry:
// CHECK-NEXT: call void @__sycl_registerlib_uid42()
// CHECK-NEXT: ret void

// OLD-NOT: __sycl_registerlib_

// EMBED: @.sycl_offloading.binary

void foo() {}
6 changes: 3 additions & 3 deletions clang/test/Driver/sycl-offload-new-driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@

/// Check the toolflow for SYCL compilation using new offload model
// RUN: %clangxx -### --target=x86_64-unknown-linux-gnu -fsycl -fsycl-targets=spir64 --offload-new-driver -ccc-install-dir %{clang_install_dir} %s 2>&1 | FileCheck -check-prefix=CHK-FLOW %s
// CHK-FLOW: clang{{.*}} "-cc1" "-triple" "spir64-unknown-unknown" "-aux-triple" "x86_64-unknown-linux-gnu" "-fsycl-is-device" {{.*}} "-fsycl-int-header=[[HEADER:.*]].h" "-fsycl-int-footer=[[FOOTER:.*]].h" {{.*}} "-o" "[[CC1DEVOUT:.*]]" "-x" "c++" "[[INPUT:.*]]"
// CHK-FLOW: clang{{.*}} "-cc1" "-triple" "spir64-unknown-unknown" "-aux-triple" "x86_64-unknown-linux-gnu" "-fsycl-is-device" {{.*}} "-fsycl-int-header=[[HEADER:.*]].h" "-fsycl-int-footer=[[FOOTER:.*]].h" {{.*}} "--offload-new-driver" {{.*}} "-o" "[[CC1DEVOUT:.*]]" "-x" "c++" "[[INPUT:.*]]"
// CHK-FLOW-NEXT: llvm-offload-binary{{.*}} "-o" "[[PACKOUT:.*]]" "--image=file=[[CC1DEVOUT]],triple=spir64-unknown-unknown,arch=generic,kind=sycl{{.*}}"
// CHK-FLOW-NEXT: clang{{.*}} "-cc1" "-triple" "x86_64-unknown-linux-gnu"{{.*}} "-fsycl-is-host"{{.*}} "-include-internal-header" "[[HEADER]].h" "-dependency-filter" "[[HEADER]].h" {{.*}} "-include-internal-footer" "[[FOOTER]].h" "-dependency-filter" "[[FOOTER]].h"{{.*}} "-fembed-offload-object=[[PACKOUT]]" {{.*}} "-o" "[[CC1FINALOUT:.*]]" "-x" "c++" "[[INPUT]]"
// CHK-FLOW-NEXT: clang{{.*}} "-cc1" "-triple" "x86_64-unknown-linux-gnu"{{.*}} "-fsycl-is-host"{{.*}} "-include-internal-header" "[[HEADER]].h" "-dependency-filter" "[[HEADER]].h" {{.*}} "-include-internal-footer" "[[FOOTER]].h" "-dependency-filter" "[[FOOTER]].h"{{.*}} "--offload-new-driver" {{.*}} "-fembed-offload-object=[[PACKOUT]]" {{.*}} "-o" "[[CC1FINALOUT:.*]]" "-x" "c++" "[[INPUT]]"
// CHK-FLOW-NEXT: clang-linker-wrapper{{.*}} "--host-triple=x86_64-unknown-linux-gnu"{{.*}} "--linker-path={{.*}}/ld" {{.*}} "[[CC1FINALOUT]]"

/// Verify phases used to generate SPIR-V instead of LLVM-IR
Expand Down Expand Up @@ -96,7 +96,7 @@
// RUN: | FileCheck -check-prefix=CHK_ARCH \
// RUN: -DTRIPLE=nvptx64-nvidia-cuda -DARCH=sm_75 %s
// CHK_ARCH: clang{{.*}} "-triple" "[[TRIPLE]]"
// CHK_ARCH-SAME: "-fsycl-is-device" {{.*}} "-o" "[[CC1DEVOUT:.+\.bc]]"
// CHK_ARCH-SAME: "-fsycl-is-device" {{.*}} "--offload-new-driver"{{.*}} "-o" "[[CC1DEVOUT:.+\.bc]]"
// CHK_ARCH-NEXT: llvm-offload-binary{{.*}} "--image=file=[[CC1DEVOUT]],triple=[[TRIPLE]],arch=[[ARCH]]{{.*}},kind=sycl{{.*}}"

// Verify offload-packager option values
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// REQUIRES: system-linux, x86-registered-target, spirv-to-ir-wrapper, sycl-post-link

// Check that the SYCL wrapper object for a target defines the
// __sycl_registerlib_<id> symbols referenced by the host objects whose device
// code is for that target, including archive members found through -l.

// RUN: %clang_cc1 -fsycl-is-device -disable-llvm-passes -triple=spir64-unknown-unknown %s -emit-llvm-bc -o %t.bc
// RUN: llvm-offload-binary -o %t.fat --image=file=%t.bc,kind=sycl,triple=spir64-unknown-unknown
// RUN: llvm-offload-binary -o %t.gen.fat --image=file=%t.bc,kind=sycl,triple=spir64_gen-unknown-unknown,arch=pvc
// RUN: %clang_cc1 -DMAIN %s -triple=x86_64-unknown-linux-gnu -fsycl-is-host --offload-new-driver -fsycl-unique-prefix=main -emit-obj -fembed-offload-object=%t.fat -o %t.main.o
// RUN: %clang_cc1 -DFN=lib_fn %s -triple=x86_64-unknown-linux-gnu -fsycl-is-host --offload-new-driver -fsycl-unique-prefix=lib -emit-obj -fembed-offload-object=%t.fat -o %t.lib.o
// RUN: %clang_cc1 -DFN=gen_fn %s -triple=x86_64-unknown-linux-gnu -fsycl-is-host --offload-new-driver -fsycl-unique-prefix=gen -emit-obj -fembed-offload-object=%t.gen.fat -o %t.gen.o
// RUN: rm -rf %t.dir && mkdir -p %t.dir
// RUN: llvm-ar rc %t.dir/libsyclreg.a %t.lib.o %t.gen.o
// RUN: touch %t.devicelib.bc
// RUN: clang-linker-wrapper --print-wrapped-module --host-triple=x86_64-unknown-linux-gnu \
// RUN: --bitcode-library=spir64-unknown-unknown=%t.devicelib.bc \
// RUN: -sycl-post-link-options=-split=auto -sycl-post-link-options=-symbols \
// RUN: -sycl-post-link-options=-properties --linker-path=/usr/bin/ld \
// RUN: %t.main.o -L%t.dir -lsyclreg -o %t.out 2>&1 \
// RUN: | FileCheck %s --implicit-check-not=__sycl_registerlib_gen

// No spir64_gen device code is linked, so nothing defines that reference.
// CHECK: define weak void @__sycl_registerlib_main()
// CHECK: define weak void @__sycl_registerlib_lib()

template <typename t, typename Func>
__attribute__((sycl_kernel)) void kernel(const Func &func) {
func();
}

#ifdef MAIN
extern "C" {
// Define stub versions of the SYCL runtime image registration functions that are called by
// the wrapped host code so we don't need to link against the real SYCL runtime.
void __sycl_register_lib(void *) {}
void __sycl_unregister_lib(void *) {}
Comment thread
tahonermann marked this conversation as resolved.
}

void lib_fn();
int main() {
kernel<class main_kernel>([]() {});
lib_fn();
}
#else
void FN() { kernel<class lib_kernel>([]() {}); }
#endif
Loading
Loading