Skip to content

Commit 628eaff

Browse files
asudarsaNoumanAmir657
authored andcommitted
[Clang][SYCL] Introduce clang-sycl-linker to link SYCL offloading device code (Part 1 of many) (llvm#112245)
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 In this PR, we introduce a new tool that will be used to perform device code linking for SYCL offload kind. It accepts SYCL device objects in LLVM IR bitcode format and will generate a fully linked device object that can then be wrapped and linked into the host object. A primary use case for this tool is to perform device code linking for objects with SYCL offload kind inside the clang-linker-wrapper. It can also be invoked via clang driver as follows: `clang --target=spirv64 --sycl-link input.bc` Device code linking for SYCL offloading kind has a number of known quirks that makes it difficult to use in a unified offloading setting. Two of the primary issues are: 1. Several finalization steps are required to be run on the fully-linked LLVM IR bitcode to gaurantee conformance to SYCL standards. This step is unique to SYCL offloading compilation flow. 2. SPIR-V LLVM Translator tool is an extenal tool and hence SPIR-V IR code generation cannot be done as part of LTO. This limitation will be lifted once SPIR-V backend is available as a viable LLVM backend. Hence, we introduce this new tool to provide a clean wrapper to perform SYCL device linking. Co-Author: Michael Toguchi Thanks --------- Signed-off-by: Arvind Sudarsanam <arvind.sudarsanam@intel.com>
1 parent ea28553 commit 628eaff

File tree

14 files changed

+755
-3
lines changed

14 files changed

+755
-3
lines changed

clang/docs/ClangSYCLLinker.rst

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
=======================
2+
Clang SYCL Linker
3+
=======================
4+
5+
.. contents::
6+
:local:
7+
8+
.. _clang-sycl-linker:
9+
10+
Introduction
11+
============
12+
13+
This tool works as a wrapper around the SYCL device code linking process.
14+
The purpose of this tool is to provide an interface to link SYCL device bitcode
15+
in LLVM IR format, SYCL device bitcode in SPIR-V IR format, and native binary
16+
objects, and then use the SPIR-V LLVM Translator tool on fully linked device
17+
objects to produce the final output.
18+
After the linking stage, the fully linked device code in LLVM IR format may
19+
undergo several SYCL-specific finalization steps before the SPIR-V code
20+
generation step.
21+
The tool will also support the Ahead-Of-Time (AOT) compilation flow. AOT
22+
compilation is the process of invoking the back-end at compile time to produce
23+
the final binary, as opposed to just-in-time (JIT) compilation when final code
24+
generation is deferred until application runtime.
25+
26+
Device code linking for SYCL offloading has several known quirks that
27+
make it difficult to use in a unified offloading setting. Two of the primary
28+
issues are:
29+
1. Several finalization steps are required to be run on the fully linked LLVM
30+
IR bitcode to guarantee conformance to SYCL standards. This step is unique to
31+
the SYCL offloading compilation flow.
32+
2. The SPIR-V LLVM Translator tool is an external tool and hence SPIR-V IR code
33+
generation cannot be done as part of LTO. This limitation can be lifted once
34+
the SPIR-V backend is available as a viable LLVM backend.
35+
36+
This tool has been proposed to work around these issues.
37+
38+
Usage
39+
=====
40+
41+
This tool can be used with the following options. Several of these options will
42+
be passed down to downstream tools like 'llvm-link', 'llvm-spirv', etc.
43+
44+
.. code-block:: console
45+
46+
OVERVIEW: A utility that wraps around the SYCL device code linking process.
47+
This enables linking and code generation for SPIR-V JIT targets and AOT
48+
targets.
49+
50+
USAGE: clang-sycl-linker [options]
51+
52+
OPTIONS:
53+
--arch <value> Specify the name of the target architecture.
54+
--dry-run Print generated commands without running.
55+
-g Specify that this was a debug compile.
56+
-help-hidden Display all available options
57+
-help Display available options (--help-hidden for more)
58+
--library-path=<dir> Set the library path for SYCL device libraries
59+
--device-libs=<value> A comma separated list of device libraries that are linked during the device link
60+
-o <path> Path to file to write output
61+
--save-temps Save intermediate results
62+
--triple <value> Specify the target triple.
63+
--version Display the version number and exit
64+
-v Print verbose information
65+
-spirv-dump-device-code=<dir> Directory to dump SPIR-V IR code into
66+
-is-windows-msvc-env Specify if we are compiling under windows environment
67+
-llvm-spirv-options=<value> Pass options to llvm-spirv tool
68+
--llvm-spirv-path=<dir> Set the system llvm-spirv path
69+
70+
Example
71+
=======
72+
73+
This tool is intended to be invoked when targeting any of the target offloading
74+
toolchains. When the --sycl-link option is passed to the clang driver, the
75+
driver will invoke the linking job of the target offloading toolchain, which in
76+
turn will invoke this tool. This tool can be used to create one or more fully
77+
linked device images that are ready to be wrapped and linked with host code to
78+
generate the final executable.
79+
80+
.. code-block:: console
81+
82+
clang-sycl-linker --triple spirv64 --arch native input.bc

clang/docs/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ Using Clang Tools
9898
ClangOffloadBundler
9999
ClangOffloadPackager
100100
ClangRepl
101+
ClangSYCLLinker
101102

102103
Design Documents
103104
================

clang/include/clang/Driver/Options.td

+4-1
Original file line numberDiff line numberDiff line change
@@ -6775,7 +6775,10 @@ def fsycl : Flag<["-"], "fsycl">,
67756775
def fno_sycl : Flag<["-"], "fno-sycl">,
67766776
Visibility<[ClangOption, CLOption]>,
67776777
Group<sycl_Group>, HelpText<"Disables SYCL kernels compilation for device">;
6778-
6778+
def sycl_link : Flag<["--"], "sycl-link">, Flags<[HelpHidden]>,
6779+
Visibility<[ClangOption, CLOption]>,
6780+
Group<sycl_Group>, HelpText<"Perform link through clang-sycl-linker via the target "
6781+
"offloading toolchain.">;
67796782
// OS-specific options
67806783
let Flags = [TargetSpecific] in {
67816784
defm android_pad_segment : BooleanFFlag<"android-pad-segment">, Group<f_Group>;

clang/lib/Driver/Driver.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -4791,6 +4791,11 @@ Action *Driver::ConstructPhaseAction(
47914791
if (Phase == phases::Assemble && Input->getType() != types::TY_PP_Asm)
47924792
return Input;
47934793

4794+
// Use of --sycl-link will only allow for the link phase to occur. This is
4795+
// for all input files.
4796+
if (Args.hasArg(options::OPT_sycl_link) && Phase != phases::Link)
4797+
return Input;
4798+
47944799
// Build the appropriate action.
47954800
switch (Phase) {
47964801
case phases::Link:

clang/lib/Driver/ToolChains/SPIRV.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,21 @@ void SPIRV::Linker::ConstructJob(Compilation &C, const JobAction &JA,
9595
CmdArgs.push_back("-o");
9696
CmdArgs.push_back(Output.getFilename());
9797

98+
// Use of --sycl-link will call the clang-sycl-linker instead of
99+
// the default linker (spirv-link).
100+
if (Args.hasArg(options::OPT_sycl_link))
101+
Linker = ToolChain.GetProgramPath("clang-sycl-linker");
98102
C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
99103
Args.MakeArgString(Linker), CmdArgs,
100104
Inputs, Output));
101105
}
106+
107+
SPIRVToolChain::SPIRVToolChain(const Driver &D, const llvm::Triple &Triple,
108+
const ArgList &Args)
109+
: ToolChain(D, Triple, Args) {
110+
// TODO: Revisit need/use of --sycl-link option once SYCL toolchain is
111+
// available and SYCL linking support is moved there.
112+
NativeLLVMSupport = Args.hasArg(options::OPT_sycl_link);
113+
}
114+
115+
bool SPIRVToolChain::HasNativeLLVMSupport() const { return NativeLLVMSupport; }

clang/lib/Driver/ToolChains/SPIRV.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ class LLVM_LIBRARY_VISIBILITY SPIRVToolChain final : public ToolChain {
5757

5858
public:
5959
SPIRVToolChain(const Driver &D, const llvm::Triple &Triple,
60-
const llvm::opt::ArgList &Args)
61-
: ToolChain(D, Triple, Args) {}
60+
const llvm::opt::ArgList &Args);
6261

6362
bool useIntegratedAs() const override { return true; }
6463

@@ -72,6 +71,7 @@ class LLVM_LIBRARY_VISIBILITY SPIRVToolChain final : public ToolChain {
7271
}
7372
bool isPICDefaultForced() const override { return false; }
7473
bool SupportsProfiling() const override { return false; }
74+
bool HasNativeLLVMSupport() const override;
7575

7676
clang::driver::Tool *SelectTool(const JobAction &JA) const override;
7777

@@ -81,6 +81,7 @@ class LLVM_LIBRARY_VISIBILITY SPIRVToolChain final : public ToolChain {
8181

8282
private:
8383
clang::driver::Tool *getTranslator() const;
84+
bool NativeLLVMSupport;
8485
};
8586

8687
} // namespace toolchains

clang/test/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ list(APPEND CLANG_TEST_DEPS
8080
clang-nvlink-wrapper
8181
clang-offload-bundler
8282
clang-offload-packager
83+
clang-sycl-linker
8384
diagtool
8485
hmaptool
8586
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Tests the clang-sycl-linker tool.
2+
//
3+
// Test a simple case without arguments.
4+
// RUN: %clangxx -emit-llvm -c %s -o %t_1.bc
5+
// RUN: %clangxx -emit-llvm -c %s -o %t_2.bc
6+
// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \
7+
// RUN: | FileCheck %s --check-prefix=SIMPLE
8+
// SIMPLE: "{{.*}}llvm-link{{.*}}" {{.*}}.bc {{.*}}.bc -o [[FIRSTLLVMLINKOUT:.*]].bc --suppress-warnings
9+
// SIMPLE-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[FIRSTLLVMLINKOUT]].bc
10+
//
11+
// Test that llvm-link is not called when only one input is present.
12+
// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc -o a.spv 2>&1 \
13+
// RUN: | FileCheck %s --check-prefix=SIMPLE-NO-LINK
14+
// SIMPLE-NO-LINK: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv {{.*}}.bc
15+
//
16+
// Test a simple case with device library files specified.
17+
// RUN: touch %T/lib1.bc
18+
// RUN: touch %T/lib2.bc
19+
// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc -o a.spv 2>&1 \
20+
// RUN: | FileCheck %s --check-prefix=DEVLIBS
21+
// DEVLIBS: "{{.*}}llvm-link{{.*}}" {{.*}}.bc {{.*}}.bc -o [[FIRSTLLVMLINKOUT:.*]].bc --suppress-warnings
22+
// DEVLIBS-NEXT: "{{.*}}llvm-link{{.*}}" -only-needed [[FIRSTLLVMLINKOUT]].bc {{.*}}lib1.bc {{.*}}lib2.bc -o [[SECONDLLVMLINKOUT:.*]].bc --suppress-warnings
23+
// DEVLIBS-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[SECONDLLVMLINKOUT]].bc
24+
//
25+
// Test a simple case with .o (fat object) as input.
26+
// TODO: Remove this test once fat object support is added.
27+
// RUN: %clangxx -c %s -o %t.o
28+
// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t.o -o a.spv 2>&1 \
29+
// RUN: | FileCheck %s --check-prefix=FILETYPEERROR
30+
// FILETYPEERROR: Unsupported file type
31+
//
32+
// Test to see if device library related errors are emitted.
33+
// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs= -o a.spv 2>&1 \
34+
// RUN: | FileCheck %s --check-prefix=DEVLIBSERR1
35+
// DEVLIBSERR1: Number of device library files cannot be zero
36+
// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc,lib3.bc -o a.spv 2>&1 \
37+
// RUN: | FileCheck %s --check-prefix=DEVLIBSERR2
38+
// DEVLIBSERR2: '{{.*}}lib3.bc' SYCL device library file is not found
39+
//
40+
// Test if correct set of llvm-spirv options are emitted for windows environment.
41+
// RUN: clang-sycl-linker --dry-run -triple spirv64 --is-windows-msvc-env %t_1.bc %t_2.bc -o a.spv 2>&1 \
42+
// RUN: | FileCheck %s --check-prefix=LLVMOPTSWIN
43+
// LLVMOPTSWIN: -spirv-debug-info-version=ocl-100 -spirv-allow-extra-diexpressions -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext=
44+
//
45+
// Test if correct set of llvm-spirv options are emitted for linux environment.
46+
// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \
47+
// RUN: | FileCheck %s --check-prefix=LLVMOPTSLIN
48+
// LLVMOPTSLIN: -spirv-debug-info-version=nonsemantic-shader-200 -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext=
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Tests the driver when linking LLVM IR bitcode files and targeting SPIR-V
2+
// architecture.
3+
//
4+
// Test that -Xlinker options are being passed to clang-sycl-linker.
5+
// RUN: touch %t.bc
6+
// RUN: %clangxx -### --target=spirv64 --sycl-link -Xlinker --llvm-spirv-path=/tmp \
7+
// RUN: -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.bc,lib2.bc %t.bc 2>&1 \
8+
// RUN: | FileCheck %s -check-prefix=XLINKEROPTS
9+
// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "--library-path=/tmp" "--device-libs=lib1.bc,lib2.bc" "{{.*}}.bc" "-o" "a.out"

clang/test/lit.cfg.py

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"yaml2obj",
9797
"clang-linker-wrapper",
9898
"clang-nvlink-wrapper",
99+
"clang-sycl-linker",
99100
"llvm-lto",
100101
"llvm-lto2",
101102
"llvm-profdata",

clang/tools/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_clang_subdirectory(clang-nvlink-wrapper)
1212
add_clang_subdirectory(clang-offload-packager)
1313
add_clang_subdirectory(clang-offload-bundler)
1414
add_clang_subdirectory(clang-scan-deps)
15+
add_clang_subdirectory(clang-sycl-linker)
1516
add_clang_subdirectory(clang-installapi)
1617
if(HAVE_CLANG_REPL_SUPPORT)
1718
add_clang_subdirectory(clang-repl)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
set(LLVM_LINK_COMPONENTS
2+
${LLVM_TARGETS_TO_BUILD}
3+
Option
4+
)
5+
6+
set(LLVM_TARGET_DEFINITIONS SYCLLinkOpts.td)
7+
tablegen(LLVM SYCLLinkOpts.inc -gen-opt-parser-defs)
8+
add_public_tablegen_target(SYCLLinkerOpts)
9+
10+
if(NOT CLANG_BUILT_STANDALONE)
11+
set(tablegen_deps intrinsics_gen SYCLLinkerOpts)
12+
endif()
13+
14+
add_clang_tool(clang-sycl-linker
15+
ClangSYCLLinker.cpp
16+
17+
DEPENDS
18+
${tablegen_deps}
19+
)
20+
21+
set(CLANG_SYCL_LINKER_LIB_DEPS
22+
clangBasic
23+
)
24+
25+
target_link_libraries(clang-sycl-linker
26+
PRIVATE
27+
${CLANG_SYCL_LINKER_LIB_DEPS}
28+
)

0 commit comments

Comments
 (0)