Skip to content

Commit 4a4d41e

Browse files
authored
[SYCL][SPIR-V Backend][clang-sycl-linker] Add SPIR-V backend support inside clang-sycl-linker (#133967)
This PR does the following: 1. Use SPIR-V backend to do LLVM to SPIR-V translation inside clang-sycl-linker 2. Remove llvm-spirv translator from clang-sycl-linker Currently, no SPIR-V extensions are enabled for SYCL compilation flow. This will be updated in subsequent commits. Thanks Note: This is one of the many PRs being introduced to add SYCL programming model support to LLVM ([RFC](https://discourse.llvm.org/t/rfc-add-sycl-programming-model-support/50812)). --------- Signed-off-by: Arvind Sudarsanam <[email protected]>
1 parent 8b67f36 commit 4a4d41e

File tree

9 files changed

+81
-181
lines changed

9 files changed

+81
-181
lines changed

clang/test/Driver/Inputs/SYCL/bar.ll

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
12
target triple = "spirv64"
23

34
define spir_func i32 @bar_func1(i32 %a, i32 %b) {

clang/test/Driver/Inputs/SYCL/baz.ll

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
12
target triple = "spirv64"
23

34
define spir_func i32 @bar_func1(i32 %a, i32 %b) {

clang/test/Driver/Inputs/SYCL/foo.ll

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
12
target triple = "spirv64"
23

34
define spir_func i32 @foo_func1(i32 %a, i32 %b) {

clang/test/Driver/Inputs/SYCL/libsycl.ll

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
12
target triple = "spirv64"
23

34
define spir_func i32 @addFive(i32 %a) {

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

+2-12
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \
77
// RUN: | FileCheck %s --check-prefix=SIMPLE-FO
88
// SIMPLE-FO: sycl-device-link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc
9-
// SIMPLE-FO-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc
9+
// SIMPLE-FO-NEXT: SPIR-V Backend: input: [[LLVMLINKOUT]].bc, output: a.spv
1010
//
1111
// Test the dry run of a simple case with device library files specified.
1212
// RUN: touch %T/lib1.bc
1313
// RUN: touch %T/lib2.bc
1414
// 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 \
1515
// RUN: | FileCheck %s --check-prefix=DEVLIBS
1616
// DEVLIBS: sycl-device-link: inputs: {{.*}}.bc libfiles: {{.*}}lib1.bc, {{.*}}lib2.bc output: [[LLVMLINKOUT:.*]].bc
17-
// DEVLIBS-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc
17+
// DEVLIBS-NEXT: SPIR-V Backend: input: [[LLVMLINKOUT]].bc, output: a.spv
1818
//
1919
// Test a simple case with a random file (not bitcode) as input.
2020
// RUN: touch %t.o
@@ -29,13 +29,3 @@
2929
// 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 \
3030
// RUN: | FileCheck %s --check-prefix=DEVLIBSERR2
3131
// DEVLIBSERR2: '{{.*}}lib3.bc' SYCL device library file is not found
32-
//
33-
// Test if correct set of llvm-spirv options are emitted for windows environment.
34-
// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 --is-windows-msvc-env %t_1.bc %t_2.bc -o a.spv 2>&1 \
35-
// RUN: | FileCheck %s --check-prefix=LLVMOPTSWIN
36-
// LLVMOPTSWIN: -spirv-debug-info-version=ocl-100 -spirv-allow-extra-diexpressions -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext=
37-
//
38-
// Test if correct set of llvm-spirv options are emitted for linux environment.
39-
// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \
40-
// RUN: | FileCheck %s --check-prefix=LLVMOPTSLIN
41-
// LLVMOPTSLIN: -spirv-debug-info-version=nonsemantic-shader-200 -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext=

clang/test/Driver/sycl-link-spirv-target.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44
// Test that -Xlinker options are being passed to clang-sycl-linker.
55
// RUN: touch %t.bc
6-
// RUN: %clangxx -### --target=spirv64 --sycl-link -Xlinker --llvm-spirv-path=/tmp \
7-
// RUN: -Xlinker -triple=spirv64 -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.bc,lib2.bc %t.bc 2>&1 \
6+
// RUN: %clangxx -### --target=spirv64 --sycl-link -Xlinker -triple=spirv64 -Xlinker --library-path=/tmp \
7+
// RUN: -Xlinker --device-libs=lib1.bc,lib2.bc %t.bc 2>&1 \
88
// RUN: | FileCheck %s -check-prefix=XLINKEROPTS
9-
// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "-triple=spirv64" "--library-path=/tmp" "--device-libs=lib1.bc,lib2.bc" "{{.*}}.bc" "-o" "a.out"
9+
// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "-triple=spirv64" "--library-path=/tmp" "--device-libs=lib1.bc,lib2.bc" "{{.*}}.bc" "-o" "a.out"

clang/tools/clang-sycl-linker/CMakeLists.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
set(LLVM_LINK_COMPONENTS
22
${LLVM_TARGETS_TO_BUILD}
3+
Analysis
34
BinaryFormat
45
BitWriter
56
Core
67
IRReader
78
Linker
9+
MC
810
Option
911
Object
10-
TargetParser
1112
Support
13+
Target
14+
TargetParser
1215
)
1316

1417
set(LLVM_TARGET_DEFINITIONS SYCLLinkOpts.td)

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

+67-152
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/IRReader/IRReader.h"
2626
#include "llvm/LTO/LTO.h"
2727
#include "llvm/Linker/Linker.h"
28+
#include "llvm/MC/TargetRegistry.h"
2829
#include "llvm/Object/Archive.h"
2930
#include "llvm/Object/ArchiveWriter.h"
3031
#include "llvm/Object/Binary.h"
@@ -48,6 +49,7 @@
4849
#include "llvm/Support/TargetSelect.h"
4950
#include "llvm/Support/TimeProfiler.h"
5051
#include "llvm/Support/WithColor.h"
52+
#include "llvm/Target/TargetMachine.h"
5153

5254
using namespace llvm;
5355
using namespace llvm::opt;
@@ -141,40 +143,6 @@ Expected<StringRef> createTempFile(const ArgList &Args, const Twine &Prefix,
141143
return TempFiles.back();
142144
}
143145

144-
Expected<std::string> findProgram(const ArgList &Args, StringRef Name,
145-
ArrayRef<StringRef> Paths) {
146-
if (Args.hasArg(OPT_dry_run))
147-
return Name.str();
148-
ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);
149-
if (!Path)
150-
Path = sys::findProgramByName(Name);
151-
if (!Path)
152-
return createStringError(Path.getError(),
153-
"Unable to find '" + Name + "' in path");
154-
return *Path;
155-
}
156-
157-
void printCommands(ArrayRef<StringRef> CmdArgs) {
158-
if (CmdArgs.empty())
159-
return;
160-
161-
llvm::errs() << " \"" << CmdArgs.front() << "\" ";
162-
llvm::errs() << llvm::join(std::next(CmdArgs.begin()), CmdArgs.end(), " ")
163-
<< "\n";
164-
}
165-
166-
/// Execute the command \p ExecutablePath with the arguments \p Args.
167-
Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) {
168-
if (Verbose || DryRun)
169-
printCommands(Args);
170-
171-
if (!DryRun)
172-
if (sys::ExecuteAndWait(ExecutablePath, Args))
173-
return createStringError(
174-
"'%s' failed", sys::path::filename(ExecutablePath).str().c_str());
175-
return Error::success();
176-
}
177-
178146
Expected<SmallVector<std::string>> getInput(const ArgList &Args) {
179147
// Collect all input bitcode files to be passed to the device linking stage.
180148
SmallVector<std::string> BitcodeFiles;
@@ -243,12 +211,11 @@ Expected<SmallVector<std::string>> getSYCLDeviceLibs(const ArgList &Args) {
243211
/// 3. Link all the images gathered in Step 2 with the output of Step 1 using
244212
/// linkInModule API. LinkOnlyNeeded flag is used.
245213
Expected<StringRef> linkDeviceCode(ArrayRef<std::string> InputFiles,
246-
const ArgList &Args) {
214+
const ArgList &Args, LLVMContext &C) {
247215
llvm::TimeTraceScope TimeScope("SYCL link device code");
248216

249217
assert(InputFiles.size() && "No inputs to link");
250218

251-
LLVMContext C;
252219
auto LinkerOutput = std::make_unique<Module>("sycl-device-link", C);
253220
Linker L(*LinkerOutput);
254221
// Link SYCL device input files.
@@ -307,120 +274,61 @@ Expected<StringRef> linkDeviceCode(ArrayRef<std::string> InputFiles,
307274
return *BitcodeOutput;
308275
}
309276

310-
/// Add any llvm-spirv option that relies on a specific Triple in addition
311-
/// to user supplied options.
312-
static void getSPIRVTransOpts(const ArgList &Args,
313-
SmallVector<StringRef, 8> &TranslatorArgs,
314-
const llvm::Triple Triple) {
315-
// Enable NonSemanticShaderDebugInfo.200 for non-Windows
316-
const bool IsWindowsMSVC =
317-
Triple.isWindowsMSVCEnvironment() || Args.hasArg(OPT_is_windows_msvc_env);
318-
const bool EnableNonSemanticDebug = !IsWindowsMSVC;
319-
if (EnableNonSemanticDebug) {
320-
TranslatorArgs.push_back(
321-
"-spirv-debug-info-version=nonsemantic-shader-200");
322-
} else {
323-
TranslatorArgs.push_back("-spirv-debug-info-version=ocl-100");
324-
// Prevent crash in the translator if input IR contains DIExpression
325-
// operations which don't have mapping to OpenCL.DebugInfo.100 spec.
326-
TranslatorArgs.push_back("-spirv-allow-extra-diexpressions");
327-
}
328-
std::string UnknownIntrinsics("-spirv-allow-unknown-intrinsics=llvm.genx.");
329-
330-
TranslatorArgs.push_back(Args.MakeArgString(UnknownIntrinsics));
331-
332-
// Disable all the extensions by default
333-
std::string ExtArg("-spirv-ext=-all");
334-
std::string DefaultExtArg =
335-
",+SPV_EXT_shader_atomic_float_add,+SPV_EXT_shader_atomic_float_min_max"
336-
",+SPV_KHR_no_integer_wrap_decoration,+SPV_KHR_float_controls"
337-
",+SPV_KHR_expect_assume,+SPV_KHR_linkonce_odr";
338-
std::string INTELExtArg =
339-
",+SPV_INTEL_subgroups,+SPV_INTEL_media_block_io"
340-
",+SPV_INTEL_device_side_avc_motion_estimation"
341-
",+SPV_INTEL_fpga_loop_controls,+SPV_INTEL_unstructured_loop_controls"
342-
",+SPV_INTEL_fpga_reg,+SPV_INTEL_blocking_pipes"
343-
",+SPV_INTEL_function_pointers,+SPV_INTEL_kernel_attributes"
344-
",+SPV_INTEL_io_pipes,+SPV_INTEL_inline_assembly"
345-
",+SPV_INTEL_arbitrary_precision_integers"
346-
",+SPV_INTEL_float_controls2,+SPV_INTEL_vector_compute"
347-
",+SPV_INTEL_fast_composite"
348-
",+SPV_INTEL_arbitrary_precision_fixed_point"
349-
",+SPV_INTEL_arbitrary_precision_floating_point"
350-
",+SPV_INTEL_variable_length_array,+SPV_INTEL_fp_fast_math_mode"
351-
",+SPV_INTEL_long_composites"
352-
",+SPV_INTEL_arithmetic_fence"
353-
",+SPV_INTEL_global_variable_decorations"
354-
",+SPV_INTEL_cache_controls"
355-
",+SPV_INTEL_fpga_buffer_location"
356-
",+SPV_INTEL_fpga_argument_interfaces"
357-
",+SPV_INTEL_fpga_invocation_pipelining_attributes"
358-
",+SPV_INTEL_fpga_latency_control"
359-
",+SPV_INTEL_task_sequence"
360-
",+SPV_KHR_shader_clock"
361-
",+SPV_INTEL_bindless_images";
362-
ExtArg = ExtArg + DefaultExtArg + INTELExtArg;
363-
ExtArg += ",+SPV_INTEL_token_type"
364-
",+SPV_INTEL_bfloat16_conversion"
365-
",+SPV_INTEL_joint_matrix"
366-
",+SPV_INTEL_hw_thread_queries"
367-
",+SPV_KHR_uniform_group_instructions"
368-
",+SPV_INTEL_masked_gather_scatter"
369-
",+SPV_INTEL_tensor_float32_conversion"
370-
",+SPV_INTEL_optnone"
371-
",+SPV_KHR_non_semantic_info"
372-
",+SPV_KHR_cooperative_matrix";
373-
TranslatorArgs.push_back(Args.MakeArgString(ExtArg));
374-
}
375-
376277
/// Run LLVM to SPIR-V translation.
377-
/// Converts 'File' from LLVM bitcode to SPIR-V format using llvm-spirv tool.
278+
/// Converts 'File' from LLVM bitcode to SPIR-V format using SPIR-V backend.
378279
/// 'Args' encompasses all arguments required for linking device code and will
379-
/// be parsed to generate options required to be passed into llvm-spirv tool.
380-
static Expected<StringRef> runLLVMToSPIRVTranslation(StringRef File,
381-
const ArgList &Args) {
382-
llvm::TimeTraceScope TimeScope("LLVMToSPIRVTranslation");
383-
StringRef LLVMSPIRVPath = Args.getLastArgValue(OPT_llvm_spirv_path_EQ);
384-
Expected<std::string> LLVMToSPIRVProg =
385-
findProgram(Args, "llvm-spirv", {LLVMSPIRVPath});
386-
if (!LLVMToSPIRVProg)
387-
return LLVMToSPIRVProg.takeError();
388-
389-
SmallVector<StringRef, 8> CmdArgs;
390-
CmdArgs.push_back(*LLVMToSPIRVProg);
391-
const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
392-
getSPIRVTransOpts(Args, CmdArgs, Triple);
393-
StringRef LLVMToSPIRVOptions;
394-
if (Arg *A = Args.getLastArg(OPT_llvm_spirv_options_EQ))
395-
LLVMToSPIRVOptions = A->getValue();
396-
LLVMToSPIRVOptions.split(CmdArgs, " ", /* MaxSplit = */ -1,
397-
/* KeepEmpty = */ false);
398-
CmdArgs.append({"-o", OutputFile});
399-
CmdArgs.push_back(File);
400-
if (Error Err = executeCommands(*LLVMToSPIRVProg, CmdArgs))
401-
return std::move(Err);
402-
403-
if (!SPIRVDumpDir.empty()) {
404-
std::error_code EC =
405-
llvm::sys::fs::create_directory(SPIRVDumpDir, /*IgnoreExisting*/ true);
406-
if (EC)
407-
return createStringError(
408-
EC,
409-
formatv("failed to create dump directory. path: {0}, error_code: {1}",
410-
SPIRVDumpDir, EC.value()));
411-
412-
StringRef Path = OutputFile;
413-
StringRef Filename = llvm::sys::path::filename(Path);
414-
SmallString<128> CopyPath = SPIRVDumpDir;
415-
CopyPath.append(Filename);
416-
EC = llvm::sys::fs::copy_file(Path, CopyPath);
417-
if (EC)
418-
return createStringError(
419-
EC,
420-
formatv(
421-
"failed to copy file. original: {0}, copy: {1}, error_code: {2}",
422-
Path, CopyPath, EC.value()));
423-
}
280+
/// 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+
llvm::TimeTraceScope TimeScope("SPIR-V code generation");
284+
285+
// Parse input module.
286+
SMDiagnostic Err;
287+
std::unique_ptr<Module> M = parseIRFile(File, Err, C);
288+
if (!M)
289+
return createStringError(Err.getMessage());
290+
291+
Triple TargetTriple(Args.getLastArgValue(OPT_triple_EQ));
292+
M->setTargetTriple(TargetTriple);
293+
294+
// Get a handle to SPIR-V target backend.
295+
std::string Msg;
296+
const Target *T = TargetRegistry::lookupTarget(M->getTargetTriple(), Msg);
297+
if (!T)
298+
return createStringError(Msg + ": " + M->getTargetTriple().str());
299+
300+
// Allocate SPIR-V target machine.
301+
TargetOptions Options;
302+
std::optional<Reloc::Model> RM;
303+
std::optional<CodeModel::Model> CM;
304+
std::unique_ptr<TargetMachine> TM(
305+
T->createTargetMachine(M->getTargetTriple(), /* CPU */ "",
306+
/* Features */ "", Options, RM, CM));
307+
if (!TM)
308+
return createStringError("Could not allocate target machine!");
309+
310+
// Set data layout if needed.
311+
if (M->getDataLayout().isDefault())
312+
M->setDataLayout(TM->createDataLayout());
313+
314+
// Open output file for writing.
315+
int FD = -1;
316+
if (std::error_code EC = sys::fs::openFileForWrite(OutputFile, FD))
317+
return errorCodeToError(EC);
318+
auto OS = std::make_unique<llvm::raw_fd_ostream>(FD, true);
319+
320+
// Run SPIR-V codegen passes to generate SPIR-V file.
321+
legacy::PassManager CodeGenPasses;
322+
TargetLibraryInfoImpl TLII(M->getTargetTriple());
323+
CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
324+
if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr,
325+
CodeGenFileType::ObjectFile))
326+
return createStringError("Failed to execute SPIR-V Backend");
327+
CodeGenPasses.run(*M);
328+
329+
if (Verbose)
330+
errs() << formatv("SPIR-V Backend: input: {0}, output: {1}\n", File,
331+
OutputFile);
424332

425333
return OutputFile;
426334
}
@@ -429,15 +337,17 @@ static Expected<StringRef> runLLVMToSPIRVTranslation(StringRef File,
429337
/// 1. Link input device code (user code and SYCL device library code).
430338
/// 2. Run SPIR-V code generation.
431339
Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
432-
llvm::TimeTraceScope TimeScope("SYCLDeviceLink");
340+
llvm::TimeTraceScope TimeScope("SYCL device linking");
341+
342+
LLVMContext C;
433343

434344
// Link all input bitcode files and SYCL device library files, if any.
435-
auto LinkedFile = linkDeviceCode(Files, Args);
345+
auto LinkedFile = linkDeviceCode(Files, Args, C);
436346
if (!LinkedFile)
437347
reportError(LinkedFile.takeError());
438348

439-
// LLVM to SPIR-V translation step
440-
auto SPVFile = runLLVMToSPIRVTranslation(*LinkedFile, Args);
349+
// SPIR-V code generation step.
350+
auto SPVFile = runSPIRVCodeGen(*LinkedFile, Args, C);
441351
if (!SPVFile)
442352
return SPVFile.takeError();
443353
return Error::success();
@@ -447,6 +357,11 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
447357

448358
int main(int argc, char **argv) {
449359
InitLLVM X(argc, argv);
360+
InitializeAllTargetInfos();
361+
InitializeAllTargets();
362+
InitializeAllTargetMCs();
363+
InitializeAllAsmParsers();
364+
InitializeAllAsmPrinters();
450365

451366
Executable = argv[0];
452367
sys::PrintStackTraceOnErrorSignal(argv[0]);

clang/tools/clang-sycl-linker/SYCLLinkOpts.td

+1-13
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def device_libs_EQ : CommaJoined<["--", "-"], "device-libs=">,
2727
def arch_EQ : Joined<["--", "-"], "arch=">,
2828
Flags<[LinkerOnlyOption]>,
2929
MetaVarName<"<arch>">,
30-
HelpText<"The device subarchitecture">;
30+
HelpText<"The device architecture">;
3131
def triple_EQ : Joined<["--", "-"], "triple=">,
3232
Flags<[LinkerOnlyOption]>,
3333
MetaVarName<"<triple>">,
@@ -43,18 +43,6 @@ def spirv_dump_device_code_EQ : Joined<["--", "-"], "spirv-dump-device-code=">,
4343
Flags<[LinkerOnlyOption]>,
4444
HelpText<"Path to the folder where the tool dumps SPIR-V device code. Other formats aren't dumped.">;
4545

46-
def is_windows_msvc_env : Flag<["--", "-"], "is-windows-msvc-env">,
47-
Flags<[LinkerOnlyOption, HelpHidden]>;
48-
49-
def llvm_spirv_path_EQ : Joined<["--"], "llvm-spirv-path=">,
50-
Flags<[LinkerOnlyOption]>, MetaVarName<"<dir>">,
51-
HelpText<"Set the system llvm-spirv path">;
52-
53-
// Options to pass to llvm-spirv tool
54-
def llvm_spirv_options_EQ : Joined<["--", "-"], "llvm-spirv-options=">,
55-
Flags<[LinkerOnlyOption]>,
56-
HelpText<"Options that will control llvm-spirv step">;
57-
5846
def print_linked_module : Flag<["--"], "print-linked-module">,
5947
Flags<[LinkerOnlyOption]>,
6048
HelpText<"Print the linked module's IR for testing">;

0 commit comments

Comments
 (0)