Skip to content

Commit 52e10e6

Browse files
authored
[SYCL] Add clang-linker-wrapper changes to call clang-sycl-linker for SYCL offloads (#135683)
This PR is one of the many PRs in the SYCL upstreaming effort focusing on device code linking during the SYCL offload compilation process. RFC: https://discourse.llvm.org/t/rfc-offloading-design-for-sycl-offload-kind-and-spir-targets/74088 Approved PRs so far: 1. [Clang][SYCL] Introduce clang-sycl-linker to link SYCL offloading device code (Part 1 of many) - [Link](llvm/llvm-project#112245) 2. [clang-sycl-linker] Replace llvm-link with API calls - [Link](llvm/llvm-project#133797) 3. [SYCL][SPIR-V Backend][clang-sycl-linker] Add SPIR-V backend support inside clang-sycl-linker - [Link](llvm/llvm-project#133967) This PR adds SYCL device code linking support to clang-linker-wrapper. ### Summary for this PR Device code linking happens inside clang-linker-wrapper. In the current implementation, clang-linker-wrapper does the following: 1. Extracts device code. Input_1, Input_2,..... 5. Group device code according to target devices Inputs[triple_1] = .... Inputs[triple_2] = .... 6. For each group, i.e. Inputs[triple_i], a. Gather all the offload kinds found inside those inputs in ActiveOffloadKinds b. Link all images inside Inputs[triple_i] by calling clang --target=triple_i .... c. Create a copy of that linked image for each offload kind and add it to Output[Kind] list. In SYCL compilation flow, there is a deviation in Step 3b. We call device code splitting inside the 'clang --target=triple_i ....' call and the output is now a 'packaged' file containing multiple device images. This deviation requires us to capture the OffloadKind during the linking stage and pass it along to the linking function (clang), so that clang can be called with a unique option '--sycl-link' that will help us to call 'clang-sycl-linker' under the hood (clang-sycl-linker will do SYCL specific linking). Our current objective is to implement an end-to-end SYCL offloading flow and get it working. We will eventually merge our approach with the community flow. Thanks --------- Signed-off-by: Arvind Sudarsanam <arvind.sudarsanam@intel.com>
1 parent 2ef0104 commit 52e10e6

File tree

6 files changed

+98
-14
lines changed

6 files changed

+98
-14
lines changed

clang/docs/ClangOffloadPackager.rst

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ the following values for the :ref:`offload kind<table-offload_kind>` and the
112112
+------------+-------+---------------------------------------+
113113
| OFK_HIP | 0x03 | The producer was HIP |
114114
+------------+-------+---------------------------------------+
115+
| OFK_SYCL | 0x04 | The producer was SYCL |
116+
+------------+-------+---------------------------------------+
115117

116118
The flags are used to signify certain conditions, such as the presence of
117119
debugging information or whether or not LTO was used. The string entry table is

clang/test/Driver/clang-sycl-linker-test.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \
99
// RUN: | FileCheck %s --check-prefix=SIMPLE-FO
1010
// SIMPLE-FO: sycl-device-link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc
11-
// SIMPLE-FO-NEXT: SPIR-V Backend: input: [[LLVMLINKOUT]].bc, output: a.spv
11+
// SIMPLE-FO-NEXT: SPIR-V Backend: input: [[LLVMLINKOUT]].bc, output: a_0.spv
1212
//
1313
// Test the dry run of a simple case with device library files specified.
1414
// RUN: touch %T/lib1.bc
1515
// RUN: touch %T/lib2.bc
1616
// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc -o a.spv 2>&1 \
1717
// RUN: | FileCheck %s --check-prefix=DEVLIBS
1818
// DEVLIBS: sycl-device-link: inputs: {{.*}}.bc libfiles: {{.*}}lib1.bc, {{.*}}lib2.bc output: [[LLVMLINKOUT:.*]].bc
19-
// DEVLIBS-NEXT: SPIR-V Backend: input: [[LLVMLINKOUT]].bc, output: a.spv
19+
// DEVLIBS-NEXT: SPIR-V Backend: input: [[LLVMLINKOUT]].bc, output: a_0.spv
2020
//
2121
// Test a simple case with a random file (not bitcode) as input.
2222
// RUN: touch %t.o

clang/test/Driver/linker-wrapper.c

+10
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
// REQUIRES: x86-registered-target
33
// REQUIRES: nvptx-registered-target
44
// REQUIRES: amdgpu-registered-target
5+
// REQUIRES: spirv-registered-target
56

67
// An externally visible variable so static libraries extract.
78
__attribute__((visibility("protected"), used)) int x;
89

910
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.elf.o
1011
// RUN: %clang -cc1 %s -triple nvptx64-nvidia-cuda -emit-llvm-bc -o %t.nvptx.bc
1112
// RUN: %clang -cc1 %s -triple amdgcn-amd-amdhsa -emit-llvm-bc -o %t.amdgpu.bc
13+
// RUN: %clang -cc1 %s -triple spirv64-unknown-unknown -emit-llvm-bc -o %t.spirv.bc
1214

1315
// RUN: clang-offload-packager -o %t.out \
1416
// RUN: --image=file=%t.elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
@@ -49,6 +51,14 @@ __attribute__((visibility("protected"), used)) int x;
4951

5052
// AMDGPU-LTO-TEMPS: clang{{.*}} --target=amdgcn-amd-amdhsa -mcpu=gfx1030 -flto {{.*}}-save-temps
5153

54+
// RUN: clang-offload-packager -o %t.out \
55+
// RUN: --image=file=%t.spirv.bc,kind=sycl,triple=spirv64-unknown-unknown,arch=generic
56+
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
57+
// RUN: not clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
58+
// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=SPIRV-LINK
59+
60+
// SPIRV-LINK: clang{{.*}} -o {{.*}}.img --target=spirv64-unknown-unknown {{.*}}.o --sycl-link -Xlinker -triple=spirv64-unknown-unknown -Xlinker -arch=
61+
5262
// RUN: clang-offload-packager -o %t.out \
5363
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu \
5464
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu

clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp

+27-4
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,8 @@ fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
464464
} // namespace amdgcn
465465

466466
namespace generic {
467-
Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
467+
Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args,
468+
uint16_t ActiveOffloadKindMask) {
468469
llvm::TimeTraceScope TimeScope("Clang");
469470
// Use `clang` to invoke the appropriate device tools.
470471
Expected<std::string> ClangPath =
@@ -554,6 +555,17 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
554555
if (Args.hasArg(OPT_embed_bitcode))
555556
CmdArgs.push_back("-Wl,--lto-emit-llvm");
556557

558+
// For linking device code with the SYCL offload kind, special handling is
559+
// required. Passing --sycl-link to clang results in a call to
560+
// clang-sycl-linker. Additional linker flags required by clang-sycl-linker
561+
// will be communicated via the -Xlinker option.
562+
if (ActiveOffloadKindMask & OFK_SYCL) {
563+
CmdArgs.push_back("--sycl-link");
564+
CmdArgs.append(
565+
{"-Xlinker", Args.MakeArgString("-triple=" + Triple.getTriple())});
566+
CmdArgs.append({"-Xlinker", Args.MakeArgString("-arch=" + Arch)});
567+
}
568+
557569
for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
558570
CmdArgs.append({"-Xlinker", Args.MakeArgString(Arg)});
559571
for (StringRef Arg : Args.getAllArgValues(OPT_compiler_arg_EQ))
@@ -567,7 +579,8 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
567579
} // namespace generic
568580

569581
Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles,
570-
const ArgList &Args) {
582+
const ArgList &Args,
583+
uint16_t ActiveOffloadKindMask) {
571584
const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
572585
switch (Triple.getArch()) {
573586
case Triple::nvptx:
@@ -582,7 +595,7 @@ Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles,
582595
case Triple::spirv64:
583596
case Triple::systemz:
584597
case Triple::loongarch64:
585-
return generic::clang(InputFiles, Args);
598+
return generic::clang(InputFiles, Args, ActiveOffloadKindMask);
586599
default:
587600
return createStringError(Triple.getArchName() +
588601
" linking is not supported");
@@ -792,6 +805,7 @@ bundleLinkedOutput(ArrayRef<OffloadingImage> Images, const ArgList &Args,
792805
llvm::TimeTraceScope TimeScope("Bundle linked output");
793806
switch (Kind) {
794807
case OFK_OpenMP:
808+
case OFK_SYCL:
795809
return bundleOpenMP(Images);
796810
case OFK_Cuda:
797811
return bundleCuda(Images, Args);
@@ -927,6 +941,14 @@ Expected<SmallVector<StringRef>> linkAndWrapDeviceFiles(
927941
for (const auto &File : Input)
928942
ActiveOffloadKindMask |= File.getBinary()->getOffloadKind();
929943

944+
// Linking images of SYCL offload kind with images of other kind is not
945+
// supported.
946+
// TODO: Remove the above limitation.
947+
if ((ActiveOffloadKindMask & OFK_SYCL) &&
948+
((ActiveOffloadKindMask ^ OFK_SYCL) != 0))
949+
return createStringError("Linking images of SYCL offload kind with "
950+
"images of any other kind is not supported");
951+
930952
// Write any remaining device inputs to an output file.
931953
SmallVector<StringRef> InputFiles;
932954
for (const OffloadFile &File : Input) {
@@ -937,7 +959,8 @@ Expected<SmallVector<StringRef>> linkAndWrapDeviceFiles(
937959
}
938960

939961
// Link the remaining device files using the device linker.
940-
auto OutputOrErr = linkDevice(InputFiles, LinkerArgs);
962+
auto OutputOrErr =
963+
linkDevice(InputFiles, LinkerArgs, ActiveOffloadKindMask);
941964
if (!OutputOrErr)
942965
return OutputOrErr.takeError();
943966

clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp

+54-8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ static StringRef OutputFile;
7070
/// Directory to dump SPIR-V IR if requested by user.
7171
static SmallString<128> SPIRVDumpDir;
7272

73+
using OffloadingImage = OffloadBinary::OffloadingImage;
74+
7375
static void printVersion(raw_ostream &OS) {
7476
OS << clang::getClangToolFullVersion("clang-sycl-linker") << '\n';
7577
}
@@ -278,8 +280,8 @@ Expected<StringRef> linkDeviceCode(ArrayRef<std::string> InputFiles,
278280
/// Converts 'File' from LLVM bitcode to SPIR-V format using SPIR-V backend.
279281
/// 'Args' encompasses all arguments required for linking device code and will
280282
/// be parsed to generate options required to be passed into the backend.
281-
static Expected<StringRef> runSPIRVCodeGen(StringRef File, const ArgList &Args,
282-
LLVMContext &C) {
283+
static Error runSPIRVCodeGen(StringRef File, const ArgList &Args,
284+
StringRef OutputFile, LLVMContext &C) {
283285
llvm::TimeTraceScope TimeScope("SPIR-V code generation");
284286

285287
// Parse input module.
@@ -289,7 +291,7 @@ static Expected<StringRef> runSPIRVCodeGen(StringRef File, const ArgList &Args,
289291
return createStringError(Err.getMessage());
290292

291293
if (Error Err = M->materializeAll())
292-
return std::move(Err);
294+
return Err;
293295

294296
Triple TargetTriple(Args.getLastArgValue(OPT_triple_EQ));
295297
M->setTargetTriple(TargetTriple);
@@ -333,7 +335,7 @@ static Expected<StringRef> runSPIRVCodeGen(StringRef File, const ArgList &Args,
333335
errs() << formatv("SPIR-V Backend: input: {0}, output: {1}\n", File,
334336
OutputFile);
335337

336-
return OutputFile;
338+
return Error::success();
337339
}
338340

339341
/// Performs the following steps:
@@ -349,10 +351,54 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
349351
if (!LinkedFile)
350352
reportError(LinkedFile.takeError());
351353

354+
// TODO: SYCL post link functionality involves device code splitting and will
355+
// result in multiple bitcode codes.
356+
// The following lines are placeholders to represent multiple files and will
357+
// be refactored once SYCL post link support is available.
358+
SmallVector<std::string> SplitModules;
359+
SplitModules.emplace_back(*LinkedFile);
360+
352361
// SPIR-V code generation step.
353-
auto SPVFile = runSPIRVCodeGen(*LinkedFile, Args, C);
354-
if (!SPVFile)
355-
return SPVFile.takeError();
362+
for (size_t I = 0, E = SplitModules.size(); I != E; ++I) {
363+
auto Stem = OutputFile.rsplit('.').first;
364+
std::string SPVFile(Stem);
365+
SPVFile.append("_" + utostr(I) + ".spv");
366+
auto Err = runSPIRVCodeGen(SplitModules[I], Args, SPVFile, C);
367+
if (Err)
368+
return std::move(Err);
369+
SplitModules[I] = SPVFile;
370+
}
371+
372+
// Write the final output into file.
373+
int FD = -1;
374+
if (std::error_code EC = sys::fs::openFileForWrite(OutputFile, FD))
375+
return errorCodeToError(EC);
376+
llvm::raw_fd_ostream FS(FD, /*shouldClose=*/true);
377+
378+
for (size_t I = 0, E = SplitModules.size(); I != E; ++I) {
379+
auto File = SplitModules[I];
380+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr =
381+
llvm::MemoryBuffer::getFileOrSTDIN(File);
382+
if (std::error_code EC = FileOrErr.getError()) {
383+
if (DryRun)
384+
FileOrErr = MemoryBuffer::getMemBuffer("");
385+
else
386+
return createFileError(File, EC);
387+
}
388+
OffloadingImage TheImage{};
389+
TheImage.TheImageKind = IMG_Object;
390+
TheImage.TheOffloadKind = OFK_SYCL;
391+
TheImage.StringData["triple"] =
392+
Args.MakeArgString(Args.getLastArgValue(OPT_triple_EQ));
393+
TheImage.StringData["arch"] =
394+
Args.MakeArgString(Args.getLastArgValue(OPT_arch_EQ));
395+
TheImage.Image = std::move(*FileOrErr);
396+
397+
llvm::SmallString<0> Buffer = OffloadBinary::write(TheImage);
398+
if (Buffer.size() % OffloadBinary::getAlignment() != 0)
399+
return createStringError("Offload binary has invalid size alignment");
400+
FS << Buffer;
401+
}
356402
return Error::success();
357403
}
358404

@@ -394,7 +440,7 @@ int main(int argc, char **argv) {
394440
DryRun = Args.hasArg(OPT_dry_run);
395441
SaveTemps = Args.hasArg(OPT_save_temps);
396442

397-
OutputFile = "a.spv";
443+
OutputFile = "a.out";
398444
if (Args.hasArg(OPT_o))
399445
OutputFile = Args.getLastArgValue(OPT_o);
400446

llvm/lib/Object/OffloadBinary.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ OffloadKind object::getOffloadKind(StringRef Name) {
301301
.Case("openmp", OFK_OpenMP)
302302
.Case("cuda", OFK_Cuda)
303303
.Case("hip", OFK_HIP)
304+
.Case("sycl", OFK_SYCL)
304305
.Default(OFK_None);
305306
}
306307

@@ -312,6 +313,8 @@ StringRef object::getOffloadKindName(OffloadKind Kind) {
312313
return "cuda";
313314
case OFK_HIP:
314315
return "hip";
316+
case OFK_SYCL:
317+
return "sycl";
315318
default:
316319
return "none";
317320
}

0 commit comments

Comments
 (0)