Skip to content

Commit dcd5e47

Browse files
authored
[clang-sycl-linker] Add AOT compilation support for Intel GPUs/CPUs (#133194)
This PR adds support for AOT compilation for Intel CPUs and GPUs in clang-sycl-linker. When no `-arch` is passed to `clang-sycl-linker`, the output of the tool will be the resulting linked SPIR-V bytecode. If the `-arch` is passed to `clang-sycl-linker` and the value is a supported Intel CPU or GPU, then SPIR-V bytecode is then further passed to the respective tool (`opencl-aot` or `ocloc`) for AOT compilation.
1 parent 3cf280c commit dcd5e47

File tree

3 files changed

+185
-11
lines changed

3 files changed

+185
-11
lines changed

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//
2121
// Test a simple case with a random file (not bitcode) as input.
2222
// RUN: touch %t.o
23-
// RUN: not clang-sycl-linker -triple spirv64 %t.o -o a.spv 2>&1 \
23+
// RUN: not clang-sycl-linker -triple=spirv64 %t.o -o a.spv 2>&1 \
2424
// RUN: | FileCheck %s --check-prefix=FILETYPEERROR
2525
// FILETYPEERROR: Unsupported file type
2626
//
@@ -31,3 +31,29 @@
3131
// 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 \
3232
// RUN: | FileCheck %s --check-prefix=DEVLIBSERR2
3333
// DEVLIBSERR2: '{{.*}}lib3.bc' SYCL device library file is not found
34+
//
35+
// Test AOT compilation for an Intel GPU.
36+
// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 -arch=bmg_g21 %t_1.bc %t_2.bc -o a.out 2>&1 \
37+
// RUN: --ocloc-options="-a -b" \
38+
// RUN: | FileCheck %s --check-prefix=AOT-INTEL-GPU
39+
// AOT-INTEL-GPU: sycl-device-link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc
40+
// AOT-INTEL-GPU-NEXT: SPIR-V Backend: input: [[LLVMLINKOUT]].bc, output: [[SPIRVTRANSLATIONOUT:.*]]_0.spv
41+
// AOT-INTEL-GPU-NEXT: "{{.*}}ocloc{{.*}}" {{.*}}-device bmg_g21 -a -b {{.*}}-output a_0.out -file [[SPIRVTRANSLATIONOUT]]_0.spv
42+
//
43+
// Test AOT compilation for an Intel CPU.
44+
// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 -arch=graniterapids %t_1.bc %t_2.bc -o a.out 2>&1 \
45+
// RUN: --opencl-aot-options="-a -b" \
46+
// RUN: | FileCheck %s --check-prefix=AOT-INTEL-CPU
47+
// AOT-INTEL-CPU: sycl-device-link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc
48+
// AOT-INTEL-CPU-NEXT: SPIR-V Backend: input: [[LLVMLINKOUT]].bc, output: [[SPIRVTRANSLATIONOUT:.*]]_0.spv
49+
// AOT-INTEL-CPU-NEXT: "{{.*}}opencl-aot{{.*}}" {{.*}}--device=cpu -a -b {{.*}}-o a_0.out [[SPIRVTRANSLATIONOUT]]_0.spv
50+
//
51+
// Check that the output file must be specified.
52+
// RUN: not clang-sycl-linker --dry-run %t_1.bc %t_2.bc 2>& 1 \
53+
// RUN: | FileCheck %s --check-prefix=NOOUTPUT
54+
// NOOUTPUT: Output file must be specified
55+
//
56+
// Check that the target triple must be.
57+
// RUN: not clang-sycl-linker --dry-run %t_1.bc %t_2.bc -o a.out 2>& 1 \
58+
// RUN: | FileCheck %s --check-prefix=NOTARGET
59+
// NOTARGET: Target triple must be specified

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

Lines changed: 150 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// target-specific device code.
1515
//===---------------------------------------------------------------------===//
1616

17+
#include "clang/Basic/OffloadArch.h"
1718
#include "clang/Basic/Version.h"
1819

1920
#include "llvm/ADT/StringExtras.h"
@@ -54,6 +55,7 @@
5455
using namespace llvm;
5556
using namespace llvm::opt;
5657
using namespace llvm::object;
58+
using namespace clang;
5759

5860
/// Save intermediary results.
5961
static bool SaveTemps = false;
@@ -128,6 +130,12 @@ const OptTable &getOptTable() {
128130
exit(EXIT_FAILURE);
129131
}
130132

133+
std::string getMainExecutable(const char *Name) {
134+
void *Ptr = (void *)(intptr_t)&getMainExecutable;
135+
auto COWPath = sys::fs::getMainExecutable(Name, Ptr);
136+
return sys::path::parent_path(COWPath).str();
137+
}
138+
131139
Expected<StringRef> createTempFile(const ArgList &Args, const Twine &Prefix,
132140
StringRef Extension) {
133141
SmallString<128> OutputFile;
@@ -145,6 +153,40 @@ Expected<StringRef> createTempFile(const ArgList &Args, const Twine &Prefix,
145153
return TempFiles.back();
146154
}
147155

156+
Expected<std::string> findProgram(const ArgList &Args, StringRef Name,
157+
ArrayRef<StringRef> Paths) {
158+
if (Args.hasArg(OPT_dry_run))
159+
return Name.str();
160+
ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);
161+
if (!Path)
162+
Path = sys::findProgramByName(Name);
163+
if (!Path)
164+
return createStringError(Path.getError(),
165+
"Unable to find '" + Name + "' in path");
166+
return *Path;
167+
}
168+
169+
void printCommands(ArrayRef<StringRef> CmdArgs) {
170+
if (CmdArgs.empty())
171+
return;
172+
173+
llvm::errs() << " \"" << CmdArgs.front() << "\" ";
174+
llvm::errs() << llvm::join(std::next(CmdArgs.begin()), CmdArgs.end(), " ")
175+
<< "\n";
176+
}
177+
178+
/// Execute the command \p ExecutablePath with the arguments \p Args.
179+
Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) {
180+
if (Verbose || DryRun)
181+
printCommands(Args);
182+
183+
if (!DryRun)
184+
if (sys::ExecuteAndWait(ExecutablePath, Args))
185+
return createStringError(
186+
"'%s' failed", sys::path::filename(ExecutablePath).str().c_str());
187+
return Error::success();
188+
}
189+
148190
Expected<SmallVector<std::string>> getInput(const ArgList &Args) {
149191
// Collect all input bitcode files to be passed to the device linking stage.
150192
SmallVector<std::string> BitcodeFiles;
@@ -338,6 +380,92 @@ static Error runSPIRVCodeGen(StringRef File, const ArgList &Args,
338380
return Error::success();
339381
}
340382

383+
/// Run AOT compilation for Intel CPU.
384+
/// Calls opencl-aot tool to generate device code for the Intel OpenCL CPU
385+
/// Runtime.
386+
/// \param InputFile The input SPIR-V file.
387+
/// \param OutputFile The output file name.
388+
/// \param Args Encompasses all arguments required for linking and wrapping
389+
/// device code and will be parsed to generate options required to be passed
390+
/// into the SYCL AOT compilation step.
391+
static Error runAOTCompileIntelCPU(StringRef InputFile, StringRef OutputFile,
392+
const ArgList &Args) {
393+
SmallVector<StringRef, 8> CmdArgs;
394+
Expected<std::string> OpenCLAOTPath =
395+
findProgram(Args, "opencl-aot", {getMainExecutable("opencl-aot")});
396+
if (!OpenCLAOTPath)
397+
return OpenCLAOTPath.takeError();
398+
399+
CmdArgs.push_back(*OpenCLAOTPath);
400+
CmdArgs.push_back("--device=cpu");
401+
StringRef ExtraArgs = Args.getLastArgValue(OPT_opencl_aot_options_EQ);
402+
ExtraArgs.split(CmdArgs, " ", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
403+
CmdArgs.push_back("-o");
404+
CmdArgs.push_back(OutputFile);
405+
CmdArgs.push_back(InputFile);
406+
if (Error Err = executeCommands(*OpenCLAOTPath, CmdArgs))
407+
return Err;
408+
return Error::success();
409+
}
410+
411+
/// Run AOT compilation for Intel GPU.
412+
/// Calls ocloc tool to generate device code for the Intel Graphics Compute
413+
/// Runtime.
414+
/// \param InputFile The input SPIR-V file.
415+
/// \param OutputFile The output file name.
416+
/// \param Args Encompasses all arguments required for linking and wrapping
417+
/// device code and will be parsed to generate options required to be passed
418+
/// into the SYCL AOT compilation step.
419+
static Error runAOTCompileIntelGPU(StringRef InputFile, StringRef OutputFile,
420+
const ArgList &Args) {
421+
SmallVector<StringRef, 8> CmdArgs;
422+
Expected<std::string> OclocPath =
423+
findProgram(Args, "ocloc", {getMainExecutable("ocloc")});
424+
if (!OclocPath)
425+
return OclocPath.takeError();
426+
427+
CmdArgs.push_back(*OclocPath);
428+
// The next line prevents ocloc from modifying the image name
429+
CmdArgs.push_back("-output_no_suffix");
430+
CmdArgs.push_back("-spirv_input");
431+
432+
StringRef Arch(Args.getLastArgValue(OPT_arch_EQ));
433+
if (Arch.empty())
434+
return createStringError(inconvertibleErrorCode(),
435+
"Arch must be specified for AOT compilation");
436+
CmdArgs.push_back("-device");
437+
CmdArgs.push_back(Arch);
438+
439+
StringRef ExtraArgs = Args.getLastArgValue(OPT_ocloc_options_EQ);
440+
ExtraArgs.split(CmdArgs, " ", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
441+
442+
CmdArgs.push_back("-output");
443+
CmdArgs.push_back(OutputFile);
444+
CmdArgs.push_back("-file");
445+
CmdArgs.push_back(InputFile);
446+
if (Error Err = executeCommands(*OclocPath, CmdArgs))
447+
return Err;
448+
return Error::success();
449+
}
450+
451+
/// Run AOT compilation for Intel CPU/GPU.
452+
/// \param InputFile The input SPIR-V file.
453+
/// \param OutputFile The output file name.
454+
/// \param Args Encompasses all arguments required for linking and wrapping
455+
/// device code and will be parsed to generate options required to be passed
456+
/// into the SYCL AOT compilation step.
457+
static Error runAOTCompile(StringRef InputFile, StringRef OutputFile,
458+
const ArgList &Args) {
459+
StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
460+
OffloadArch OffloadArch = StringToOffloadArch(Arch);
461+
if (IsIntelGPUOffloadArch(OffloadArch))
462+
return runAOTCompileIntelGPU(InputFile, OutputFile, Args);
463+
if (IsIntelCPUOffloadArch(OffloadArch))
464+
return runAOTCompileIntelCPU(InputFile, OutputFile, Args);
465+
466+
return createStringError(inconvertibleErrorCode(), "Unsupported arch");
467+
}
468+
341469
/// Performs the following steps:
342470
/// 1. Link input device code (user code and SYCL device library code).
343471
/// 2. Run SPIR-V code generation.
@@ -349,7 +477,7 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
349477
// Link all input bitcode files and SYCL device library files, if any.
350478
auto LinkedFile = linkDeviceCode(Files, Args, C);
351479
if (!LinkedFile)
352-
reportError(LinkedFile.takeError());
480+
return LinkedFile.takeError();
353481

354482
// TODO: SYCL post link functionality involves device code splitting and will
355483
// result in multiple bitcode codes.
@@ -358,15 +486,24 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
358486
SmallVector<std::string> SplitModules;
359487
SplitModules.emplace_back(*LinkedFile);
360488

489+
bool IsAOTCompileNeeded = IsIntelOffloadArch(
490+
StringToOffloadArch(Args.getLastArgValue(OPT_arch_EQ)));
491+
361492
// SPIR-V code generation step.
362493
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)
494+
StringRef Stem = OutputFile.rsplit('.').first;
495+
std::string SPVFile = (Stem + "_" + Twine(I) + ".spv").str();
496+
if (Error Err = runSPIRVCodeGen(SplitModules[I], Args, SPVFile, C))
368497
return Err;
369-
SplitModules[I] = SPVFile;
498+
if (!IsAOTCompileNeeded) {
499+
SplitModules[I] = SPVFile;
500+
} else {
501+
// AOT compilation step.
502+
std::string AOTFile = (Stem + "_" + Twine(I) + ".out").str();
503+
if (Error Err = runAOTCompile(SPVFile, AOTFile, Args))
504+
return Err;
505+
SplitModules[I] = AOTFile;
506+
}
370507
}
371508

372509
// Write the final output into file.
@@ -440,9 +577,12 @@ int main(int argc, char **argv) {
440577
DryRun = Args.hasArg(OPT_dry_run);
441578
SaveTemps = Args.hasArg(OPT_save_temps);
442579

443-
OutputFile = "a.out";
444-
if (Args.hasArg(OPT_o))
445-
OutputFile = Args.getLastArgValue(OPT_o);
580+
if (!Args.hasArg(OPT_o))
581+
reportError(createStringError("Output file must be specified"));
582+
OutputFile = Args.getLastArgValue(OPT_o);
583+
584+
if (!Args.hasArg(OPT_triple_EQ))
585+
reportError(createStringError("Target triple must be specified"));
446586

447587
if (Args.hasArg(OPT_spirv_dump_device_code_EQ)) {
448588
Arg *A = Args.getLastArg(OPT_spirv_dump_device_code_EQ);

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,11 @@ def spirv_dump_device_code_EQ : Joined<["--", "-"], "spirv-dump-device-code=">,
4646
def print_linked_module : Flag<["--"], "print-linked-module">,
4747
Flags<[LinkerOnlyOption]>,
4848
HelpText<"Print the linked module's IR for testing">;
49+
50+
def ocloc_options_EQ : Joined<["--", "-"], "ocloc-options=">,
51+
Flags<[LinkerOnlyOption]>,
52+
HelpText<"Options passed to ocloc for Intel GPU AOT compilation">;
53+
54+
def opencl_aot_options_EQ : Joined<["--", "-"], "opencl-aot-options=">,
55+
Flags<[LinkerOnlyOption]>,
56+
HelpText<"Options passed to opencl-aot for Intel CPU AOT compilation">;

0 commit comments

Comments
 (0)