Skip to content

Commit 93169a7

Browse files
committed
Use simplified copy of SYCL::getDeviceLibraries
Signed-off-by: Julian Oppermann <[email protected]>
1 parent 91d6baa commit 93169a7

File tree

4 files changed

+218
-77
lines changed

4 files changed

+218
-77
lines changed

sycl-jit/jit-compiler/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_llvm_library(sycl-jit
1919
BitReader
2020
Core
2121
Support
22+
Option
2223
Analysis
2324
IPO
2425
TransformUtils

sycl-jit/jit-compiler/lib/KernelFusion.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,14 @@ extern "C" JITResult fuseKernels(View<SYCLKernelInfo> KernelInformation,
239239
extern "C" JITResult compileSYCL(InMemoryFile SourceFile,
240240
View<InMemoryFile> IncludeFiles,
241241
View<const char *> UserArgs) {
242-
auto ModuleOrErr = compileDeviceCode(SourceFile, IncludeFiles, UserArgs);
242+
auto UserArgListOrErr = parseUserArgs(UserArgs);
243+
if (!UserArgListOrErr) {
244+
return errorToFusionResult(UserArgListOrErr.takeError(),
245+
"Parsing of user arguments failed");
246+
}
247+
llvm::opt::InputArgList UserArgList = std::move(*UserArgListOrErr);
248+
249+
auto ModuleOrErr = compileDeviceCode(SourceFile, IncludeFiles, UserArgList);
243250
if (!ModuleOrErr) {
244251
return errorToFusionResult(ModuleOrErr.takeError(),
245252
"Device compilation failed");
@@ -249,7 +256,7 @@ extern "C" JITResult compileSYCL(InMemoryFile SourceFile,
249256
std::unique_ptr<llvm::Module> Module = std::move(*ModuleOrErr);
250257
Context.reset(&Module->getContext());
251258

252-
if (auto Error = linkDefaultDeviceLibraries(*Module, UserArgs)) {
259+
if (auto Error = linkDeviceLibraries(*Module, UserArgList)) {
253260
return errorToFusionResult(std::move(Error), "Device linking failed");
254261
}
255262

sycl-jit/jit-compiler/lib/rtc/DeviceCompilation.cpp

Lines changed: 201 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88

99
#include "DeviceCompilation.h"
1010

11+
#include <clang/Basic/DiagnosticDriver.h>
1112
#include <clang/Basic/Version.h>
1213
#include <clang/CodeGen/CodeGenAction.h>
1314
#include <clang/Driver/Compilation.h>
15+
#include <clang/Driver/Options.h>
1416
#include <clang/Frontend/CompilerInstance.h>
17+
#include <clang/Frontend/TextDiagnosticBuffer.h>
1518
#include <clang/Tooling/CompilationDatabase.h>
1619
#include <clang/Tooling/Tooling.h>
1720

@@ -23,7 +26,9 @@
2326
using namespace clang;
2427
using namespace clang::tooling;
2528
using namespace clang::driver;
29+
using namespace clang::driver::options;
2630
using namespace llvm;
31+
using namespace llvm::opt;
2732

2833
#ifdef _GNU_SOURCE
2934
#include <dlfcn.h>
@@ -106,16 +111,33 @@ struct GetLLVMModuleAction : public ToolAction {
106111
Expected<std::unique_ptr<llvm::Module>>
107112
jit_compiler::compileDeviceCode(InMemoryFile SourceFile,
108113
View<InMemoryFile> IncludeFiles,
109-
View<const char *> UserArgs) {
114+
const InputArgList &UserArgList) {
110115
const std::string &DPCPPRoot = getDPCPPRoot();
111116
if (DPCPPRoot == InvalidDPCPPRoot) {
112117
return createStringError("Could not locate DPCPP root directory");
113118
}
114119

115-
SmallVector<std::string> CommandLine = {"-fsycl-device-only"};
116-
CommandLine.append(UserArgs.begin(), UserArgs.end());
117-
FixedCompilationDatabase DB{".", CommandLine};
120+
DerivedArgList DAL{UserArgList};
121+
const auto &OptTable = getDriverOptTable();
122+
DAL.AddFlagArg(nullptr, OptTable.getOption(OPT_fsycl_device_only));
123+
DAL.AddJoinedArg(
124+
nullptr, OptTable.getOption(OPT_resource_dir_EQ),
125+
(DPCPPRoot + "/lib/clang/" + Twine(CLANG_VERSION_MAJOR)).str());
126+
for (auto *Arg : UserArgList) {
127+
DAL.append(Arg);
128+
}
129+
// Remove args that will trigger an unused command line argument warning for
130+
// the FrontendAction invocation, but are handled later (e.g. during device
131+
// linking).
132+
DAL.eraseArg(OPT_fsycl_device_lib_EQ);
133+
DAL.eraseArg(OPT_fno_sycl_device_lib_EQ);
134+
135+
SmallVector<std::string> CommandLine;
136+
for (auto *Arg : DAL) {
137+
CommandLine.emplace_back(Arg->getAsString(DAL));
138+
}
118139

140+
FixedCompilationDatabase DB{".", CommandLine};
119141
ClangTool Tool{DB, {SourceFile.Path}};
120142

121143
// Set up in-memory filesystem.
@@ -127,17 +149,14 @@ jit_compiler::compileDeviceCode(InMemoryFile SourceFile,
127149
// Reset argument adjusters to drop the `-fsyntax-only` flag which is added by
128150
// default by this API.
129151
Tool.clearArgumentsAdjusters();
130-
// Then, modify argv[0] and set the resource directory so that the driver
131-
// picks up the correct SYCL environment.
152+
// Then, modify argv[0] so that the driver picks up the correct SYCL
153+
// environment. We've already set the resource directory above.
132154
Tool.appendArgumentsAdjuster(
133155
[&DPCPPRoot](const CommandLineArguments &Args,
134156
StringRef Filename) -> CommandLineArguments {
135157
(void)Filename;
136158
CommandLineArguments NewArgs = Args;
137159
NewArgs[0] = (Twine(DPCPPRoot) + "/bin/clang++").str();
138-
NewArgs.push_back((Twine("-resource-dir=") + DPCPPRoot + "/lib/clang/" +
139-
Twine(CLANG_VERSION_MAJOR))
140-
.str());
141160
return NewArgs;
142161
});
143162

@@ -150,87 +169,197 @@ jit_compiler::compileDeviceCode(InMemoryFile SourceFile,
150169
return createStringError("Unable to obtain LLVM module");
151170
}
152171

153-
Error jit_compiler::linkDefaultDeviceLibraries(llvm::Module &Module,
154-
View<const char *> UserArgs) {
155-
// This function mimics the device library selection process
156-
// `clang::driver::tools::SYCL::getDeviceLibraries`, assuming a SPIR-V target
157-
// (no AoT, no third-party GPUs, no native CPU).
172+
// This function is a simplified copy of the device library selection process in
173+
// `clang::driver::tools::SYCL::getDeviceLibraries`, assuming a SPIR-V target
174+
// (no AoT, no third-party GPUs, no native CPU). Keep in sync!
175+
static SmallVector<std::string, 8>
176+
getDeviceLibraries(const ArgList &Args, DiagnosticsEngine &Diags) {
177+
struct DeviceLibOptInfo {
178+
StringRef DeviceLibName;
179+
StringRef DeviceLibOption;
180+
};
181+
182+
// Currently, all SYCL device libraries will be linked by default.
183+
llvm::StringMap<bool> DeviceLibLinkInfo = {
184+
{"libc", true}, {"libm-fp32", true}, {"libm-fp64", true},
185+
{"libimf-fp32", true}, {"libimf-fp64", true}, {"libimf-bf16", true},
186+
{"libm-bfloat16", true}, {"internal", true}};
187+
188+
// If -fno-sycl-device-lib is specified, its values will be used to exclude
189+
// linkage of libraries specified by DeviceLibLinkInfo. Linkage of "internal"
190+
// libraries cannot be affected via -fno-sycl-device-lib.
191+
bool ExcludeDeviceLibs = false;
192+
193+
if (Arg *A = Args.getLastArg(OPT_fsycl_device_lib_EQ,
194+
OPT_fno_sycl_device_lib_EQ)) {
195+
if (A->getValues().size() == 0) {
196+
Diags.Report(diag::warn_drv_empty_joined_argument)
197+
<< A->getAsString(Args);
198+
} else {
199+
if (A->getOption().matches(OPT_fno_sycl_device_lib_EQ)) {
200+
ExcludeDeviceLibs = true;
201+
}
202+
203+
for (StringRef Val : A->getValues()) {
204+
if (Val == "all") {
205+
for (const auto &K : DeviceLibLinkInfo.keys()) {
206+
DeviceLibLinkInfo[K] = (K == "internal") || !ExcludeDeviceLibs;
207+
}
208+
break;
209+
}
210+
auto LinkInfoIter = DeviceLibLinkInfo.find(Val);
211+
if (LinkInfoIter == DeviceLibLinkInfo.end() || Val == "internal") {
212+
Diags.Report(diag::err_drv_unsupported_option_argument)
213+
<< A->getSpelling() << Val;
214+
return {};
215+
}
216+
DeviceLibLinkInfo[Val] = !ExcludeDeviceLibs;
217+
}
218+
}
219+
}
220+
221+
using SYCLDeviceLibsList = SmallVector<DeviceLibOptInfo, 5>;
222+
223+
const SYCLDeviceLibsList SYCLDeviceWrapperLibs = {
224+
{"libsycl-crt", "libc"},
225+
{"libsycl-complex", "libm-fp32"},
226+
{"libsycl-complex-fp64", "libm-fp64"},
227+
{"libsycl-cmath", "libm-fp32"},
228+
{"libsycl-cmath-fp64", "libm-fp64"},
229+
{"libsycl-imf", "libimf-fp32"},
230+
{"libsycl-imf-fp64", "libimf-fp64"},
231+
{"libsycl-imf-bf16", "libimf-bf16"}};
232+
// ITT annotation libraries are linked in separately whenever the device
233+
// code instrumentation is enabled.
234+
const SYCLDeviceLibsList SYCLDeviceAnnotationLibs = {
235+
{"libsycl-itt-user-wrappers", "internal"},
236+
{"libsycl-itt-compiler-wrappers", "internal"},
237+
{"libsycl-itt-stubs", "internal"}};
238+
const SYCLDeviceLibsList SYCLDeviceSanitizerLibs = {
239+
{"libsycl-sanitizer", "internal"}};
240+
241+
SmallVector<std::string, 8> LibraryList;
242+
StringRef LibSuffix = ".bc";
243+
auto AddLibraries = [&](const SYCLDeviceLibsList &LibsList) {
244+
for (const DeviceLibOptInfo &Lib : LibsList) {
245+
if (!DeviceLibLinkInfo[Lib.DeviceLibOption]) {
246+
continue;
247+
}
248+
SmallString<128> LibName(Lib.DeviceLibName);
249+
llvm::sys::path::replace_extension(LibName, LibSuffix);
250+
LibraryList.push_back(Args.MakeArgString(LibName));
251+
}
252+
};
253+
254+
AddLibraries(SYCLDeviceWrapperLibs);
255+
256+
if (Args.hasFlag(OPT_fsycl_instrument_device_code,
257+
OPT_fno_sycl_instrument_device_code, false)) {
258+
AddLibraries(SYCLDeviceAnnotationLibs);
259+
}
158260

159-
bool DeviceInstrumentationEnabled = false;
160-
for (StringRef UA : UserArgs) {
161-
// Check instrumentation-related flags (last occurence determines outcome).
162-
if (UA == "-fsycl-instrument-device-code") {
163-
DeviceInstrumentationEnabled = true;
164-
continue;
261+
if (Arg *A = Args.getLastArg(OPT_fsanitize_EQ, OPT_fno_sanitize_EQ)) {
262+
if (A->getOption().matches(OPT_fsanitize_EQ) &&
263+
A->getValues().size() == 1) {
264+
std::string SanitizeVal = A->getValue();
265+
if (SanitizeVal == "address") {
266+
AddLibraries(SYCLDeviceSanitizerLibs);
267+
}
165268
}
166-
if (UA == "-fno-sycl-instrument-device-code") {
167-
DeviceInstrumentationEnabled = false;
168-
continue;
269+
} else {
270+
// User can pass -fsanitize=address to device compiler via
271+
// -Xsycl-target-frontend, sanitize device library must be
272+
// linked with user's device image if so.
273+
bool IsDeviceAsanEnabled = false;
274+
auto SyclFEArg = Args.getAllArgValues(OPT_Xsycl_frontend);
275+
IsDeviceAsanEnabled = (std::count(SyclFEArg.begin(), SyclFEArg.end(),
276+
"-fsanitize=address") > 0);
277+
if (!IsDeviceAsanEnabled) {
278+
auto SyclFEArgEq = Args.getAllArgValues(OPT_Xsycl_frontend_EQ);
279+
IsDeviceAsanEnabled = (std::count(SyclFEArgEq.begin(), SyclFEArgEq.end(),
280+
"-fsanitize=address") > 0);
169281
}
170282

171-
// Issue warning for `-fsycl-device-lib` or `-fno-sycl-device-lib`.
172-
// TODO: Is it worth supporting these flags? We're using `LinkOnlyNeeded`
173-
// mode anyways!
174-
// TODO: If we keep the warning, it must go into the build log, not onto the
175-
// console.
176-
// TODO: The FrontendAction emits a warning that these flags are unused. We
177-
// should probably silence that by removing the argument occurence for
178-
// the compilation step.
179-
if (UA.contains("sycl-device-lib")) {
180-
errs() << "warning: device library selection with '" << UA
181-
<< "' is ignored\n";
283+
// User can also enable asan for SYCL device via -Xarch_device option.
284+
if (!IsDeviceAsanEnabled) {
285+
auto DeviceArchVals = Args.getAllArgValues(OPT_Xarch_device);
286+
for (auto DArchVal : DeviceArchVals) {
287+
if (DArchVal.find("-fsanitize=address") != std::string::npos) {
288+
IsDeviceAsanEnabled = true;
289+
break;
290+
}
291+
}
182292
}
183293

184-
// TODO: Presence of `-fsanitize=address` would require linking
185-
// `libsycl-sanitizer`, but currently compilation fails earlier.
186-
assert(!UA.contains("-fsanitize=address") && "Device ASAN unsupported");
294+
if (IsDeviceAsanEnabled) {
295+
AddLibraries(SYCLDeviceSanitizerLibs);
296+
}
187297
}
188298

299+
return LibraryList;
300+
}
301+
302+
Error jit_compiler::linkDeviceLibraries(llvm::Module &Module,
303+
const InputArgList &UserArgList) {
304+
// This function mimics the device library selection process
305+
// `clang::driver::tools::SYCL::getDeviceLibraries`, assuming a SPIR-V target
306+
// (no AoT, no third-party GPUs, no native CPU).
307+
189308
const std::string &DPCPPRoot = getDPCPPRoot();
190309
if (DPCPPRoot == InvalidDPCPPRoot) {
191310
return createStringError("Could not locate DPCPP root directory");
192311
}
193312

194-
constexpr std::array<llvm::StringLiteral, 8> SYCLDeviceWrapperLibs = {
195-
"libsycl-crt", "libsycl-complex", "libsycl-complex-fp64",
196-
"libsycl-cmath", "libsycl-cmath-fp64", "libsycl-imf",
197-
"libsycl-imf-fp64", "libsycl-imf-bf16"};
198-
199-
constexpr std::array<llvm::StringLiteral, 3> SYCLDeviceAnnotationLibs = {
200-
"libsycl-itt-user-wrappers", "libsycl-itt-compiler-wrappers",
201-
"libsycl-itt-stubs"};
313+
// TODO: Seems a bit excessive to set up this machinery for one warning and
314+
// one error. Rethink when implementing the build log/error reporting as
315+
// mandated by the extension.
316+
IntrusiveRefCntPtr<DiagnosticIDs> DiagID{new DiagnosticIDs};
317+
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts{new DiagnosticOptions};
318+
TextDiagnosticBuffer *DiagBuffer = new TextDiagnosticBuffer;
319+
DiagnosticsEngine Diags(DiagID, DiagOpts, DiagBuffer);
320+
321+
auto LibNames = getDeviceLibraries(UserArgList, Diags);
322+
if (LibNames.empty()) {
323+
assert(std::distance(DiagBuffer->err_begin(), DiagBuffer->err_end()) == 1);
324+
return createStringError("Could not determine list of device libraries: %s",
325+
DiagBuffer->err_begin()->second.c_str());
326+
}
327+
// TODO: Add warnings to build log.
202328

203329
LLVMContext &Context = Module.getContext();
204-
auto Link = [&](ArrayRef<llvm::StringLiteral> LibNames) -> Error {
205-
for (const auto &LibName : LibNames) {
206-
std::string LibPath = (DPCPPRoot + "/lib/" + LibName + ".bc").str();
207-
208-
SMDiagnostic Diag;
209-
std::unique_ptr<llvm::Module> Lib = parseIRFile(LibPath, Diag, Context);
210-
if (!Lib) {
211-
std::string DiagMsg;
212-
raw_string_ostream SOS(DiagMsg);
213-
Diag.print(/*ProgName=*/nullptr, SOS);
214-
return createStringError(DiagMsg);
215-
}
216-
217-
if (Linker::linkModules(Module, std::move(Lib), Linker::LinkOnlyNeeded)) {
218-
// TODO: `linkModules` always prints errors to the console.
219-
return createStringError("Unable to link device library: %s",
220-
LibPath.c_str());
221-
}
330+
for (const std::string &LibName : LibNames) {
331+
std::string LibPath = DPCPPRoot + "/lib/" + LibName;
332+
333+
SMDiagnostic Diag;
334+
std::unique_ptr<llvm::Module> Lib = parseIRFile(LibPath, Diag, Context);
335+
if (!Lib) {
336+
std::string DiagMsg;
337+
raw_string_ostream SOS(DiagMsg);
338+
Diag.print(/*ProgName=*/nullptr, SOS);
339+
return createStringError(DiagMsg);
222340
}
223341

224-
return Error::success();
225-
};
226-
227-
if (auto Error = Link(SYCLDeviceWrapperLibs)) {
228-
return Error;
229-
}
230-
if (DeviceInstrumentationEnabled) {
231-
if (auto Error = Link(SYCLDeviceAnnotationLibs)) {
232-
return Error;
342+
if (Linker::linkModules(Module, std::move(Lib), Linker::LinkOnlyNeeded)) {
343+
// TODO: Obtain detailed error message from the context's diagnostics
344+
// handler.
345+
return createStringError("Unable to link device library: %s",
346+
LibPath.c_str());
233347
}
234348
}
349+
235350
return Error::success();
236351
}
352+
353+
Expected<InputArgList>
354+
jit_compiler::parseUserArgs(View<const char *> UserArgs) {
355+
unsigned MissingArgIndex, MissingArgCount;
356+
auto UserArgsRef = UserArgs.to<ArrayRef>();
357+
auto AL = getDriverOptTable().ParseArgs(UserArgsRef, MissingArgIndex,
358+
MissingArgCount);
359+
if (MissingArgCount) {
360+
return createStringError(
361+
"User option '%s' at index %d is missing an argument",
362+
UserArgsRef[MissingArgIndex], MissingArgIndex);
363+
}
364+
return AL;
365+
}

0 commit comments

Comments
 (0)