[clang] 3e93e93 - [CIR][SYCL] Embed and register offloaded device binary on host path (#224692)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 18 15:59:31 PDT 2026
Author: Konstantinos Parasyris
Date: 2026-09-18T15:59:25-07:00
New Revision: 3e93e9368f9167e5129f369194db08edcdf167d6
URL: https://github.com/llvm/llvm-project/commit/3e93e9368f9167e5129f369194db08edcdf167d6
DIFF: https://github.com/llvm/llvm-project/commit/3e93e9368f9167e5129f369194db08edcdf167d6.diff
LOG: [CIR][SYCL] Embed and register offloaded device binary on host path (#224692)
Added:
clang/test/CIR/CodeGenSYCL/offload-include-binary.cpp
Modified:
clang/lib/CIR/FrontendAction/CIRGenAction.cpp
clang/lib/CIR/FrontendAction/CMakeLists.txt
Removed:
################################################################################
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 11b78b717ba68..240601f9834e5 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -12,6 +12,7 @@
#include "mlir/IR/OwningOpRef.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/DiagnosticCodeGen.h"
+#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/CIR/CIRGenerator.h"
#include "clang/CIR/CIRToCIRPasses.h"
#include "clang/CIR/LowerToLLVM.h"
@@ -21,12 +22,14 @@
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSet.h"
+#include "llvm/Frontend/Offloading/OffloadWrapper.h"
#include "llvm/IR/DiagnosticHandler.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Linker/Linker.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/Internalize.h"
@@ -208,6 +211,10 @@ class CIRGenConsumer : public clang::ASTConsumer {
if (linkInModules(*LLVMModule))
return;
+ // Embed the offloaded SYCL device binary into the host module.
+ if (C.getLangOpts().SYCLIsHost && !CGO.OffloadBinaryToEmbedFile.empty())
+ embedSYCLDeviceBinary(*LLVMModule);
+
BackendAction BEAction = getBackendActionFromOutputType(Action);
emitBackendOutput(CI, CI.getCodeGenOpts(), LLVMModule.get(), BEAction, FS,
std::move(OutputStream));
@@ -251,6 +258,28 @@ class CIRGenConsumer : public clang::ASTConsumer {
return false;
}
+ // Reads the device binary named by -foffload-include-binary and embeds it
+ // into the host module. wrapSYCLBinaries also appends the registration ctor
+ // at priority 101 when no registration-function out-param is supplied.
+ void embedSYCLDeviceBinary(llvm::Module &M) {
+ StringRef fileName = CGO.OffloadBinaryToEmbedFile;
+ auto bufferOrErr = CI.getVirtualFileSystem().getBufferForFile(fileName);
+ if (std::error_code ec = bufferOrErr.getError()) {
+ CI.getDiagnostics().Report(diag::err_cannot_open_file)
+ << fileName << ec.message();
+ return;
+ }
+ std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(bufferOrErr.get());
+ if (llvm::Error err = llvm::offloading::wrapSYCLBinaries(
+ M,
+ ArrayRef<char>(buffer->getBufferStart(), buffer->getBufferSize()),
+ llvm::offloading::SYCLJITOptions(), /*IsFinalizedImage=*/true)) {
+ CI.getDiagnostics().Report(diag::err_fe_error_backend)
+ << llvm::toString(std::move(err));
+ return;
+ }
+ }
+
void HandleTagDeclDefinition(TagDecl *D) override {
PrettyStackTraceDecl CrashInfo(D, SourceLocation(),
Context->getSourceManager(),
diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt
index d221771fd4086..b6b1e8b2bbbe2 100644
--- a/clang/lib/CIR/FrontendAction/CMakeLists.txt
+++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt
@@ -1,5 +1,6 @@
set(LLVM_LINK_COMPONENTS
Core
+ FrontendOffloading
ipo
Linker
Support
diff --git a/clang/test/CIR/CodeGenSYCL/offload-include-binary.cpp b/clang/test/CIR/CodeGenSYCL/offload-include-binary.cpp
new file mode 100644
index 0000000000000..dca4b38f3f4c3
--- /dev/null
+++ b/clang/test/CIR/CodeGenSYCL/offload-include-binary.cpp
@@ -0,0 +1,57 @@
+// REQUIRES: x86-registered-target
+
+// Verify that on the ClangIR path -foffload-include-binary embeds the finalized
+// SYCL device binary into the host module and emits the constructor that
+// registers it with the SYCL runtime, matching classic CodeGen. Unregistration
+// is done from 'atexit', so no global destructor is emitted for it.
+// The binary is already finalized, so it must not land in ".llvm.offloading".
+// RUN: echo -n 'FAKE_SYCL_DEVICE_IMAGE' > %t.bin
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host -fclangir \
+// RUN: -foffload-include-binary %t.bin -emit-llvm %s -o - \
+// RUN: | FileCheck %s --implicit-check-not='.llvm.offloading' \
+// RUN: --implicit-check-not='llvm.global_dtors'
+
+// Object emission must succeed for a translation unit that has its own static
+// initializers, so the registration ctor merges into the existing ctor list.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host -fclangir \
+// RUN: -foffload-include-binary %t.bin -emit-obj %s -o %t.o
+
+// Without the flag no SYCL registration IR should be emitted.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host -fclangir \
+// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=NONE \
+// RUN: --implicit-check-not='.sycl_offloading.binary' \
+// RUN: --implicit-check-not='__sycl_register_lib' \
+// RUN: --implicit-check-not='llvm.global_dtors'
+
+// A missing binary file must be diagnosed.
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fsycl-is-host -fclangir \
+// RUN: -foffload-include-binary %t.does-not-exist -emit-llvm %s -o - 2>&1 \
+// RUN: | FileCheck %s --check-prefix=ERROR
+
+struct S {
+ S();
+ ~S();
+};
+S s;
+
+void f() {}
+
+// CHECK: @.sycl_offloading.binary = internal unnamed_addr constant [22 x i8] c"FAKE_SYCL_DEVICE_IMAGE", section ".sycl_fatbin"
+// CHECK: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }]
+// CHECK-SAME: i32 65535, ptr @_GLOBAL__sub_I_
+// CHECK-SAME: i32 101, ptr @sycl.descriptor_reg
+// CHECK: define internal void @sycl.descriptor_reg()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @__sycl_register_lib(ptr @.sycl_offloading.binary, i64 22)
+// CHECK-NEXT: {{.*}}call i32 @atexit(ptr @sycl.descriptor_unreg)
+// CHECK-NEXT: ret void
+// CHECK: define internal void @sycl.descriptor_unreg()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @__sycl_unregister_lib(ptr @.sycl_offloading.binary, i64 22)
+// CHECK-NEXT: ret void
+
+// NONE: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }]
+// NONE-SAME: i32 65535, ptr @_GLOBAL__sub_I_
+// NONE: define dso_local void @_Z1fv()
+
+// ERROR: cannot open file '{{.*}}.does-not-exist'
More information about the cfe-commits
mailing list