diff --git a/llvm/include/llvm/SYCLPostLink/ESIMDPostSplitProcessing.h b/llvm/include/llvm/SYCLPostLink/ESIMDPostSplitProcessing.h index a69e7d645abda..b0364f79397a9 100644 --- a/llvm/include/llvm/SYCLPostLink/ESIMDPostSplitProcessing.h +++ b/llvm/include/llvm/SYCLPostLink/ESIMDPostSplitProcessing.h @@ -9,16 +9,56 @@ // required optimimizations. //===----------------------------------------------------------------------===// +#ifndef LLVM_SYCL_POST_LINK_ESIMD_POST_SPLIT_PROCESSING_H +#define LLVM_SYCL_POST_LINK_ESIMD_POST_SPLIT_PROCESSING_H + #include "llvm/SYCLPostLink/ModuleSplitter.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Error.h" + namespace llvm { namespace sycl { +struct ESIMDProcessingOptions { + llvm::module_split::IRSplitMode SplitMode = + llvm::module_split::IRSplitMode::SPLIT_NONE; + bool EmitOnlyKernelsAsEntryPoints = false; + bool AllowDeviceImageDependencies = false; + bool LowerESIMD = false; + bool SplitESIMD = false; + unsigned OptLevel = 0; +}; + /// Lowers ESIMD constructs after separation from regular SYCL code. -/// \SplitESIMD identifies that ESIMD splitting is requested in the compilation. -/// Returns true if the given \MD has been modified. -bool lowerESIMDConstructs(llvm::module_split::ModuleDesc &MD, bool OptLevelO0, - bool SplitESIMD); +/// \p Options.SplitESIMD identifies that ESIMD splitting is requested in the +/// compilation. Returns true if the given \p MD has been modified. +bool lowerESIMDConstructs(llvm::module_split::ModuleDesc &MD, + const ESIMDProcessingOptions &Options); + +/// Performs ESIMD processing that happens in the following steps: +/// 1) Separate ESIMD Module from SYCL code. +/// \p Options.EmitOnlyKernelsAsEntryPoints and +/// \p Options.AllowDeviceImageDependencies are being used in the splitting. +/// 2) If \p Options.LowerESIMD is true then ESIMD lowering pipeline is applied +/// to the ESIMD Module. +/// If \p Options.OptLevel is not O0 then ESIMD Module is being optimized +/// after the lowering. +/// 3.1) If \p Options.SplitESIMD is true then both ESIMD and non-ESIMD modules +/// are returned. +/// 3.2) Otherwise, two Modules are being linked into one Module which is +/// returned. After the linking graphs become disjoint because functions +/// shared between graphs are cloned and renamed. +/// +/// \p Modified value indicates whether the Module has been modified. +/// \p SplitOccurred value indicates whether split has occurred before or during +/// function's invocation. +Expected<SmallVector<module_split::ModuleDesc, 2>> +handleESIMD(llvm::module_split::ModuleDesc MDesc, + const ESIMDProcessingOptions &Options, bool &Modified, + bool &SplitOccurred); } // namespace sycl } // namespace llvm + +#endif // LLVM_SYCL_POST_LINK_ESIMD_POST_SPLIT_PROCESSING_H diff --git a/llvm/lib/SYCLPostLink/CMakeLists.txt b/llvm/lib/SYCLPostLink/CMakeLists.txt index ffe061acf1b66..131b249bc10b5 100644 --- a/llvm/lib/SYCLPostLink/CMakeLists.txt +++ b/llvm/lib/SYCLPostLink/CMakeLists.txt @@ -24,6 +24,7 @@ add_llvm_component_library(LLVMSYCLPostLink Demangle InstCombine IRPrinter + Linker Passes ScalarOpts Support diff --git a/llvm/lib/SYCLPostLink/ESIMDPostSplitProcessing.cpp b/llvm/lib/SYCLPostLink/ESIMDPostSplitProcessing.cpp index 85163c963605f..9fc725599bb4d 100644 --- a/llvm/lib/SYCLPostLink/ESIMDPostSplitProcessing.cpp +++ b/llvm/lib/SYCLPostLink/ESIMDPostSplitProcessing.cpp @@ -11,9 +11,11 @@ #include "llvm/SYCLPostLink/ESIMDPostSplitProcessing.h" #include "llvm/GenXIntrinsics/GenXSPIRVWriterAdaptor.h" +#include "llvm/Linker/Linker.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/SYCLLowerIR/ESIMD/LowerESIMD.h" #include "llvm/SYCLPostLink/ModuleSplitter.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/StripDeadPrototypes.h" #include "llvm/Transforms/InstCombine/InstCombine.h" @@ -30,11 +32,12 @@ using namespace llvm::module_split; namespace { -ModulePassManager buildESIMDLoweringPipeline(bool OptLevelO0, bool SplitESIMD) { +ModulePassManager +buildESIMDLoweringPipeline(const sycl::ESIMDProcessingOptions &Options) { ModulePassManager MPM; - MPM.addPass(SYCLLowerESIMDPass(!SplitESIMD)); + MPM.addPass(SYCLLowerESIMDPass(!Options.SplitESIMD)); - if (!OptLevelO0) { + if (Options.OptLevel != 0) { FunctionPassManager FPM; FPM.addPass(SROAPass(SROAOptions::ModifyCFG)); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); @@ -43,7 +46,7 @@ ModulePassManager buildESIMDLoweringPipeline(bool OptLevelO0, bool SplitESIMD) { FunctionPassManager MainFPM; MainFPM.addPass(ESIMDLowerLoadStorePass{}); - if (!OptLevelO0) { + if (Options.OptLevel != 0) { MainFPM.addPass(SROAPass(SROAOptions::ModifyCFG)); MainFPM.addPass(EarlyCSEPass(true)); MainFPM.addPass(InstCombinePass{}); @@ -60,12 +63,29 @@ ModulePassManager buildESIMDLoweringPipeline(bool OptLevelO0, bool SplitESIMD) { return MPM; } +Expected<ModuleDesc> linkModules(ModuleDesc MD1, ModuleDesc MD2) { + std::vector<std::string> Names; + MD1.saveEntryPointNames(Names); + MD2.saveEntryPointNames(Names); + bool LinkError = + llvm::Linker::linkModules(MD1.getModule(), MD2.releaseModulePtr()); + + if (LinkError) + return createStringError( + formatv("link failed. Module names: {0}, {1}", MD1.Name, MD2.Name)); + + ModuleDesc Res(MD1.releaseModulePtr(), std::move(Names)); + Res.assignMergedProperties(MD1, MD2); + Res.Name = (Twine("linked[") + MD1.Name + "," + MD2.Name + "]").str(); + return Res; +} + } // anonymous namespace // When ESIMD code was separated from the regular SYCL code, // we can safely process ESIMD part. -bool sycl::lowerESIMDConstructs(ModuleDesc &MD, bool OptLevelO0, - bool SplitESIMD) { +bool sycl::lowerESIMDConstructs(ModuleDesc &MD, + const sycl::ESIMDProcessingOptions &Options) { // TODO: support options like -debug-pass, -print-[before|after], and others LoopAnalysisManager LAM; CGSCCAnalysisManager CGAM; @@ -81,7 +101,7 @@ bool sycl::lowerESIMDConstructs(ModuleDesc &MD, bool OptLevelO0, std::vector<std::string> Names; MD.saveEntryPointNames(Names); - ModulePassManager MPM = buildESIMDLoweringPipeline(OptLevelO0, SplitESIMD); + ModulePassManager MPM = buildESIMDLoweringPipeline(Options); PreservedAnalyses Res = MPM.run(MD.getModule(), MAM); // GenXSPIRVWriterAdaptor pass replaced some functions with "rewritten" @@ -89,3 +109,56 @@ bool sycl::lowerESIMDConstructs(ModuleDesc &MD, bool OptLevelO0, MD.rebuildEntryPoints(Names); return !Res.areAllPreserved(); } + +Expected<SmallVector<ModuleDesc, 2>> +llvm::sycl::handleESIMD(ModuleDesc MDesc, + const sycl::ESIMDProcessingOptions &Options, + bool &Modified, bool &SplitOccurred) { + SmallVector<ModuleDesc, 2> Result = + splitByESIMD(std::move(MDesc), Options.EmitOnlyKernelsAsEntryPoints, + Options.AllowDeviceImageDependencies); + + assert(Result.size() <= 2 && + "Split modules aren't expected to be more than 2."); + + SplitOccurred |= Result.size() > 1; + + for (ModuleDesc &MD : Result) { +#ifdef LLVM_ENABLE_DUMP + dumpEntryPoints(MD.entries(), MD.Name.c_str(), 4); +#endif // LLVM_ENABLE_DUMP + if (Options.LowerESIMD && MD.isESIMD()) + Modified |= lowerESIMDConstructs(MD, Options); + } + + if (Options.SplitESIMD || Result.size() == 1) + return Result; + + // SYCL/ESIMD splitting is not requested, link back into single module. + int ESIMDInd = Result[0].isESIMD() ? 0 : 1; + int SYCLInd = 1 - ESIMDInd; + assert(Result[SYCLInd].isSYCL() && + "Result[SYCLInd].isSYCL() expected to be true."); + + // Make sure that no link conflicts occur. + Result[ESIMDInd].renameDuplicatesOf(Result[SYCLInd].getModule(), ".esimd"); + auto LinkedOrErr = linkModules(std::move(Result[0]), std::move(Result[1])); + if (!LinkedOrErr) + return LinkedOrErr.takeError(); + + ModuleDesc &Linked = *LinkedOrErr; + Linked.restoreLinkageOfDirectInvokeSimdTargets(); + std::vector<std::string> Names; + Linked.saveEntryPointNames(Names); + // Cleanup may remove some entry points, need to save/rebuild. + Linked.cleanup(Options.AllowDeviceImageDependencies); + Linked.rebuildEntryPoints(Names); + Result.clear(); + Result.emplace_back(std::move(Linked)); +#ifdef LLVM_ENABLE_DUMP + dumpEntryPoints(Result.back().entries(), Result.back().Name.c_str(), 4); +#endif // LLVM_ENABLE_DUMP + Modified = true; + + return Result; +} diff --git a/llvm/tools/sycl-post-link/CMakeLists.txt b/llvm/tools/sycl-post-link/CMakeLists.txt index ace9e66e44535..d229f80767fd3 100644 --- a/llvm/tools/sycl-post-link/CMakeLists.txt +++ b/llvm/tools/sycl-post-link/CMakeLists.txt @@ -9,7 +9,6 @@ set(LLVM_LINK_COMPONENTS TransformUtils SYCLLowerIR SYCLPostLink - Linker Passes Analysis ) diff --git a/llvm/tools/sycl-post-link/sycl-post-link.cpp b/llvm/tools/sycl-post-link/sycl-post-link.cpp index b9aa5453284a5..84b99a945d8bf 100644 --- a/llvm/tools/sycl-post-link/sycl-post-link.cpp +++ b/llvm/tools/sycl-post-link/sycl-post-link.cpp @@ -59,8 +59,6 @@ using namespace llvm; using namespace llvm::sycl; -using string_vector = std::vector<std::string>; - namespace { #ifdef NDEBUG @@ -259,6 +257,19 @@ struct IrPropSymFilenameTriple { std::string Sym; }; +unsigned getOptLevel() { + if (OptLevelO3) + return 3; + if (OptLevelO2 || OptLevelOs || OptLevelOz) + return 2; + if (OptLevelO1) + return 1; + if (OptLevelO0) + return 0; + + return 2; // default value +} + void writeToFile(const std::string &Filename, const std::string &Content) { std::error_code EC; raw_fd_ostream OS{Filename, EC, sys::fs::OpenFlags::OF_None}; @@ -434,23 +445,6 @@ void saveDeviceLibModule( saveModule(OutTables, DeviceLibMD, I, DeviceLibFileName); } -module_split::ModuleDesc link(module_split::ModuleDesc &&MD1, - module_split::ModuleDesc &&MD2) { - std::vector<std::string> Names; - MD1.saveEntryPointNames(Names); - MD2.saveEntryPointNames(Names); - bool LinkError = - llvm::Linker::linkModules(MD1.getModule(), MD2.releaseModulePtr()); - - if (LinkError) { - error(" error when linking SYCL and ESIMD modules"); - } - module_split::ModuleDesc Res(MD1.releaseModulePtr(), std::move(Names)); - Res.assignMergedProperties(MD1, MD2); - Res.Name = "linked[" + MD1.Name + "," + MD2.Name + "]"; - return Res; -} - bool processSpecConstants(module_split::ModuleDesc &MD) { MD.Props.SpecConstsMet = false; @@ -516,64 +510,6 @@ void addTableRow(util::SimpleTable &Table, Table.addRow(Row); } -SmallVector<module_split::ModuleDesc, 2> -handleESIMD(module_split::ModuleDesc &&MDesc, bool &Modified, - bool &SplitOccurred) { - // Do SYCL/ESIMD splitting. It happens always, as ESIMD and SYCL must - // undergo different set of LLVMIR passes. After this they are linked back - // together to form single module with disjoint SYCL and ESIMD call graphs - // unless -split-esimd option is specified. The graphs become disjoint - // when linked back because functions shared between graphs are cloned and - // renamed. - SmallVector<module_split::ModuleDesc, 2> Result = - module_split::splitByESIMD(std::move(MDesc), EmitOnlyKernelsAsEntryPoints, - AllowDeviceImageDependencies); - - if (Result.size() > 1 && SplitOccurred && - (SplitMode == module_split::SPLIT_PER_KERNEL) && !SplitEsimd) { - // Controversial state reached - SYCL and ESIMD entry points resulting - // from SYCL/ESIMD split (which is done always) are linked back, since - // -split-esimd is not specified, but per-kernel split is requested. - warning("SYCL and ESIMD entry points detected and split mode is " - "per-kernel, so " + - SplitEsimd.ValueStr + " must also be specified"); - } - SplitOccurred |= Result.size() > 1; - - for (auto &MD : Result) { - DUMP_ENTRY_POINTS(MD.entries(), MD.Name.c_str(), 3); - if (LowerEsimd && MD.isESIMD()) - Modified |= sycl::lowerESIMDConstructs(MD, OptLevelO0, SplitEsimd); - } - - if (!SplitEsimd && Result.size() > 1) { - // SYCL/ESIMD splitting is not requested, link back into single module. - assert(Result.size() == 2 && - "Unexpected number of modules as results of ESIMD split"); - int ESIMDInd = Result[0].isESIMD() ? 0 : 1; - int SYCLInd = 1 - ESIMDInd; - assert(Result[SYCLInd].isSYCL() && - "no non-ESIMD module as a result ESIMD split?"); - - // ... but before that, make sure no link conflicts will occur. - Result[ESIMDInd].renameDuplicatesOf(Result[SYCLInd].getModule(), ".esimd"); - module_split::ModuleDesc Linked = - link(std::move(Result[0]), std::move(Result[1])); - Linked.restoreLinkageOfDirectInvokeSimdTargets(); - string_vector Names; - Linked.saveEntryPointNames(Names); - // cleanup may remove some entry points, need to save/rebuild - Linked.cleanup(AllowDeviceImageDependencies); - Linked.rebuildEntryPoints(Names); - Result.clear(); - Result.emplace_back(std::move(Linked)); - DUMP_ENTRY_POINTS(Result.back().entries(), Result.back().Name.c_str(), 3); - Modified = true; - } - - return Result; -} - // Checks if the given target and module are compatible. // A target and module are compatible if all the optional kernel features // the module uses are supported by that target (i.e. that module can be @@ -691,10 +627,17 @@ processInputModule(std::unique_ptr<Module> M) { MDesc.fixupLinkageOfDirectInvokeSimdTargets(); - SmallVector<module_split::ModuleDesc, 2> MMs = - handleESIMD(std::move(MDesc), Modified, SplitOccurred); + ESIMDProcessingOptions Options = {SplitMode, + EmitOnlyKernelsAsEntryPoints, + AllowDeviceImageDependencies, + LowerEsimd, + SplitEsimd, + getOptLevel()}; + auto ModulesOrErr = + handleESIMD(std::move(MDesc), Options, Modified, SplitOccurred); + CHECK_AND_EXIT(ModulesOrErr.takeError()); + SmallVector<module_split::ModuleDesc, 2> &MMs = *ModulesOrErr; assert(MMs.size() && "at least one module is expected after ESIMD split"); - SmallVector<module_split::ModuleDesc, 2> MMsWithDefaultSpecConsts; for (size_t I = 0; I != MMs.size(); ++I) { if (GenerateDeviceImageWithDefaultSpecConsts) {