Skip to content

[sycl-post-link][NFC] Small cleanups and cosmetic changes #4986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 76 additions & 76 deletions llvm/tools/sycl-post-link/sycl-post-link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@
#include "llvm/Transforms/Utils/Cloning.h"

#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <vector>

using namespace llvm;

using string_vector = std::vector<std::string>;
using PropSetRegTy = llvm::util::PropertySetRegistry;
using EntryPointsMap = std::map<StringRef, std::vector<const Function *>>;

namespace {

Expand Down Expand Up @@ -184,7 +185,6 @@ cl::opt<bool> EmitOnlyKernelsAsEntryPoints{
cl::cat(PostLinkCat), cl::init(false)};

struct ImagePropSaveInfo {
bool SetSpecConstAtRT;
bool SpecConstsMet;
bool EmitKernelParamInfo;
bool EmitProgramMetadata;
Expand All @@ -210,9 +210,9 @@ void writeToFile(const std::string &Filename, const std::string &Content) {
OS.close();
}

// Describes scope covered by each entry in the module-kernel map populated by
// the collectKernelModuleMap function.
enum KernelMapEntryScope {
// Describes scope covered by each entry in the module-entry points map
// populated by the collectEntryPointToModuleMap function.
enum EntryPointsSplitScope {
Scope_PerKernel, // one entry per kernel
Scope_PerModule, // one entry per module
Scope_Global // single entry in the map for all kernels
Expand Down Expand Up @@ -245,7 +245,7 @@ bool hasIndirectFunctionCalls(const Module &M) {
return false;
}

KernelMapEntryScope selectDeviceCodeSplitScope(const Module &M) {
EntryPointsSplitScope selectDeviceCodeSplitScope(const Module &M) {
bool DoSplit = SplitMode.getNumOccurrences() > 0;
if (DoSplit) {
switch (SplitMode) {
Expand Down Expand Up @@ -288,7 +288,7 @@ bool isSpirvSyclBuiltin(StringRef FName) {
}

bool isEntryPoint(const Function &F) {
// Skip declarations, if any: they should not be included into KernelModuleMap
// Skip declarations, if any: they should not be included into EntryPointsMap
// or otherwise we will end up with incorrectly generated list of symbols.
if (F.isDeclaration())
return false;
Expand All @@ -311,13 +311,12 @@ bool isEntryPoint(const Function &F) {
// This function decides how entry points of the input module M will be
// distributed ("split") into multiple modules based on the command options and
// IR attributes. The decision is recorded in the output map parameter
// ResKernelModuleMap which maps some key to a group of entry points. Each such
// EntryPointsSplitMap which maps some key to a group of entry points. Each such
// group along with IR it depends on (globals, functions from its call graph,
// ...) will constitute a separate module.
void collectEntryPointToModuleMap(
const Module &M,
std::map<StringRef, std::vector<const Function *>> &ResKernelModuleMap,
KernelMapEntryScope EntryScope) {
void collectEntryPointToModuleMap(const Module &M,
EntryPointsMap &EntryPointsSplitMap,
EntryPointsSplitScope EntryScope) {

// Only process module entry points:
for (const auto &F : M.functions()) {
Expand All @@ -326,7 +325,7 @@ void collectEntryPointToModuleMap(

switch (EntryScope) {
case Scope_PerKernel:
ResKernelModuleMap[F.getName()].push_back(&F);
EntryPointsSplitMap[F.getName()].push_back(&F);
break;
case Scope_PerModule: {
if (!F.hasFnAttribute(ATTR_SYCL_MODULE_ID))
Expand All @@ -339,12 +338,12 @@ void collectEntryPointToModuleMap(

Attribute Id = F.getFnAttribute(ATTR_SYCL_MODULE_ID);
StringRef Val = Id.getValueAsString();
ResKernelModuleMap[Val].push_back(&F);
EntryPointsSplitMap[Val].push_back(&F);
break;
}
case Scope_Global:
// the map key is not significant here
ResKernelModuleMap[GLOBAL_SCOPE_NAME].push_back(&F);
EntryPointsSplitMap[GLOBAL_SCOPE_NAME].push_back(&F);
break;
}
}
Expand Down Expand Up @@ -488,41 +487,41 @@ std::vector<uint32_t> getKernelReqdWorkGroupSizeMetadata(const Function &Func) {
return {X, Y, Z};
}

// Input parameter KernelModuleMap is a map containing groups of entry points
// with same values of the sycl-module-id attribute. ResSymbolsLists is a vector
// of entry points names lists. Each vector element is a string with entry point
// names from the same module separated by \n.
// Input parameter EntryPointsSplitMap contains a map of entry points or groups
// of entry points with same values of the sycl-module-id attribute.
// Return value is a vector of entry points names lists. Each vector element is
// a string with entry point names from the same module separated by \n.
// The function saves names of entry points from one group to a single
// std::string and stores this string to the ResSymbolsLists vector.
void collectSymbolsLists(
const std::map<StringRef, std::vector<const Function *>> &KernelModuleMap,
string_vector &ResSymbolsLists) {
for (const auto &It : KernelModuleMap) {
string_vector collectSymbolsLists(const EntryPointsMap &EntryPointsSplitMap) {
string_vector ResSymbolsLists{};
for (const auto &It : EntryPointsSplitMap) {
std::string SymbolsList;
for (const auto &F : It.second) {
SymbolsList =
(Twine(SymbolsList) + Twine(F->getName()) + Twine("\n")).str();
}
ResSymbolsLists.push_back(std::move(SymbolsList));
}
return ResSymbolsLists;
}

struct ResultModule {
StringRef KernelModuleName;
StringRef SplitModuleId;
std::unique_ptr<Module> ModulePtr;
};

// Input parameter KernelModuleMap is a map containing groups of entry points
// with same values of the sycl-module-id attribute. For each group of entry
// points a separate IR module will be produced.
// ResModules is a vector of pairs of kernel module names and produced modules.
// The function splits input LLVM IR module M into smaller ones and stores them
// to the ResModules vector.
void splitModule(
const Module &M,
const std::map<StringRef, std::vector<const Function *>> &KernelModuleMap,
std::vector<ResultModule> &ResModules) {
for (const auto &It : KernelModuleMap) {
// Input parameter EntryPointsSplitMap contains a map of entry points or groups
// of entry points with same values of the sycl-module-id attribute.
// For each group of entry points a separate IR module will be produced.
// ResModules is a vector of pairs of split module identifiers and produced
// modules. The function splits input LLVM IR module M into smaller ones and
// stores them to the ResModules vector.
std::vector<ResultModule>
splitModule(const Module &M, const EntryPointsMap &EntryPointsSplitMap) {
std::vector<ResultModule> ResModules{};

for (const auto &It : EntryPointsSplitMap) {
// For each group of entry points collect all dependencies.
SetVector<const GlobalValue *> GVs;
std::vector<const Function *> Workqueue;
Expand Down Expand Up @@ -570,6 +569,8 @@ void splitModule(
// Save results.
ResModules.push_back({It.first, std::move(MClone)});
}

return ResModules;
}

std::string makeResultFileName(Twine Ext, int I, StringRef Suffix) {
Expand Down Expand Up @@ -614,10 +615,12 @@ string_vector saveResultModules(const std::vector<ResultModule> &ResModules,
return Res;
}

string_vector saveDeviceImageProperty(
const std::vector<ResultModule> &ResultModules,
const std::map<StringRef, std::vector<const Function *>> &KernelModuleMap,
const ImagePropSaveInfo &ImgPSInfo) {
string_vector
saveDeviceImageProperty(const std::vector<ResultModule> &ResultModules,
const EntryPointsMap &EntryPointsSplitMap,
const ImagePropSaveInfo &ImgPSInfo) {
using PropSetRegTy = llvm::util::PropertySetRegistry;

string_vector Res;
legacy::PassManager GetSYCLDeviceLibReqMask;
auto *SDLReqMaskLegacyPass = new SYCLDeviceLibReqMaskPass();
Expand Down Expand Up @@ -679,8 +682,8 @@ string_vector saveDeviceImageProperty(
if (ImgPSInfo.EmitExportedSymbols) {
// For each result module, extract the exported functions
auto ModuleFunctionsIt =
KernelModuleMap.find(ResultModules[I].KernelModuleName);
if (ModuleFunctionsIt != KernelModuleMap.end()) {
EntryPointsSplitMap.find(ResultModules[I].SplitModuleId);
if (ModuleFunctionsIt != EntryPointsSplitMap.end()) {
for (const auto &F : ModuleFunctionsIt->second) {
if (F->getCallingConv() == CallingConv::SPIR_FUNC) {
PropSet[PropSetRegTy::SYCL_EXPORTED_SYMBOLS].insert(
Expand Down Expand Up @@ -804,28 +807,30 @@ TableFiles processOneModule(std::unique_ptr<Module> M, bool IsEsimd,
if (IsEsimd && LowerEsimd)
LowerEsimdConstructs(*M);

std::map<StringRef, std::vector<const Function *>> GlobalsSet;
EntryPointsMap GlobalsSet;

bool DoSplit = SplitMode.getNumOccurrences() > 0;

if (DoSplit || DoSymGen) {
KernelMapEntryScope Scope = selectDeviceCodeSplitScope(*M);
EntryPointsSplitScope Scope = selectDeviceCodeSplitScope(*M);
collectEntryPointToModuleMap(*M, GlobalsSet, Scope);
}

std::vector<ResultModule> ResultModules;
StringRef FileSuffix = IsEsimd ? "esimd_" : "";

bool DoSpecConst = SpecConstLower.getNumOccurrences() > 0;
bool SpecConstsMet = false;
bool SetSpecConstAtRT = DoSpecConst && (SpecConstLower == SC_USE_RT_VAL);
std::vector<ResultModule> ResultModules;

if (DoSplit)
splitModule(*M, GlobalsSet, ResultModules);
ResultModules = splitModule(*M, GlobalsSet);
// post-link always produces a code result, even if it is unmodified input
if (ResultModules.empty())
ResultModules.push_back({GLOBAL_SCOPE_NAME, std::move(M)});

bool DoSpecConst = SpecConstLower.getNumOccurrences() > 0;
bool SpecConstsMet = false;

if (DoSpecConst) {
bool SetSpecConstAtRT = (SpecConstLower == SC_USE_RT_VAL);
ModulePassManager RunSpecConst;
ModuleAnalysisManager MAM;
SpecConstantsPass SCP(SetSpecConstAtRT);
Expand All @@ -851,23 +856,22 @@ TableFiles processOneModule(std::unique_ptr<Module> M, bool IsEsimd,
// Reuse input module with only regular SYCL kernels if there were
// no spec constants and no splitting.
// We cannot reuse input module for ESIMD code since it was transformed.
bool CanReuseInputModule = !SpecConstsMet && (ResultModules.size() == 1) &&
!SyclAndEsimdCode && !IsEsimd &&
!IsLLVMUsedRemoved;
string_vector Files =
CanReuseInputModule
? string_vector{InputFilename}
: saveResultModules(ResultModules, IsEsimd ? "esimd_" : "");
bool CanReuseInputModule = !SyclAndEsimdCode && !IsEsimd &&
!IsLLVMUsedRemoved && !SpecConstsMet &&
(ResultModules.size() == 1);
string_vector Files = CanReuseInputModule
? string_vector{InputFilename}
: saveResultModules(ResultModules, FileSuffix);

// "Code" column is always output
std::copy(Files.begin(), Files.end(),
std::back_inserter(TblFiles[COL_CODE]));
}

{
ImagePropSaveInfo ImgPSInfo = {SetSpecConstAtRT, SpecConstsMet,
EmitKernelParamInfo, EmitProgramMetadata,
EmitExportedSymbols, IsEsimd};
ImagePropSaveInfo ImgPSInfo = {SpecConstsMet, EmitKernelParamInfo,
EmitProgramMetadata, EmitExportedSymbols,
IsEsimd};
string_vector Files =
saveDeviceImageProperty(ResultModules, GlobalsSet, ImgPSInfo);
std::copy(Files.begin(), Files.end(),
Expand All @@ -876,15 +880,14 @@ TableFiles processOneModule(std::unique_ptr<Module> M, bool IsEsimd,

if (DoSymGen) {
// extract symbols per each module
string_vector ResultSymbolsLists;
collectSymbolsLists(GlobalsSet, ResultSymbolsLists);
string_vector ResultSymbolsLists = collectSymbolsLists(GlobalsSet);
if (ResultSymbolsLists.empty()) {
// push empty symbols list for consistency
assert(ResultModules.size() == 1);
ResultSymbolsLists.push_back("");
}
string_vector Files =
saveResultSymbolsLists(ResultSymbolsLists, IsEsimd ? "esimd_" : "");
saveResultSymbolsLists(ResultSymbolsLists, FileSuffix);
std::copy(Files.begin(), Files.end(),
std::back_inserter(TblFiles[COL_SYM]));
}
Expand Down Expand Up @@ -917,14 +920,14 @@ ModulePair splitSyclEsimd(std::unique_ptr<Module> M) {
if (SyclFunctions.empty())
return std::make_pair(std::unique_ptr<Module>(nullptr), std::move(M));

// Key values in KernelModuleMap are not significant, but they define the
// order, in which entry points are processed in the splitModule function. The
// caller of the splitSyclEsimd function expects a pair of 1-Sycl and 2-Esimd
// modules, hence the strings names below.
std::map<StringRef, std::vector<const Function *>> KernelModuleMap(
// Key values in SyclEsimdEntryPointsMap are not significant, but they define
// the order, in which entry points are processed in the splitModule function.
// The caller of the splitSyclEsimd function expects a pair of 1-Sycl and
// 2-Esimd modules, hence the strings names below.
EntryPointsMap SyclEsimdEntryPointsMap(
{{"1-SYCL", SyclFunctions}, {"2-ESIMD", EsimdFunctions}});
std::vector<ResultModule> ResultModules;
splitModule(*M, KernelModuleMap, ResultModules);
std::vector<ResultModule> ResultModules =
splitModule(*M, SyclEsimdEntryPointsMap);
assert(ResultModules.size() == 2);
return std::make_pair(std::move(ResultModules[0].ModulePtr),
std::move(ResultModules[1].ModulePtr));
Expand Down Expand Up @@ -1057,20 +1060,17 @@ int main(int argc, char **argv) {
<< " -" << IROutputOnly.ArgStr << "\n";
return 1;
}

if (OutputFilename.getNumOccurrences() == 0)
OutputFilename = (Twine(sys::path::stem(InputFilename)) + ".files").str();

SMDiagnostic Err;
std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
// It is OK to use raw pointer here as we control that it does not outlive M
// or objects it is moved to
Module *MPtr = M.get();

if (!MPtr) {
if (!M) {
Err.print(argv[0], errs());
return 1;
}

if (OutputFilename.getNumOccurrences() == 0)
OutputFilename = (Twine(sys::path::stem(InputFilename)) + ".files").str();

TableFiles TblFiles = processInputModule(std::move(M));

// Input module was processed and a single output file was requested.
Expand Down