[llvm] 65d730b - [SYCL] Add offload wrapping for SYCL kind (#147508)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 26 10:02:37 PDT 2025
Author: Maksim Sabianin
Date: 2025-09-26T17:02:31Z
New Revision: 65d730b4a5aaea91493305dd32e27bc8e033a0ac
URL: https://github.com/llvm/llvm-project/commit/65d730b4a5aaea91493305dd32e27bc8e033a0ac
DIFF: https://github.com/llvm/llvm-project/commit/65d730b4a5aaea91493305dd32e27bc8e033a0ac.diff
LOG: [SYCL] Add offload wrapping for SYCL kind (#147508)
This patch adds an Offload Wrapper for the SYCL kind. This is an
essential step for SYCL offloading and the compilation flow. The usage
of offload wrapping is added to the clang-linker-wrapper tool.
Modifications:
Implemented `bundleSYCL()` function to handle SYCL image bundling.
Implemented `wrapSYCLBinaries()` function that is invoked from
clang-linker-wrapper.
SYCL Offload Wrapping uses specific data structures such as
`__sycl.tgt_device_image` and `__sycl.tgt_bin_desc`. Each SYCL image
maintains its own symbol table (unlike shared global tables in other
targets). Therefore, symbols are encoded explicitly during the offload
wrapping. Also, images refer to their own Offloading Entries arrays
unlike other targets.
The proposed `__sycl.tgt_device_image` uses Version 3 to differentiate
from images generated by Intel DPC++. The structure proposed in this
patch doesn't have fields deprecated in DPC++.
Added:
Modified:
clang/test/Driver/linker-wrapper-image.c
clang/test/Driver/linker-wrapper.c
clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h
llvm/include/llvm/Frontend/Offloading/Utility.h
llvm/include/llvm/Object/OffloadBinary.h
llvm/lib/Frontend/Offloading/OffloadWrapper.cpp
llvm/lib/Frontend/Offloading/Utility.cpp
llvm/tools/llvm-offload-wrapper/llvm-offload-wrapper.cpp
Removed:
################################################################################
diff --git a/clang/test/Driver/linker-wrapper-image.c b/clang/test/Driver/linker-wrapper-image.c
index c0de56d58196a..31476173cd370 100644
--- a/clang/test/Driver/linker-wrapper-image.c
+++ b/clang/test/Driver/linker-wrapper-image.c
@@ -1,6 +1,7 @@
// REQUIRES: x86-registered-target
// REQUIRES: nvptx-registered-target
// REQUIRES: amdgpu-registered-target
+// REQUIRES: spirv-registered-target
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.elf.o
@@ -263,3 +264,36 @@
// HIP: while.end:
// HIP-NEXT: ret void
// HIP-NEXT: }
+
+// RUN: clang-offload-packager -o %t.out --image=file=%t.elf.o,kind=sycl,triple=spirv64-unknown-unknown,arch=generic
+// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
+// RUN: -fembed-offload-object=%t.out
+// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
+// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=SYCL
+// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu -r \
+// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=SYCL
+
+// SYCL: %__sycl.tgt_device_image = type { i16, i8, i8, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }
+// SYCL-NEXT: %__sycl.tgt_bin_desc = type { i16, i16, ptr, ptr, ptr }
+
+// SYCL: @.sycl_offloading.target.0 = internal unnamed_addr constant [1 x i8] zeroinitializer
+// SYCL-NEXT: @.sycl_offloading.opts.compile.0 = internal unnamed_addr constant [1 x i8] zeroinitializer
+// SYCL-NEXT: @.sycl_offloading.opts.link.0 = internal unnamed_addr constant [1 x i8] zeroinitializer
+// SYCL-NEXT: @.sycl_offloading.0.data = internal unnamed_addr constant [0 x i8] zeroinitializer, section ".llvm.offloading"
+// SYCL-NEXT: @.offloading.entry_name = internal unnamed_addr constant [5 x i8] c"stub\00", section ".llvm.rodata.offloading", align 1
+// SYCL-NEXT: @.offloading.entry.stub = weak constant %struct.__tgt_offload_entry { i64 0, i16 1, i16 8, i32 0, ptr null, ptr @.offloading.entry_name, i64 0, i64 0, ptr null }, section "llvm_offload_entries", align 8
+// SYCL-NEXT: @.sycl_offloading.entries_arr = internal constant [1 x %struct.__tgt_offload_entry] [%struct.__tgt_offload_entry { i64 0, i16 1, i16 8, i32 0, ptr null, ptr @.offloading.entry_name, i64 0, i64 0, ptr null }]
+// SYCL-NEXT: @.sycl_offloading.device_images = internal unnamed_addr constant [1 x %__sycl.tgt_device_image] [%__sycl.tgt_device_image { i16 3, i8 8, i8 0, ptr @.sycl_offloading.target.0, ptr @.sycl_offloading.opts.compile.0, ptr @.sycl_offloading.opts.link.0, ptr @.sycl_offloading.0.data, ptr @.sycl_offloading.0.data, ptr @.sycl_offloading.entries_arr, ptr getelementptr ([1 x %struct.__tgt_offload_entry], ptr @.sycl_offloading.entries_arr, i64 0, i64 1), ptr null, ptr null }]
+// SYCL-NEXT: @.sycl_offloading.descriptor = internal constant %__sycl.tgt_bin_desc { i16 1, i16 1, ptr @.sycl_offloading.device_images, ptr null, ptr null }
+
+// SYCL: define internal void @sycl.descriptor_reg() section ".text.startup" {
+// SYCL-NEXT: entry:
+// SYCL-NEXT: call void @__sycl_register_lib(ptr @.sycl_offloading.descriptor)
+// SYCL-NEXT: ret void
+// SYCL-NEXT: }
+
+// SYCL: define internal void @sycl.descriptor_unreg() section ".text.startup" {
+// SYCL-NEXT: entry:
+// SYCL-NEXT: call void @__sycl_unregister_lib(ptr @.sycl_offloading.descriptor)
+// SYCL-NEXT: ret void
+// SYCL-NEXT: }
diff --git a/clang/test/Driver/linker-wrapper.c b/clang/test/Driver/linker-wrapper.c
index e73fa5ca3dbf9..c060dae7bb154 100644
--- a/clang/test/Driver/linker-wrapper.c
+++ b/clang/test/Driver/linker-wrapper.c
@@ -54,7 +54,7 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: clang-offload-packager -o %t.out \
// RUN: --image=file=%t.spirv.bc,kind=sycl,triple=spirv64-unknown-unknown,arch=generic
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
-// RUN: not clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
+// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=SPIRV-LINK
// SPIRV-LINK: clang{{.*}} -o {{.*}}.img -dumpdir a.out.spirv64..img. --target=spirv64-unknown-unknown {{.*}}.o --sycl-link -Xlinker -triple=spirv64-unknown-unknown -Xlinker -arch=
diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index a56e758fb75d8..be658acacb406 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -717,6 +717,14 @@ wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
M, BuffersToWrap.front(), offloading::getOffloadEntryArray(M)))
return std::move(Err);
break;
+ case OFK_SYCL: {
+ // TODO: fill these options once the Driver supports them.
+ offloading::SYCLJITOptions Options;
+ if (Error Err =
+ offloading::wrapSYCLBinaries(M, BuffersToWrap.front(), Options))
+ return std::move(Err);
+ break;
+ }
default:
return createStringError(getOffloadKindName(Kind) +
" wrapping is not supported");
@@ -754,6 +762,32 @@ bundleOpenMP(ArrayRef<OffloadingImage> Images) {
return std::move(Buffers);
}
+Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
+bundleSYCL(ArrayRef<OffloadingImage> Images) {
+ SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
+ if (DryRun) {
+ // In dry-run mode there is an empty input which is insufficient for the
+ // testing. Therefore, we return here a stub image.
+ OffloadingImage Image;
+ Image.TheImageKind = IMG_None;
+ Image.TheOffloadKind = OffloadKind::OFK_SYCL;
+ Image.StringData["symbols"] = "stub";
+ Image.Image = MemoryBuffer::getMemBufferCopy("");
+ SmallString<0> SerializedImage = OffloadBinary::write(Image);
+ Buffers.emplace_back(MemoryBuffer::getMemBufferCopy(SerializedImage));
+ return std::move(Buffers);
+ }
+
+ for (const OffloadingImage &Image : Images) {
+ // clang-sycl-linker packs outputs into one binary blob. Therefore, it is
+ // passed to Offload Wrapper as is.
+ StringRef S(Image.Image->getBufferStart(), Image.Image->getBufferSize());
+ Buffers.emplace_back(MemoryBuffer::getMemBufferCopy(S));
+ }
+
+ return std::move(Buffers);
+}
+
Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
bundleCuda(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles;
@@ -806,8 +840,9 @@ bundleLinkedOutput(ArrayRef<OffloadingImage> Images, const ArgList &Args,
llvm::TimeTraceScope TimeScope("Bundle linked output");
switch (Kind) {
case OFK_OpenMP:
- case OFK_SYCL:
return bundleOpenMP(Images);
+ case OFK_SYCL:
+ return bundleSYCL(Images);
case OFK_Cuda:
return bundleCuda(Images, Args);
case OFK_HIP:
diff --git a/llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h b/llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h
index 6b9da06707261..24017492e30b2 100644
--- a/llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h
+++ b/llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h
@@ -13,6 +13,8 @@
#include "llvm/IR/Module.h"
#include "llvm/Support/Compiler.h"
+#include <string>
+
namespace llvm {
namespace offloading {
using EntryArrayTy = std::pair<GlobalVariable *, GlobalVariable *>;
@@ -52,6 +54,24 @@ LLVM_ABI llvm::Error wrapHIPBinary(llvm::Module &M, llvm::ArrayRef<char> Images,
EntryArrayTy EntryArray,
llvm::StringRef Suffix = "",
bool EmitSurfacesAndTextures = true);
+
+struct SYCLJITOptions {
+ // Target/compiler specific options that are suggested to use to "compile"
+ // program at runtime.
+ std::string CompileOptions;
+ // Target/compiler specific options that are suggested to use to "link"
+ // program at runtime.
+ std::string LinkOptions;
+};
+
+/// Wraps OffloadBinaries in the given \p Buffers into the module \p M
+/// as global symbols and registers the images with the SYCL Runtime.
+/// \param Options Compiler and linker options to be encoded for the later
+/// use by a runtime for JIT compilation.
+LLVM_ABI llvm::Error
+wrapSYCLBinaries(llvm::Module &M, llvm::ArrayRef<char> Buffer,
+ SYCLJITOptions Options = SYCLJITOptions());
+
} // namespace offloading
} // namespace llvm
diff --git a/llvm/include/llvm/Frontend/Offloading/Utility.h b/llvm/include/llvm/Frontend/Offloading/Utility.h
index f8a2b1237b5e1..23e6702beb476 100644
--- a/llvm/include/llvm/Frontend/Offloading/Utility.h
+++ b/llvm/include/llvm/Frontend/Offloading/Utility.h
@@ -82,7 +82,8 @@ LLVM_ABI StructType *getEntryTy(Module &M);
/// \param Data Extra data storage associated with the entry.
/// \param SectionName The section this entry will be placed at.
/// \param AuxAddr An extra pointer if needed.
-LLVM_ABI void
+/// \return The emitted global variable containing the offloading entry.
+LLVM_ABI GlobalVariable *
emitOffloadingEntry(Module &M, object::OffloadKind Kind, Constant *Addr,
StringRef Name, uint64_t Size, uint32_t Flags,
uint64_t Data, Constant *AuxAddr = nullptr,
diff --git a/llvm/include/llvm/Object/OffloadBinary.h b/llvm/include/llvm/Object/OffloadBinary.h
index b5c845fa8eb70..ac2dbf60e2aec 100644
--- a/llvm/include/llvm/Object/OffloadBinary.h
+++ b/llvm/include/llvm/Object/OffloadBinary.h
@@ -48,6 +48,7 @@ enum ImageKind : uint16_t {
IMG_Cubin,
IMG_Fatbinary,
IMG_PTX,
+ IMG_SPIRV,
IMG_LAST,
};
@@ -70,9 +71,9 @@ class OffloadBinary : public Binary {
/// The offloading metadata that will be serialized to a memory buffer.
struct OffloadingImage {
- ImageKind TheImageKind;
- OffloadKind TheOffloadKind;
- uint32_t Flags;
+ ImageKind TheImageKind = ImageKind::IMG_None;
+ OffloadKind TheOffloadKind = OffloadKind::OFK_None;
+ uint32_t Flags = 0;
MapVector<StringRef, StringRef> StringData;
std::unique_ptr<MemoryBuffer> Image;
};
diff --git a/llvm/lib/Frontend/Offloading/OffloadWrapper.cpp b/llvm/lib/Frontend/Offloading/OffloadWrapper.cpp
index cfddc06fbc00b..c4aa2c7638450 100644
--- a/llvm/lib/Frontend/Offloading/OffloadWrapper.cpp
+++ b/llvm/lib/Frontend/Offloading/OffloadWrapper.cpp
@@ -8,19 +8,32 @@
#include "llvm/Frontend/Offloading/OffloadWrapper.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Frontend/Offloading/Utility.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
#include "llvm/Object/OffloadBinary.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
+#include <memory>
+#include <string>
+#include <utility>
+
using namespace llvm;
+using namespace llvm::object;
using namespace llvm::offloading;
namespace {
@@ -620,6 +633,384 @@ void createRegisterFatbinFunction(Module &M, GlobalVariable *FatbinDesc,
// Add this function to constructors.
appendToGlobalCtors(M, CtorFunc, /*Priority=*/101);
}
+
+/// SYCLWrapper helper class that creates all LLVM IRs wrapping given images.
+struct SYCLWrapper {
+ Module &M;
+ LLVMContext &C;
+ SYCLJITOptions Options;
+
+ StructType *EntryTy = nullptr;
+ StructType *SyclDeviceImageTy = nullptr;
+ StructType *SyclBinDescTy = nullptr;
+
+ SYCLWrapper(Module &M, const SYCLJITOptions &Options)
+ : M(M), C(M.getContext()), Options(Options) {
+ EntryTy = offloading::getEntryTy(M);
+ SyclDeviceImageTy = getSyclDeviceImageTy();
+ SyclBinDescTy = getSyclBinDescTy();
+ }
+
+ IntegerType *getSizeTTy() {
+ switch (M.getDataLayout().getPointerSize()) {
+ case 4:
+ return Type::getInt32Ty(C);
+ case 8:
+ return Type::getInt64Ty(C);
+ }
+ llvm_unreachable("unsupported pointer type size");
+ }
+
+ SmallVector<Constant *, 2> getSizetConstPair(size_t First, size_t Second) {
+ IntegerType *SizeTTy = getSizeTTy();
+ return SmallVector<Constant *, 2>{ConstantInt::get(SizeTTy, First),
+ ConstantInt::get(SizeTTy, Second)};
+ }
+
+ /// Note: Properties aren't supported and the support is going
+ /// to be added later.
+ /// Creates a structure corresponding to:
+ /// SYCL specific image descriptor type.
+ /// \code
+ /// struct __sycl.tgt_device_image {
+ /// // version of this structure - for backward compatibility;
+ /// // all modifications which change order/type/offsets of existing fields
+ /// // should increment the version.
+ /// uint16_t Version;
+ /// // the kind of offload model the image employs.
+ /// uint8_t OffloadKind;
+ /// // format of the image data - SPIRV, LLVMIR bitcode, etc
+ /// uint8_t Format;
+ /// // null-terminated string representation of the device's target
+ /// // architecture
+ /// const char *Arch;
+ /// // a null-terminated string; target- and compiler-specific options
+ /// // which are suggested to use to "compile" program at runtime
+ /// const char *CompileOptions;
+ /// // a null-terminated string; target- and compiler-specific options
+ /// // which are suggested to use to "link" program at runtime
+ /// const char *LinkOptions;
+ /// // Pointer to the device binary image start
+ /// void *ImageStart;
+ /// // Pointer to the device binary image end
+ /// void *ImageEnd;
+ /// // the entry table
+ /// __tgt_offload_entry *EntriesBegin;
+ /// __tgt_offload_entry *EntriesEnd;
+ /// const char *PropertiesBegin;
+ /// const char *PropertiesEnd;
+ /// };
+ /// \endcode
+ StructType *getSyclDeviceImageTy() {
+ return StructType::create(
+ {
+ Type::getInt16Ty(C), // Version
+ Type::getInt8Ty(C), // OffloadKind
+ Type::getInt8Ty(C), // Format
+ PointerType::getUnqual(C), // Arch
+ PointerType::getUnqual(C), // CompileOptions
+ PointerType::getUnqual(C), // LinkOptions
+ PointerType::getUnqual(C), // ImageStart
+ PointerType::getUnqual(C), // ImageEnd
+ PointerType::getUnqual(C), // EntriesBegin
+ PointerType::getUnqual(C), // EntriesEnd
+ PointerType::getUnqual(C), // PropertiesBegin
+ PointerType::getUnqual(C) // PropertiesEnd
+ },
+ "__sycl.tgt_device_image");
+ }
+
+ /// Creates a structure for SYCL specific binary descriptor type. Corresponds
+ /// to:
+ ///
+ /// \code
+ /// struct __sycl.tgt_bin_desc {
+ /// // version of this structure - for backward compatibility;
+ /// // all modifications which change order/type/offsets of existing fields
+ /// // should increment the version.
+ /// uint16_t Version;
+ /// uint16_t NumDeviceImages;
+ /// __sycl.tgt_device_image *DeviceImages;
+ /// // the offload entry table
+ /// __tgt_offload_entry *HostEntriesBegin;
+ /// __tgt_offload_entry *HostEntriesEnd;
+ /// };
+ /// \endcode
+ StructType *getSyclBinDescTy() {
+ return StructType::create(
+ {Type::getInt16Ty(C), Type::getInt16Ty(C), PointerType::getUnqual(C),
+ PointerType::getUnqual(C), PointerType::getUnqual(C)},
+ "__sycl.tgt_bin_desc");
+ }
+
+ /// Adds a global readonly variable that is initialized by given
+ /// \p Initializer to the module.
+ GlobalVariable *addGlobalArrayVariable(const Twine &Name,
+ ArrayRef<char> Initializer,
+ const Twine &Section = "") {
+ auto *Arr = ConstantDataArray::get(M.getContext(), Initializer);
+ auto *Var = new GlobalVariable(M, Arr->getType(), /*isConstant*/ true,
+ GlobalVariable::InternalLinkage, Arr, Name);
+ Var->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
+
+ SmallVector<char, 32> NameBuf;
+ auto SectionName = Section.toStringRef(NameBuf);
+ if (!SectionName.empty())
+ Var->setSection(SectionName);
+ return Var;
+ }
+
+ /// Adds given \p Buf as a global variable into the module.
+ /// \returns Pair of pointers that point at the beginning and the end of the
+ /// variable.
+ std::pair<Constant *, Constant *>
+ addArrayToModule(ArrayRef<char> Buf, const Twine &Name,
+ const Twine &Section = "") {
+ auto *Var = addGlobalArrayVariable(Name, Buf, Section);
+ auto *ImageB = ConstantExpr::getGetElementPtr(Var->getValueType(), Var,
+ getSizetConstPair(0, 0));
+ auto *ImageE = ConstantExpr::getGetElementPtr(
+ Var->getValueType(), Var, getSizetConstPair(0, Buf.size()));
+ return std::make_pair(ImageB, ImageE);
+ }
+
+ /// Adds given \p Data as constant byte array in the module.
+ /// \returns Constant pointer to the added data. The pointer type does not
+ /// carry size information.
+ Constant *addRawDataToModule(ArrayRef<char> Data, const Twine &Name) {
+ auto *Var = addGlobalArrayVariable(Name, Data);
+ auto *DataPtr = ConstantExpr::getGetElementPtr(Var->getValueType(), Var,
+ getSizetConstPair(0, 0));
+ return DataPtr;
+ }
+
+ /// Creates a global variable of const char* type and creates an
+ /// initializer that initializes it with \p Str.
+ ///
+ /// \returns Link-time constant pointer (constant expr) to that
+ /// variable.
+ Constant *addStringToModule(StringRef Str, const Twine &Name) {
+ auto *Arr = ConstantDataArray::getString(C, Str);
+ auto *Var = new GlobalVariable(M, Arr->getType(), /*isConstant*/ true,
+ GlobalVariable::InternalLinkage, Arr, Name);
+ Var->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
+ auto *Zero = ConstantInt::get(getSizeTTy(), 0);
+ Constant *ZeroZero[] = {Zero, Zero};
+ return ConstantExpr::getGetElementPtr(Var->getValueType(), Var, ZeroZero);
+ }
+
+ /// Each image contains its own set of symbols, which may contain
diff erent
+ /// symbols than other images. This function constructs an array of
+ /// symbol entries for a particular image.
+ ///
+ /// \returns Pointers to the beginning and end of the array.
+ std::pair<Constant *, Constant *>
+ initOffloadEntriesPerImage(StringRef Entries, const Twine &OffloadKindTag) {
+ SmallVector<Constant *> EntriesInits;
+ std::unique_ptr<MemoryBuffer> MB = MemoryBuffer::getMemBuffer(
+ Entries, /*BufferName*/ "", /*RequiresNullTerminator*/ false);
+ for (line_iterator LI(*MB); !LI.is_at_eof(); ++LI) {
+ GlobalVariable *GV =
+ emitOffloadingEntry(M, /*Kind*/ OffloadKind::OFK_SYCL,
+ Constant::getNullValue(PointerType::getUnqual(C)),
+ /*Name*/ *LI, /*Size*/ 0,
+ /*Flags*/ 0, /*Data*/ 0);
+ EntriesInits.push_back(GV->getInitializer());
+ }
+
+ auto *Arr = ConstantArray::get(ArrayType::get(EntryTy, EntriesInits.size()),
+ EntriesInits);
+ auto *EntriesGV = new GlobalVariable(M, Arr->getType(), /*isConstant*/ true,
+ GlobalVariable::InternalLinkage, Arr,
+ OffloadKindTag + "entries_arr");
+
+ auto *EntriesB = ConstantExpr::getGetElementPtr(
+ EntriesGV->getValueType(), EntriesGV, getSizetConstPair(0, 0));
+ auto *EntriesE = ConstantExpr::getGetElementPtr(
+ EntriesGV->getValueType(), EntriesGV,
+ getSizetConstPair(0, EntriesInits.size()));
+ return std::make_pair(EntriesB, EntriesE);
+ }
+
+ Constant *wrapImage(const OffloadBinary &OB, const Twine &ImageID,
+ StringRef OffloadKindTag) {
+ // Note: Intel DPC++ compiler had 2 versions of this structure
+ // and clang++ has a third
diff erent structure. To avoid ABI incompatibility
+ // between generated device images the Version here starts from 3.
+ constexpr uint16_t DeviceImageStructVersion = 3;
+ Constant *Version =
+ ConstantInt::get(Type::getInt16Ty(C), DeviceImageStructVersion);
+ Constant *OffloadKindConstant = ConstantInt::get(
+ Type::getInt8Ty(C), static_cast<uint8_t>(OB.getOffloadKind()));
+ Constant *ImageKindConstant = ConstantInt::get(
+ Type::getInt8Ty(C), static_cast<uint8_t>(OB.getImageKind()));
+ StringRef Triple = OB.getString("triple");
+ Constant *TripleConstant =
+ addStringToModule(Triple, Twine(OffloadKindTag) + "target." + ImageID);
+ Constant *CompileOptions =
+ addStringToModule(Options.CompileOptions,
+ Twine(OffloadKindTag) + "opts.compile." + ImageID);
+ Constant *LinkOptions = addStringToModule(
+ Options.LinkOptions, Twine(OffloadKindTag) + "opts.link." + ImageID);
+
+ // Note: NULL for now.
+ std::pair<Constant *, Constant *> PropertiesConstants = {
+ Constant::getNullValue(PointerType::getUnqual(C)),
+ Constant::getNullValue(PointerType::getUnqual(C))};
+
+ StringRef RawImage = OB.getImage();
+ std::pair<Constant *, Constant *> Binary = addArrayToModule(
+ ArrayRef<char>(RawImage.begin(), RawImage.end()),
+ Twine(OffloadKindTag) + ImageID + ".data", ".llvm.offloading");
+
+ // For SYCL images offload entries are defined here per image.
+ std::pair<Constant *, Constant *> ImageEntriesPtrs =
+ initOffloadEntriesPerImage(OB.getString("symbols"), OffloadKindTag);
+ Constant *WrappedBinary = ConstantStruct::get(
+ SyclDeviceImageTy, Version, OffloadKindConstant, ImageKindConstant,
+ TripleConstant, CompileOptions, LinkOptions, Binary.first,
+ Binary.second, ImageEntriesPtrs.first, ImageEntriesPtrs.second,
+ PropertiesConstants.first, PropertiesConstants.second);
+
+ return WrappedBinary;
+ }
+
+ GlobalVariable *combineWrappedImages(ArrayRef<Constant *> WrappedImages,
+ StringRef OffloadKindTag) {
+ auto *ImagesData = ConstantArray::get(
+ ArrayType::get(SyclDeviceImageTy, WrappedImages.size()), WrappedImages);
+ auto *ImagesGV =
+ new GlobalVariable(M, ImagesData->getType(), /*isConstant*/ true,
+ GlobalValue::InternalLinkage, ImagesData,
+ Twine(OffloadKindTag) + "device_images");
+ ImagesGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
+
+ auto *Zero = ConstantInt::get(getSizeTTy(), 0);
+ Constant *ZeroZero[] = {Zero, Zero};
+ auto *ImagesB = ConstantExpr::getGetElementPtr(ImagesGV->getValueType(),
+ ImagesGV, ZeroZero);
+
+ Constant *EntriesB = Constant::getNullValue(PointerType::getUnqual(C));
+ Constant *EntriesE = Constant::getNullValue(PointerType::getUnqual(C));
+ static constexpr uint16_t BinDescStructVersion = 1;
+ auto *DescInit = ConstantStruct::get(
+ SyclBinDescTy,
+ ConstantInt::get(Type::getInt16Ty(C), BinDescStructVersion),
+ ConstantInt::get(Type::getInt16Ty(C), WrappedImages.size()), ImagesB,
+ EntriesB, EntriesE);
+
+ return new GlobalVariable(M, DescInit->getType(), /*isConstant*/ true,
+ GlobalValue::InternalLinkage, DescInit,
+ Twine(OffloadKindTag) + "descriptor");
+ }
+
+ /// Creates binary descriptor for the given device images. Binary descriptor
+ /// is an object that is passed to the offloading runtime at program startup
+ /// and it describes all device images available in the executable or shared
+ /// library. It is defined as follows:
+ ///
+ /// \code
+ /// __attribute__((visibility("hidden")))
+ /// __tgt_offload_entry *__sycl_offload_entries_arr0[];
+ /// ...
+ /// __attribute__((visibility("hidden")))
+ /// __tgt_offload_entry *__sycl_offload_entries_arrN[];
+ ///
+ /// __attribute__((visibility("hidden")))
+ /// extern const char *CompileOptions = "...";
+ /// ...
+ /// __attribute__((visibility("hidden")))
+ /// extern const char *LinkOptions = "...";
+ /// ...
+ ///
+ /// static const char Image0[] = { ... };
+ /// ...
+ /// static const char ImageN[] = { ... };
+ ///
+ /// static const __sycl.tgt_device_image Images[] = {
+ /// {
+ /// Version, // Version
+ /// OffloadKind, // OffloadKind
+ /// Format, // Format of the image.
+ // TripleString, // Arch
+ /// CompileOptions, // CompileOptions
+ /// LinkOptions, // LinkOptions
+ /// Image0, // ImageStart
+ /// Image0 + IMAGE0_SIZE, // ImageEnd
+ /// __sycl_offload_entries_arr0, // EntriesBegin
+ /// __sycl_offload_entries_arr0 + ENTRIES0_SIZE, // EntriesEnd
+ /// NULL, // PropertiesBegin
+ /// NULL, // PropertiesEnd
+ /// },
+ /// ...
+ /// };
+ ///
+ /// static const __sycl.tgt_bin_desc FatbinDesc = {
+ /// Version, //Version
+ /// sizeof(Images) / sizeof(Images[0]), //NumDeviceImages
+ /// Images, //DeviceImages
+ /// NULL, //HostEntriesBegin
+ /// NULL //HostEntriesEnd
+ /// };
+ /// \endcode
+ ///
+ /// \returns Global variable that represents FatbinDesc.
+ GlobalVariable *createFatbinDesc(ArrayRef<OffloadFile> OffloadFiles) {
+ StringRef OffloadKindTag = ".sycl_offloading.";
+ SmallVector<Constant *> WrappedImages;
+ WrappedImages.reserve(OffloadFiles.size());
+ for (size_t I = 0, E = OffloadFiles.size(); I != E; ++I)
+ WrappedImages.push_back(
+ wrapImage(*OffloadFiles[I].getBinary(), Twine(I), OffloadKindTag));
+
+ return combineWrappedImages(WrappedImages, OffloadKindTag);
+ }
+
+ void createRegisterFatbinFunction(GlobalVariable *FatbinDesc) {
+ auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
+ auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage,
+ Twine("sycl") + ".descriptor_reg", &M);
+ Func->setSection(".text.startup");
+
+ // Get RegFuncName function declaration.
+ auto *RegFuncTy =
+ FunctionType::get(Type::getVoidTy(C), PointerType::getUnqual(C),
+ /*isVarArg=*/false);
+ FunctionCallee RegFuncC =
+ M.getOrInsertFunction("__sycl_register_lib", RegFuncTy);
+
+ // Construct function body
+ IRBuilder Builder(BasicBlock::Create(C, "entry", Func));
+ Builder.CreateCall(RegFuncC, FatbinDesc);
+ Builder.CreateRetVoid();
+
+ // Add this function to constructors.
+ appendToGlobalCtors(M, Func, /*Priority*/ 1);
+ }
+
+ void createUnregisterFunction(GlobalVariable *FatbinDesc) {
+ auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
+ auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage,
+ "sycl.descriptor_unreg", &M);
+ Func->setSection(".text.startup");
+
+ // Get UnregFuncName function declaration.
+ auto *UnRegFuncTy =
+ FunctionType::get(Type::getVoidTy(C), PointerType::getUnqual(C),
+ /*isVarArg=*/false);
+ FunctionCallee UnRegFuncC =
+ M.getOrInsertFunction("__sycl_unregister_lib", UnRegFuncTy);
+
+ // Construct function body
+ IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
+ Builder.CreateCall(UnRegFuncC, FatbinDesc);
+ Builder.CreateRetVoid();
+
+ // Add this function to global destructors.
+ appendToGlobalDtors(M, Func, /*Priority*/ 1);
+ }
+}; // end of SYCLWrapper
+
} // namespace
Error offloading::wrapOpenMPBinaries(Module &M, ArrayRef<ArrayRef<char>> Images,
@@ -660,3 +1051,22 @@ Error offloading::wrapHIPBinary(Module &M, ArrayRef<char> Image,
EmitSurfacesAndTextures);
return Error::success();
}
+
+Error llvm::offloading::wrapSYCLBinaries(llvm::Module &M, ArrayRef<char> Buffer,
+ SYCLJITOptions Options) {
+ SYCLWrapper W(M, Options);
+ MemoryBufferRef MBR(StringRef(Buffer.begin(), Buffer.size()),
+ /*Identifier*/ "");
+ SmallVector<OffloadFile> OffloadFiles;
+ if (Error E = extractOffloadBinaries(MBR, OffloadFiles))
+ return E;
+
+ GlobalVariable *Desc = W.createFatbinDesc(OffloadFiles);
+ if (!Desc)
+ return createStringError(inconvertibleErrorCode(),
+ "No binary descriptors created.");
+
+ W.createRegisterFatbinFunction(Desc);
+ W.createUnregisterFunction(Desc);
+ return Error::success();
+}
diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp
index 4b75307ca0b6c..5000488a52f37 100644
--- a/llvm/lib/Frontend/Offloading/Utility.cpp
+++ b/llvm/lib/Frontend/Offloading/Utility.cpp
@@ -82,11 +82,11 @@ offloading::getOffloadingEntryInitializer(Module &M, object::OffloadKind Kind,
return {EntryInitializer, Str};
}
-void offloading::emitOffloadingEntry(Module &M, object::OffloadKind Kind,
- Constant *Addr, StringRef Name,
- uint64_t Size, uint32_t Flags,
- uint64_t Data, Constant *AuxAddr,
- StringRef SectionName) {
+GlobalVariable *
+offloading::emitOffloadingEntry(Module &M, object::OffloadKind Kind,
+ Constant *Addr, StringRef Name, uint64_t Size,
+ uint32_t Flags, uint64_t Data,
+ Constant *AuxAddr, StringRef SectionName) {
const llvm::Triple &Triple = M.getTargetTriple();
auto [EntryInitializer, NameGV] = getOffloadingEntryInitializer(
@@ -106,6 +106,7 @@ void offloading::emitOffloadingEntry(Module &M, object::OffloadKind Kind,
else
Entry->setSection(SectionName);
Entry->setAlignment(Align(object::OffloadBinary::getAlignment()));
+ return Entry;
}
std::pair<GlobalVariable *, GlobalVariable *>
diff --git a/llvm/tools/llvm-offload-wrapper/llvm-offload-wrapper.cpp b/llvm/tools/llvm-offload-wrapper/llvm-offload-wrapper.cpp
index 9dac1646b1e26..d65b402571ae8 100644
--- a/llvm/tools/llvm-offload-wrapper/llvm-offload-wrapper.cpp
+++ b/llvm/tools/llvm-offload-wrapper/llvm-offload-wrapper.cpp
@@ -84,6 +84,10 @@ static Error wrapImages(ArrayRef<ArrayRef<char>> BuffersToWrap) {
M, BuffersToWrap.front(), offloading::getOffloadEntryArray(M)))
return Err;
break;
+ case llvm::object::OFK_SYCL:
+ if (Error Err = offloading::wrapSYCLBinaries(M, BuffersToWrap.front()))
+ return Err;
+ break;
default:
return createStringError(getOffloadKindName(Kind) +
" wrapping is not supported");
More information about the llvm-commits
mailing list