Skip to content

Commit cb8b521

Browse files
authored
[ESIMD][NFC] Extract ESIMD handling from sycl-post-link to library. (#18684)
This change extracts handleESIMD from sycl-post-link to SYCLPostLink component for reuse in NewOffloading compilation flow in clang and for reuse in sycl-jit. The body of `handleESIMD` function is refactored for better readability. The documentation of lowerESIMDConstructs is fixed and the argument name is changed to more straightforward.
1 parent 0eb7c73 commit cb8b521

File tree

5 files changed

+150
-95
lines changed

5 files changed

+150
-95
lines changed

llvm/include/llvm/SYCLPostLink/ESIMDPostSplitProcessing.h

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,57 @@
99
// required optimimizations.
1010
//===----------------------------------------------------------------------===//
1111

12+
#ifndef LLVM_SYCL_POST_LINK_ESIMD_POST_SPLIT_PROCESSING_H
13+
#define LLVM_SYCL_POST_LINK_ESIMD_POST_SPLIT_PROCESSING_H
14+
1215
#include "llvm/SYCLPostLink/ModuleSplitter.h"
1316

17+
#include "llvm/ADT/SmallVector.h"
18+
#include "llvm/Support/Error.h"
19+
1420
namespace llvm {
1521
namespace sycl {
1622

23+
struct ESIMDProcessingOptions {
24+
llvm::module_split::IRSplitMode SplitMode =
25+
llvm::module_split::IRSplitMode::SPLIT_NONE;
26+
bool EmitOnlyKernelsAsEntryPoints = false;
27+
bool AllowDeviceImageDependencies = false;
28+
bool LowerESIMD = false;
29+
bool SplitESIMD = false;
30+
unsigned OptLevel = 0;
31+
bool ForceDisableESIMDOpt = false;
32+
};
33+
1734
/// Lowers ESIMD constructs after separation from regular SYCL code.
18-
/// \SplitESIMD identifies that ESIMD splitting is requested in the compilation.
19-
/// Returns true if the given \MD has been modified.
20-
bool lowerESIMDConstructs(llvm::module_split::ModuleDesc &MD, bool OptLevelO0,
21-
bool SplitESIMD);
35+
/// \p Options.SplitESIMD identifies that ESIMD splitting is requested in the
36+
/// compilation. Returns true if the given \p MD has been modified.
37+
bool lowerESIMDConstructs(llvm::module_split::ModuleDesc &MD,
38+
const ESIMDProcessingOptions &Options);
39+
40+
/// Performs ESIMD processing that happens in the following steps:
41+
/// 1) Separate ESIMD Module from SYCL code.
42+
/// \p Options.EmitOnlyKernelsAsEntryPoints and
43+
/// \p Options.AllowDeviceImageDependencies are being used in the splitting.
44+
/// 2) If \p Options.LowerESIMD is true then ESIMD lowering pipeline is applied
45+
/// to the ESIMD Module.
46+
/// If \p Options.OptLevel is not O0 then ESIMD Module is being optimized
47+
/// after the lowering.
48+
/// 3.1) If \p Options.SplitESIMD is true then both ESIMD and non-ESIMD modules
49+
/// are returned.
50+
/// 3.2) Otherwise, two Modules are being linked into one Module which is
51+
/// returned. After the linking graphs become disjoint because functions
52+
/// shared between graphs are cloned and renamed.
53+
///
54+
/// \p Modified value indicates whether the Module has been modified.
55+
/// \p SplitOccurred value indicates whether split has occurred before or during
56+
/// function's invocation.
57+
Expected<SmallVector<module_split::ModuleDesc, 2>>
58+
handleESIMD(llvm::module_split::ModuleDesc MDesc,
59+
const ESIMDProcessingOptions &Options, bool &Modified,
60+
bool &SplitOccurred);
2261

2362
} // namespace sycl
2463
} // namespace llvm
64+
65+
#endif // LLVM_SYCL_POST_LINK_ESIMD_POST_SPLIT_PROCESSING_H

llvm/lib/SYCLPostLink/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_llvm_component_library(LLVMSYCLPostLink
2424
Demangle
2525
InstCombine
2626
IRPrinter
27+
Linker
2728
Passes
2829
ScalarOpts
2930
Support

llvm/lib/SYCLPostLink/ESIMDPostSplitProcessing.cpp

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
#include "llvm/SYCLPostLink/ESIMDPostSplitProcessing.h"
1212

1313
#include "llvm/GenXIntrinsics/GenXSPIRVWriterAdaptor.h"
14+
#include "llvm/Linker/Linker.h"
1415
#include "llvm/Passes/PassBuilder.h"
1516
#include "llvm/SYCLLowerIR/ESIMD/LowerESIMD.h"
1617
#include "llvm/SYCLPostLink/ModuleSplitter.h"
18+
#include "llvm/Support/FormatVariadic.h"
1719
#include "llvm/Transforms/IPO/AlwaysInliner.h"
1820
#include "llvm/Transforms/IPO/StripDeadPrototypes.h"
1921
#include "llvm/Transforms/InstCombine/InstCombine.h"
@@ -30,12 +32,12 @@ using namespace llvm::module_split;
3032

3133
namespace {
3234

33-
ModulePassManager buildESIMDLoweringPipeline(bool ForceDisableESIMDOpt,
34-
bool SplitESIMD) {
35+
ModulePassManager
36+
buildESIMDLoweringPipeline(const sycl::ESIMDProcessingOptions &Options) {
3537
ModulePassManager MPM;
36-
MPM.addPass(SYCLLowerESIMDPass(!SplitESIMD));
38+
MPM.addPass(SYCLLowerESIMDPass(!Options.SplitESIMD));
3739

38-
if (!ForceDisableESIMDOpt) {
40+
if (!Options.ForceDisableESIMDOpt) {
3941
FunctionPassManager FPM;
4042
FPM.addPass(SROAPass(SROAOptions::ModifyCFG));
4143
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
@@ -44,7 +46,7 @@ ModulePassManager buildESIMDLoweringPipeline(bool ForceDisableESIMDOpt,
4446
FunctionPassManager MainFPM;
4547
MainFPM.addPass(ESIMDLowerLoadStorePass{});
4648

47-
if (!ForceDisableESIMDOpt) {
49+
if (!Options.ForceDisableESIMDOpt) {
4850
MainFPM.addPass(SROAPass(SROAOptions::ModifyCFG));
4951
MainFPM.addPass(EarlyCSEPass(true));
5052
MainFPM.addPass(InstCombinePass{});
@@ -61,12 +63,29 @@ ModulePassManager buildESIMDLoweringPipeline(bool ForceDisableESIMDOpt,
6163
return MPM;
6264
}
6365

66+
Expected<ModuleDesc> linkModules(ModuleDesc MD1, ModuleDesc MD2) {
67+
std::vector<std::string> Names;
68+
MD1.saveEntryPointNames(Names);
69+
MD2.saveEntryPointNames(Names);
70+
bool LinkError =
71+
llvm::Linker::linkModules(MD1.getModule(), MD2.releaseModulePtr());
72+
73+
if (LinkError)
74+
return createStringError(
75+
formatv("link failed. Module names: {0}, {1}", MD1.Name, MD2.Name));
76+
77+
ModuleDesc Res(MD1.releaseModulePtr(), std::move(Names));
78+
Res.assignMergedProperties(MD1, MD2);
79+
Res.Name = (Twine("linked[") + MD1.Name + "," + MD2.Name + "]").str();
80+
return std::move(Res);
81+
}
82+
6483
} // anonymous namespace
6584

6685
// When ESIMD code was separated from the regular SYCL code,
6786
// we can safely process ESIMD part.
68-
bool sycl::lowerESIMDConstructs(ModuleDesc &MD, bool ForceDisableESIMDOpt,
69-
bool SplitESIMD) {
87+
bool sycl::lowerESIMDConstructs(ModuleDesc &MD,
88+
const sycl::ESIMDProcessingOptions &Options) {
7089
// TODO: support options like -debug-pass, -print-[before|after], and others
7190
LoopAnalysisManager LAM;
7291
CGSCCAnalysisManager CGAM;
@@ -82,12 +101,64 @@ bool sycl::lowerESIMDConstructs(ModuleDesc &MD, bool ForceDisableESIMDOpt,
82101

83102
std::vector<std::string> Names;
84103
MD.saveEntryPointNames(Names);
85-
ModulePassManager MPM =
86-
buildESIMDLoweringPipeline(ForceDisableESIMDOpt, SplitESIMD);
104+
ModulePassManager MPM = buildESIMDLoweringPipeline(Options);
87105
PreservedAnalyses Res = MPM.run(MD.getModule(), MAM);
88106

89107
// GenXSPIRVWriterAdaptor pass replaced some functions with "rewritten"
90108
// versions so the entry point table must be rebuilt.
91109
MD.rebuildEntryPoints(Names);
92110
return !Res.areAllPreserved();
93111
}
112+
113+
Expected<SmallVector<ModuleDesc, 2>>
114+
llvm::sycl::handleESIMD(ModuleDesc MDesc,
115+
const sycl::ESIMDProcessingOptions &Options,
116+
bool &Modified, bool &SplitOccurred) {
117+
SmallVector<ModuleDesc, 2> Result =
118+
splitByESIMD(std::move(MDesc), Options.EmitOnlyKernelsAsEntryPoints,
119+
Options.AllowDeviceImageDependencies);
120+
121+
assert(Result.size() <= 2 &&
122+
"Split modules aren't expected to be more than 2.");
123+
124+
SplitOccurred |= Result.size() > 1;
125+
126+
for (ModuleDesc &MD : Result) {
127+
#ifdef LLVM_ENABLE_DUMP
128+
dumpEntryPoints(MD.entries(), MD.Name.c_str(), 4);
129+
#endif // LLVM_ENABLE_DUMP
130+
if (Options.LowerESIMD && MD.isESIMD())
131+
Modified |= lowerESIMDConstructs(MD, Options);
132+
}
133+
134+
if (Options.SplitESIMD || Result.size() == 1)
135+
return std::move(Result);
136+
137+
// SYCL/ESIMD splitting is not requested, link back into single module.
138+
int ESIMDInd = Result[0].isESIMD() ? 0 : 1;
139+
int SYCLInd = 1 - ESIMDInd;
140+
assert(Result[SYCLInd].isSYCL() &&
141+
"Result[SYCLInd].isSYCL() expected to be true.");
142+
143+
// Make sure that no link conflicts occur.
144+
Result[ESIMDInd].renameDuplicatesOf(Result[SYCLInd].getModule(), ".esimd");
145+
auto LinkedOrErr = linkModules(std::move(Result[0]), std::move(Result[1]));
146+
if (!LinkedOrErr)
147+
return LinkedOrErr.takeError();
148+
149+
ModuleDesc &Linked = *LinkedOrErr;
150+
Linked.restoreLinkageOfDirectInvokeSimdTargets();
151+
std::vector<std::string> Names;
152+
Linked.saveEntryPointNames(Names);
153+
// Cleanup may remove some entry points, need to save/rebuild.
154+
Linked.cleanup(Options.AllowDeviceImageDependencies);
155+
Linked.rebuildEntryPoints(Names);
156+
Result.clear();
157+
Result.emplace_back(std::move(Linked));
158+
#ifdef LLVM_ENABLE_DUMP
159+
dumpEntryPoints(Result.back().entries(), Result.back().Name.c_str(), 4);
160+
#endif // LLVM_ENABLE_DUMP
161+
Modified = true;
162+
163+
return std::move(Result);
164+
}

llvm/tools/sycl-post-link/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ set(LLVM_LINK_COMPONENTS
99
TransformUtils
1010
SYCLLowerIR
1111
SYCLPostLink
12-
Linker
1312
Passes
1413
Analysis
1514
)

llvm/tools/sycl-post-link/sycl-post-link.cpp

Lines changed: 24 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@
5959
using namespace llvm;
6060
using namespace llvm::sycl;
6161

62-
using string_vector = std::vector<std::string>;
63-
6462
namespace {
6563

6664
#ifdef NDEBUG
@@ -263,6 +261,19 @@ struct IrPropSymFilenameTriple {
263261
std::string Sym;
264262
};
265263

264+
unsigned getOptLevel() {
265+
if (OptLevelO3)
266+
return 3;
267+
if (OptLevelO2 || OptLevelOs || OptLevelOz)
268+
return 2;
269+
if (OptLevelO1)
270+
return 1;
271+
if (OptLevelO0)
272+
return 0;
273+
274+
return 2; // default value
275+
}
276+
266277
static void writeToFile(const StringRef Filename, const StringRef Content) {
267278
std::error_code EC;
268279
raw_fd_ostream OS{Filename, EC, sys::fs::OpenFlags::OF_None};
@@ -418,23 +429,6 @@ void saveDeviceLibModule(
418429
saveModule(OutTables, DeviceLibMD, I, OutputPrefix, "");
419430
}
420431

421-
module_split::ModuleDesc link(module_split::ModuleDesc &&MD1,
422-
module_split::ModuleDesc &&MD2) {
423-
std::vector<std::string> Names;
424-
MD1.saveEntryPointNames(Names);
425-
MD2.saveEntryPointNames(Names);
426-
bool LinkError =
427-
llvm::Linker::linkModules(MD1.getModule(), MD2.releaseModulePtr());
428-
429-
if (LinkError) {
430-
error(" error when linking SYCL and ESIMD modules");
431-
}
432-
module_split::ModuleDesc Res(MD1.releaseModulePtr(), std::move(Names));
433-
Res.assignMergedProperties(MD1, MD2);
434-
Res.Name = "linked[" + MD1.Name + "," + MD2.Name + "]";
435-
return Res;
436-
}
437-
438432
bool processSpecConstants(module_split::ModuleDesc &MD) {
439433
MD.Props.SpecConstsMet = false;
440434

@@ -500,65 +494,6 @@ void addTableRow(util::SimpleTable &Table,
500494
Table.addRow(Row);
501495
}
502496

503-
SmallVector<module_split::ModuleDesc, 2>
504-
handleESIMD(module_split::ModuleDesc &&MDesc, bool &Modified,
505-
bool &SplitOccurred) {
506-
// Do SYCL/ESIMD splitting. It happens always, as ESIMD and SYCL must
507-
// undergo different set of LLVMIR passes. After this they are linked back
508-
// together to form single module with disjoint SYCL and ESIMD call graphs
509-
// unless -split-esimd option is specified. The graphs become disjoint
510-
// when linked back because functions shared between graphs are cloned and
511-
// renamed.
512-
SmallVector<module_split::ModuleDesc, 2> Result =
513-
module_split::splitByESIMD(std::move(MDesc), EmitOnlyKernelsAsEntryPoints,
514-
AllowDeviceImageDependencies);
515-
516-
if (Result.size() > 1 && SplitOccurred &&
517-
(SplitMode == module_split::SPLIT_PER_KERNEL) && !SplitEsimd) {
518-
// Controversial state reached - SYCL and ESIMD entry points resulting
519-
// from SYCL/ESIMD split (which is done always) are linked back, since
520-
// -split-esimd is not specified, but per-kernel split is requested.
521-
warning("SYCL and ESIMD entry points detected and split mode is "
522-
"per-kernel, so " +
523-
SplitEsimd.ValueStr + " must also be specified");
524-
}
525-
SplitOccurred |= Result.size() > 1;
526-
527-
for (auto &MD : Result) {
528-
DUMP_ENTRY_POINTS(MD.entries(), MD.Name.c_str(), 3);
529-
if (LowerEsimd && MD.isESIMD())
530-
Modified |=
531-
sycl::lowerESIMDConstructs(MD, ForceDisableESIMDOpt, SplitEsimd);
532-
}
533-
534-
if (!SplitEsimd && Result.size() > 1) {
535-
// SYCL/ESIMD splitting is not requested, link back into single module.
536-
assert(Result.size() == 2 &&
537-
"Unexpected number of modules as results of ESIMD split");
538-
int ESIMDInd = Result[0].isESIMD() ? 0 : 1;
539-
int SYCLInd = 1 - ESIMDInd;
540-
assert(Result[SYCLInd].isSYCL() &&
541-
"no non-ESIMD module as a result ESIMD split?");
542-
543-
// ... but before that, make sure no link conflicts will occur.
544-
Result[ESIMDInd].renameDuplicatesOf(Result[SYCLInd].getModule(), ".esimd");
545-
module_split::ModuleDesc Linked =
546-
link(std::move(Result[0]), std::move(Result[1]));
547-
Linked.restoreLinkageOfDirectInvokeSimdTargets();
548-
string_vector Names;
549-
Linked.saveEntryPointNames(Names);
550-
// cleanup may remove some entry points, need to save/rebuild
551-
Linked.cleanup(AllowDeviceImageDependencies);
552-
Linked.rebuildEntryPoints(Names);
553-
Result.clear();
554-
Result.emplace_back(std::move(Linked));
555-
DUMP_ENTRY_POINTS(Result.back().entries(), Result.back().Name.c_str(), 3);
556-
Modified = true;
557-
}
558-
559-
return Result;
560-
}
561-
562497
// Checks if the given target and module are compatible.
563498
// A target and module are compatible if all the optional kernel features
564499
// the module uses are supported by that target (i.e. that module can be
@@ -676,10 +611,18 @@ processInputModule(std::unique_ptr<Module> M, const StringRef OutputPrefix) {
676611

677612
MDesc.fixupLinkageOfDirectInvokeSimdTargets();
678613

679-
SmallVector<module_split::ModuleDesc, 2> MMs =
680-
handleESIMD(std::move(MDesc), Modified, SplitOccurred);
614+
ESIMDProcessingOptions Options = {SplitMode,
615+
EmitOnlyKernelsAsEntryPoints,
616+
AllowDeviceImageDependencies,
617+
LowerEsimd,
618+
SplitEsimd,
619+
getOptLevel(),
620+
ForceDisableESIMDOpt};
621+
auto ModulesOrErr =
622+
handleESIMD(std::move(MDesc), Options, Modified, SplitOccurred);
623+
CHECK_AND_EXIT(ModulesOrErr.takeError());
624+
SmallVector<module_split::ModuleDesc, 2> &MMs = *ModulesOrErr;
681625
assert(MMs.size() && "at least one module is expected after ESIMD split");
682-
683626
SmallVector<module_split::ModuleDesc, 2> MMsWithDefaultSpecConsts;
684627
for (size_t I = 0; I != MMs.size(); ++I) {
685628
if (GenerateDeviceImageWithDefaultSpecConsts) {

0 commit comments

Comments
 (0)