Skip to content

Commit a726365

Browse files
committed
WIP: Symbols and properties available in jit_compiler (runtime)
Signed-off-by: Julian Oppermann <[email protected]>
1 parent 01f7e44 commit a726365

File tree

11 files changed

+413
-18
lines changed

11 files changed

+413
-18
lines changed

sycl-jit/common/include/Kernel.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <cstdint>
1818
#include <cstring>
1919
#include <functional>
20+
#include <string_view>
2021
#include <type_traits>
2122

2223
namespace jit_compiler {
@@ -350,11 +351,60 @@ struct SYCLKernelInfo {
350351
: Name{KernelName}, Args{NumArgs}, Attributes{}, NDR{}, BinaryInfo{} {}
351352
};
352353

354+
// RTC-related datastructures
355+
// TODO: Consider moving into separate header.
356+
353357
struct InMemoryFile {
354358
const char *Path;
355359
const char *Contents;
356360
};
357361

362+
using RTCBundleBinaryInfo = SYCLKernelBinaryInfo;
363+
using FrozenSymbolTable = DynArray<sycl::detail::string>;
364+
365+
// Note: `FrozenPropertyValue` and `FrozenPropertySet` constructors take
366+
// `std::string_view` arguments instead of `const char *` because they will be
367+
// created from `llvm::SmallString`s, which don't contain the trailing '\0'
368+
// byte. Hence obtaining a C-string would cause an additional copy.
369+
370+
struct FrozenPropertyValue {
371+
sycl::detail::string Name;
372+
bool IsUIntValue;
373+
uint32_t UIntValue;
374+
DynArray<uint8_t> Bytes;
375+
376+
FrozenPropertyValue() = default;
377+
FrozenPropertyValue(FrozenPropertyValue &&) = default;
378+
FrozenPropertyValue &operator=(FrozenPropertyValue &&) = default;
379+
380+
FrozenPropertyValue(std::string_view Name, uint32_t Value)
381+
: Name{Name}, IsUIntValue{true}, UIntValue{Value}, Bytes{0} {}
382+
FrozenPropertyValue(std::string_view Name, const uint8_t *Ptr, size_t Size)
383+
: Name{Name}, IsUIntValue{false}, Bytes{Size} {
384+
std::memcpy(Bytes.begin(), Ptr, Size);
385+
}
386+
};
387+
388+
struct FrozenPropertySet {
389+
sycl::detail::string Name;
390+
DynArray<FrozenPropertyValue> Values;
391+
392+
FrozenPropertySet() = default;
393+
FrozenPropertySet(FrozenPropertySet &&) = default;
394+
FrozenPropertySet &operator=(FrozenPropertySet &&) = default;
395+
396+
FrozenPropertySet(std::string_view Name, size_t Size)
397+
: Name{Name}, Values{Size} {}
398+
};
399+
400+
using FrozenPropertyRegistry = DynArray<FrozenPropertySet>;
401+
402+
struct RTCBundleInfo {
403+
RTCBundleBinaryInfo BinaryInfo;
404+
FrozenSymbolTable SymbolTable;
405+
FrozenPropertyRegistry Properties;
406+
};
407+
358408
} // namespace jit_compiler
359409

360410
#endif // SYCL_FUSION_COMMON_KERNEL_H

sycl-jit/jit-compiler/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_llvm_library(sycl-jit
88
lib/fusion/JITContext.cpp
99
lib/fusion/ModuleHelper.cpp
1010
lib/rtc/DeviceCompilation.cpp
11+
lib/rtc/PostLinkActions.cpp
1112
lib/helper/ConfigHelper.cpp
1213

1314
SHARED
@@ -31,6 +32,7 @@ add_llvm_library(sycl-jit
3132
Target
3233
TargetParser
3334
MC
35+
SYCLLowerIR
3436
${LLVM_TARGETS_TO_BUILD}
3537

3638
LINK_LIBS

sycl-jit/jit-compiler/include/KernelFusion.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,32 @@ class JITResult {
5656
sycl::detail::string ErrorMessage;
5757
};
5858

59+
class RTCResult {
60+
public:
61+
explicit RTCResult(const char *ErrorMessage)
62+
: Failed{true}, BundleInfo{}, ErrorMessage{ErrorMessage} {}
63+
64+
explicit RTCResult(RTCBundleInfo &&BundleInfo)
65+
: Failed{false}, BundleInfo{std::move(BundleInfo)}, ErrorMessage{} {}
66+
67+
bool failed() const { return Failed; }
68+
69+
const char *getErrorMessage() const {
70+
assert(failed() && "No error message present");
71+
return ErrorMessage.c_str();
72+
}
73+
74+
const RTCBundleInfo &getBundleInfo() const {
75+
assert(!failed() && "No bundle info");
76+
return BundleInfo;
77+
}
78+
79+
private:
80+
bool Failed;
81+
RTCBundleInfo BundleInfo;
82+
sycl::detail::string ErrorMessage;
83+
};
84+
5985
extern "C" {
6086

6187
#ifdef __clang__
@@ -77,7 +103,7 @@ KF_EXPORT_SYMBOL JITResult materializeSpecConstants(
77103
const char *KernelName, jit_compiler::SYCLKernelBinaryInfo &BinInfo,
78104
View<unsigned char> SpecConstBlob);
79105

80-
KF_EXPORT_SYMBOL JITResult compileSYCL(InMemoryFile SourceFile,
106+
KF_EXPORT_SYMBOL RTCResult compileSYCL(InMemoryFile SourceFile,
81107
View<InMemoryFile> IncludeFiles,
82108
View<const char *> UserArgs);
83109

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

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ using namespace jit_compiler;
2525
using FusedFunction = helper::FusionHelper::FusedFunction;
2626
using FusedFunctionList = std::vector<FusedFunction>;
2727

28-
static JITResult errorToFusionResult(llvm::Error &&Err,
29-
const std::string &Msg) {
28+
template <typename ResultType>
29+
static ResultType wrapError(llvm::Error &&Err, const std::string &Msg) {
3030
std::stringstream ErrMsg;
3131
ErrMsg << Msg << "\nDetailed information:\n";
3232
llvm::handleAllErrors(std::move(Err),
@@ -35,7 +35,16 @@ static JITResult errorToFusionResult(llvm::Error &&Err,
3535
// compiled without exception support.
3636
ErrMsg << "\t" << StrErr.getMessage() << "\n";
3737
});
38-
return JITResult{ErrMsg.str().c_str()};
38+
return ResultType{ErrMsg.str().c_str()};
39+
}
40+
41+
static JITResult errorToFusionResult(llvm::Error &&Err,
42+
const std::string &Msg) {
43+
return wrapError<JITResult>(std::move(Err), Msg);
44+
}
45+
46+
static RTCResult errorToRTCResult(llvm::Error &&Err, const std::string &Msg) {
47+
return wrapError<RTCResult>(std::move(Err), Msg);
3948
}
4049

4150
static std::vector<jit_compiler::NDRange>
@@ -234,37 +243,47 @@ fuseKernels(View<SYCLKernelInfo> KernelInformation, const char *FusedKernelName,
234243
return JITResult{FusedKernelInfo};
235244
}
236245

237-
extern "C" KF_EXPORT_SYMBOL JITResult
246+
extern "C" KF_EXPORT_SYMBOL RTCResult
238247
compileSYCL(InMemoryFile SourceFile, View<InMemoryFile> IncludeFiles,
239248
View<const char *> UserArgs) {
240249
auto UserArgListOrErr = parseUserArgs(UserArgs);
241250
if (!UserArgListOrErr) {
242-
return errorToFusionResult(UserArgListOrErr.takeError(),
243-
"Parsing of user arguments failed");
251+
return errorToRTCResult(UserArgListOrErr.takeError(),
252+
"Parsing of user arguments failed");
244253
}
245254
llvm::opt::InputArgList UserArgList = std::move(*UserArgListOrErr);
246255

247256
auto ModuleOrErr = compileDeviceCode(SourceFile, IncludeFiles, UserArgList);
248257
if (!ModuleOrErr) {
249-
return errorToFusionResult(ModuleOrErr.takeError(),
250-
"Device compilation failed");
258+
return errorToRTCResult(ModuleOrErr.takeError(),
259+
"Device compilation failed");
251260
}
252261

253262
std::unique_ptr<llvm::LLVMContext> Context;
254263
std::unique_ptr<llvm::Module> Module = std::move(*ModuleOrErr);
255264
Context.reset(&Module->getContext());
256265

257266
if (auto Error = linkDeviceLibraries(*Module, UserArgList)) {
258-
return errorToFusionResult(std::move(Error), "Device linking failed");
267+
return errorToRTCResult(std::move(Error), "Device linking failed");
259268
}
260269

261-
SYCLKernelInfo Kernel;
262-
if (auto Error = translation::KernelTranslator::translateKernel(
263-
Kernel, *Module, JITContext::getInstance(), BinaryFormat::SPIRV)) {
264-
return errorToFusionResult(std::move(Error), "SPIR-V translation failed");
270+
auto BundleInfoOrError = performPostLink(*Module, UserArgList);
271+
if (!BundleInfoOrError) {
272+
return errorToRTCResult(BundleInfoOrError.takeError(),
273+
"Post-link phase failed");
274+
}
275+
auto BundleInfo = std::move(*BundleInfoOrError);
276+
277+
auto BinaryInfoOrError =
278+
translation::KernelTranslator::translateBundleToSPIRV(
279+
*Module, JITContext::getInstance());
280+
if (!BinaryInfoOrError) {
281+
return errorToRTCResult(BinaryInfoOrError.takeError(),
282+
"SPIR-V translation failed");
265283
}
284+
BundleInfo.BinaryInfo = std::move(*BinaryInfoOrError);
266285

267-
return JITResult{Kernel};
286+
return RTCResult{std::move(BundleInfo)};
268287
}
269288

270289
extern "C" KF_EXPORT_SYMBOL void resetJITConfiguration() {

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

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include "DeviceCompilation.h"
1010

11+
#include "PostLinkActions.h"
12+
1113
#include <clang/Basic/DiagnosticDriver.h>
1214
#include <clang/Basic/Version.h>
1315
#include <clang/CodeGen/CodeGenAction.h>
@@ -20,15 +22,22 @@
2022

2123
#include <llvm/IRReader/IRReader.h>
2224
#include <llvm/Linker/Linker.h>
23-
24-
#include <array>
25+
#include <llvm/SYCLLowerIR/ComputeModuleRuntimeInfo.h>
26+
#include <llvm/SYCLLowerIR/ModuleSplitter.h>
27+
#include <llvm/SYCLLowerIR/SYCLJointMatrixTransform.h>
28+
#include <llvm/Support/PropertySetIO.h>
2529

2630
using namespace clang;
2731
using namespace clang::tooling;
2832
using namespace clang::driver;
2933
using namespace clang::driver::options;
3034
using namespace llvm;
3135
using namespace llvm::opt;
36+
using namespace llvm::sycl;
37+
using namespace llvm::module_split;
38+
using namespace llvm::util;
39+
using namespace jit_compiler;
40+
using namespace jit_compiler::post_link;
3241

3342
#ifdef _GNU_SOURCE
3443
#include <dlfcn.h>
@@ -356,6 +365,96 @@ Error jit_compiler::linkDeviceLibraries(llvm::Module &Module,
356365
return Error::success();
357366
}
358367

368+
Expected<RTCBundleInfo> jit_compiler::performPostLink(
369+
llvm::Module &Module, [[maybe_unused]] const InputArgList &UserArgList) {
370+
// This is a simplified version of `processInputModule` in
371+
// `llvm/tools/sycl-post-link.cpp`. Assertions/TODOs point to functionality
372+
// left out of the algorithm for now.
373+
374+
// After linking device bitcode "llvm.used" holds references to the kernels
375+
// that are defined in the device image. But after splitting device image into
376+
// separate kernels we may end up with having references to kernel declaration
377+
// originating from "llvm.used" in the IR that is passed to llvm-spirv tool,
378+
// and these declarations cause an assertion in llvm-spirv. To workaround this
379+
// issue remove "llvm.used" from the input module before performing any other
380+
// actions.
381+
removeSYCLKernelsConstRefArray(Module);
382+
383+
// There may be device_global variables kept alive in "llvm.compiler.used"
384+
// to keep the optimizer from wrongfully removing them. llvm.compiler.used
385+
// symbols are usually removed at backend lowering, but this is handled here
386+
// for SPIR-V since SYCL compilation uses llvm-spirv, not the SPIR-V backend.
387+
removeDeviceGlobalFromCompilerUsed(Module);
388+
389+
assert(!isModuleUsingAsan(Module));
390+
// Otherwise: Need to instrument each image scope device globals if the module
391+
// has been instrumented by sanitizer pass.
392+
393+
// Transform Joint Matrix builtin calls to align them with SPIR-V friendly
394+
// LLVM IR specification.
395+
runModulePass<SYCLJointMatrixTransformPass>(Module);
396+
397+
// TODO: Implement actual device code splitting. We're just using the splitter
398+
// to obtain additional information about the module for now.
399+
// TODO: EmitOnlyKernelsAsEntryPoints is controlled by
400+
// `shouldEmitOnlyKernelsAsEntryPoints` in
401+
// `clang/lib/Driver/ToolChains/Clang.cpp`.
402+
std::unique_ptr<ModuleSplitterBase> Splitter = getDeviceCodeSplitter(
403+
ModuleDesc{std::unique_ptr<llvm::Module>{&Module}}, SPLIT_NONE,
404+
/*IROutputOnly=*/false,
405+
/*EmitOnlyKernelsAsEntryPoints=*/true);
406+
bool SplitOccurred = Splitter->remainingSplits() > 1;
407+
assert(!SplitOccurred);
408+
409+
// TODO: Call `verifyNoCrossModuleDeviceGlobalUsage` if device globals shall
410+
// be processed.
411+
412+
assert(Splitter->hasMoreSplits());
413+
ModuleDesc MDesc = Splitter->nextSplit();
414+
assert(&Module == &MDesc.getModule());
415+
MDesc.saveSplitInformationAsMetadata();
416+
417+
RTCBundleInfo BundleInfo;
418+
BundleInfo.SymbolTable =
419+
decltype(BundleInfo.SymbolTable){MDesc.entries().size()};
420+
transform(MDesc.entries(), BundleInfo.SymbolTable.begin(),
421+
[](Function *F) { return F->getName(); });
422+
423+
// TODO: Determine what is requested.
424+
GlobalBinImageProps PropReq{
425+
/*EmitKernelParamInfo=*/true, /*EmitProgramMetadata=*/true,
426+
/*EmitExportedSymbols=*/true, /*EmitImportedSymbols=*/true,
427+
/*DeviceGlobals=*/false};
428+
PropertySetRegistry Properties =
429+
computeModuleProperties(MDesc.getModule(), MDesc.entries(), PropReq);
430+
// TODO: Manually add `compile_target` property as in
431+
// `saveModuleProperties`?
432+
const auto &PropertySets = Properties.getPropSets();
433+
434+
BundleInfo.Properties = decltype(BundleInfo.Properties){PropertySets.size()};
435+
for (auto &&[KV, FrozenPropSet] : zip(PropertySets, BundleInfo.Properties)) {
436+
const auto &PropertySetName = KV.first;
437+
const auto &PropertySet = KV.second;
438+
FrozenPropertySet FPS{PropertySetName.str(), PropertySet.size()};
439+
for (auto &&[KV2, FrozenProp] : zip(PropertySet, FPS.Values)) {
440+
const auto &PropertyName = KV2.first;
441+
const auto &PropertyValue = KV2.second;
442+
FrozenProp = PropertyValue.getType() == PropertyValue::Type::UINT32
443+
? FrozenPropertyValue{PropertyName.str(),
444+
PropertyValue.asUint32()}
445+
: FrozenPropertyValue{
446+
PropertyName.str(), PropertyValue.asRawByteArray(),
447+
PropertyValue.getRawByteArraySize()};
448+
}
449+
FrozenPropSet = std::move(FPS);
450+
};
451+
452+
// Regain ownership of the module.
453+
MDesc.releaseModulePtr().release();
454+
455+
return BundleInfo;
456+
}
457+
359458
Expected<InputArgList>
360459
jit_compiler::parseUserArgs(View<const char *> UserArgs) {
361460
unsigned MissingArgIndex, MissingArgCount;
@@ -410,5 +509,17 @@ jit_compiler::parseUserArgs(View<const char *> UserArgs) {
410509
}
411510
}
412511

512+
if (auto DCSMode = AL.getLastArgValue(OPT_fsycl_device_code_split_EQ, "none");
513+
DCSMode != "none" && DCSMode != "auto") {
514+
return createStringError("Device code splitting is not yet supported");
515+
}
516+
517+
if (AL.hasArg(OPT_fsycl_device_code_split_esimd,
518+
OPT_fno_sycl_device_code_split_esimd)) {
519+
// TODO: There are more ESIMD-related options.
520+
return createStringError(
521+
"Runtime compilation of ESIMD kernels is not yet supported");
522+
}
523+
413524
return Expected<InputArgList>{std::move(AL)};
414525
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ compileDeviceCode(InMemoryFile SourceFile, View<InMemoryFile> IncludeFiles,
2727
llvm::Error linkDeviceLibraries(llvm::Module &Module,
2828
const llvm::opt::InputArgList &UserArgList);
2929

30+
llvm::Expected<RTCBundleInfo>
31+
performPostLink(llvm::Module &Module,
32+
const llvm::opt::InputArgList &UserArgList);
33+
3034
llvm::Expected<llvm::opt::InputArgList>
3135
parseUserArgs(View<const char *> UserArgs);
3236

0 commit comments

Comments
 (0)