Skip to content
Merged
163 changes: 79 additions & 84 deletions llvm/tools/sycl-post-link/sycl-post-link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,33 +259,18 @@ struct IrPropSymFilenameTriple {
std::string Sym;
};

void writeToFile(const std::string &Filename, const std::string &Content) {
static void writeToFile(const StringRef Filename, const StringRef Content) {
std::error_code EC;
raw_fd_ostream OS{Filename, EC, sys::fs::OpenFlags::OF_None};
checkError(EC, "error opening the file '" + Filename + "'");
OS.write(Content.data(), Content.size());
OS.close();
}

// Creates a filename based on current output filename, given extension,
// sequential ID and suffix.
std::string makeResultFileName(Twine Ext, int I, StringRef Suffix) {
const StringRef Dir0 = OutputDir.getNumOccurrences() > 0
? OutputDir
: sys::path::parent_path(OutputFiles[0].Filename);
const StringRef Sep = sys::path::get_separator();
std::string Dir = Dir0.str();
if (!Dir0.empty() && !Dir0.ends_with(Sep))
Dir += Sep.str();
return (Dir + sys::path::stem(OutputFiles[0].Filename) + Suffix + "_" +
Twine(I) + Ext)
.str();
}

void saveModuleIR(Module &M, StringRef OutFilename) {
void saveModuleIR(Module &M, const StringRef Filename) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void saveModuleIR(Module &M, const StringRef Filename) {
void saveModuleIR(const Module &M, const StringRef Filename) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MPM.run would not accept a const reference.

std::error_code EC;
raw_fd_ostream Out{OutFilename, EC, sys::fs::OF_None};
checkError(EC, "error opening the file '" + OutFilename + "'");
raw_fd_ostream Out{Filename, EC, sys::fs::OF_None};
checkError(EC, "error opening the file '" + Filename + "'");

ModulePassManager MPM;
ModuleAnalysisManager MAM;
Expand All @@ -298,17 +283,9 @@ void saveModuleIR(Module &M, StringRef OutFilename) {
MPM.run(M, MAM);
}

std::string saveModuleIR(Module &M, int I, StringRef Suff) {
DUMP_ENTRY_POINTS(M, EmitOnlyKernelsAsEntryPoints, "saving IR");
StringRef FileExt = (OutputAssembly) ? ".ll" : ".bc";
std::string OutFilename = makeResultFileName(FileExt, I, Suff);
saveModuleIR(M, OutFilename);
return OutFilename;
}

std::string saveModuleProperties(module_split::ModuleDesc &MD,
const GlobalBinImageProps &GlobProps, int I,
StringRef Suff, StringRef Target = "") {
void saveModuleProperties(const module_split::ModuleDesc &MD,
const GlobalBinImageProps &GlobProps,
const StringRef Filename, StringRef Target = "") {

PropSetRegTy PropSet;

Expand All @@ -331,35 +308,21 @@ std::string saveModuleProperties(module_split::ModuleDesc &MD,
PropSet.remove(PropSetRegTy::SYCL_DEVICE_REQUIREMENTS,
PropSetRegTy::PROPERTY_REQD_WORK_GROUP_SIZE);

std::string NewSuff = Suff.str();
if (!Target.empty()) {
if (!Target.empty())
PropSet.add(PropSetRegTy::SYCL_DEVICE_REQUIREMENTS, "compile_target",
Target);
NewSuff += "_";
NewSuff += Target;
}

std::error_code EC;
std::string SCFile = makeResultFileName(".prop", I, NewSuff);
raw_fd_ostream SCOut(SCFile, EC);
checkError(EC, "error opening file '" + SCFile + "'");
raw_fd_ostream SCOut(Filename, EC);
checkError(EC, "error opening file '" + Filename + "'");
PropSet.write(SCOut);

return SCFile;
}

// Saves specified collection of symbols to a file.
std::string saveModuleSymbolTable(const module_split::ModuleDesc &MD, int I,
StringRef Suffix) {
auto SymT = computeModuleSymbolTable(MD.getModule(), MD.entries());
std::string OutFileName = makeResultFileName(".sym", I, Suffix);
writeToFile(OutFileName, SymT);
return OutFileName;
}

// Compute the filename suffix for the module
StringRef getModuleSuffix(const module_split::ModuleDesc &MD) {
return MD.isESIMD() ? "_esimd" : "";
void saveModuleSymbolTable(const module_split::ModuleDesc &MD,
const StringRef Filename) {
std::string SymT = computeModuleSymbolTable(MD.getModule(), MD.entries());
writeToFile(Filename, SymT);
}

bool isTargetCompatibleWithModule(const std::string &Target,
Expand All @@ -368,35 +331,41 @@ bool isTargetCompatibleWithModule(const std::string &Target,
void addTableRow(util::SimpleTable &Table,
const IrPropSymFilenameTriple &RowData);

// @param OutTables List of tables (one for each target) to output results
// @param MD Module descriptor to save
// @param IRFilename filename of already available IR component. If not empty,
// IR component saving is skipped, and this file name is recorded as such in
// the result.
void saveModule(std::vector<std::unique_ptr<util::SimpleTable>> &OutTables,
module_split::ModuleDesc &MD, int I, StringRef IRFilename) {
/// \param OutTables List of tables (one for each target) to output results
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a 'target'?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From some time ago we started support targets in sycl-post-link. Therefore, each target gets it's own table.
Links: #12727 and #12252

/// \param MD Module descriptor to save
/// \param OutputPrefix Prefix for all generated outputs.
/// \param IRFilename Filename of IR component. If filename is not empty, it
/// is recorded in the OutTable. Otherwise, a new file is created to save
/// the IR component, and the new name is recorded in the OutTable.
void saveModule(
const std::vector<std::unique_ptr<util::SimpleTable>> &OutTables,
module_split::ModuleDesc &MD, const int I, const Twine &OutputPrefix,
const StringRef IRFilename) {
IrPropSymFilenameTriple BaseTriple;
StringRef Suffix = getModuleSuffix(MD);
StringRef Suffix = MD.isESIMD() ? "_esimd" : "";
MD.saveSplitInformationAsMetadata();

if (!MD.isSYCLDeviceLib()) {
if (!IRFilename.empty()) {
// don't save IR, just record the filename
BaseTriple.Ir = IRFilename.str();
} else {
if (!IRFilename.empty()) {
// Don't save IR, just record the filename.
BaseTriple.Ir = IRFilename.str();
} else {
if (!MD.isSYCLDeviceLib()) {
// For deviceLib Modules, we don't need to do clean up and no entry-point
// is included. The module only includes a bunch of exported functions
// intended to be invoked by user's device modules.
MD.cleanup(AllowDeviceImageDependencies);
BaseTriple.Ir = saveModuleIR(MD.getModule(), I, Suffix);
}
} else {
// For deviceLib Modules, don't need to do clean up, no entry-point
// is included, the module only includes a bunch of exported functions
// intended to be invoked by user's device modules.
BaseTriple.Ir = saveModuleIR(MD.getModule(), I, Suffix);

StringRef IRExtension = OutputAssembly ? ".ll" : ".bc";
BaseTriple.Ir =
(OutputPrefix + Suffix + "_" + Twine(I) + IRExtension).str();
saveModuleIR(MD.getModule(), BaseTriple.Ir);
}

if (DoSymGen) {
// save the names of the entry points - the symbol table
BaseTriple.Sym = saveModuleSymbolTable(MD, I, Suffix);
// Save the names of the entry points - the symbol table.
BaseTriple.Sym = (OutputPrefix + Suffix + "_" + Twine(I) + ".sym").str();
saveModuleSymbolTable(MD, BaseTriple.Sym);
}

for (const auto &[Table, OutputFile] : zip_equal(OutTables, OutputFiles)) {
Expand All @@ -407,20 +376,31 @@ void saveModule(std::vector<std::unique_ptr<util::SimpleTable>> &OutTables,
GlobalBinImageProps Props = {EmitKernelParamInfo, EmitProgramMetadata,
EmitKernelNames, EmitExportedSymbols,
EmitImportedSymbols, DeviceGlobals};
StringRef Target = OutputFile.Target;
std::string NewSuff = Suffix.str();
if (!Target.empty())
NewSuff = (Twine("_") + Target).str();

CopyTriple.Prop =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious. Why do we not use DoPropGen flag here to guard 'saveModuleProperties'?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we do use the flag.

saveModuleProperties(MD, Props, I, Suffix, OutputFile.Target);
(OutputPrefix + NewSuff + "_" + Twine(I) + ".prop").str();
saveModuleProperties(MD, Props, CopyTriple.Prop, Target);
}
addTableRow(*Table, CopyTriple);
}
}

void saveDeviceLibModule(
std::vector<std::unique_ptr<util::SimpleTable>> &OutTables, int I,
const std::vector<std::unique_ptr<util::SimpleTable>> &OutTables,
const Twine &OutputPrefix, const int I,
const std::string &DeviceLibFileName) {
assert(!DeviceLibFileName.empty() &&
"DeviceLibFileName is expected to be non-empty.");
SMDiagnostic Err;
LLVMContext Context;
StringRef DeviceLibLoc = DeviceLibDir;
std::string DeviceLibPath = DeviceLibLoc.str() + "/" + DeviceLibFileName;
StringRef Sep = llvm::sys::path::get_separator();
std::string DeviceLibPath =
(DeviceLibLoc.str() + Sep + DeviceLibFileName).str();
std::unique_ptr<Module> DeviceLibIR =
parseIRFile(DeviceLibPath, Err, Context);
Module *DeviceLibMPtr = DeviceLibIR.get();
Expand All @@ -431,7 +411,7 @@ void saveDeviceLibModule(
}
llvm::module_split::ModuleDesc DeviceLibMD(std::move(DeviceLibIR),
DeviceLibFileName);
saveModule(OutTables, DeviceLibMD, I, DeviceLibFileName);
saveModule(OutTables, DeviceLibMD, I, OutputPrefix, "");
}

module_split::ModuleDesc link(module_split::ModuleDesc &&MD1,
Expand Down Expand Up @@ -623,7 +603,7 @@ bool isTargetCompatibleWithModule(const std::string &Target,
}

std::vector<std::unique_ptr<util::SimpleTable>>
processInputModule(std::unique_ptr<Module> M) {
processInputModule(std::unique_ptr<Module> M, const StringRef OutputPrefix) {
// Construct the resulting table which will accumulate all the outputs.
SmallVector<StringRef, MAX_COLUMNS_IN_FILE_TABLE> ColumnTitles{
StringRef(COL_CODE)};
Expand Down Expand Up @@ -728,7 +708,7 @@ processInputModule(std::unique_ptr<Module> M) {
}
for (module_split::ModuleDesc &IrMD : MMs) {
IsBF16DeviceLibUsed |= isSYCLDeviceLibBF16Used(IrMD.getModule());
saveModule(Tables, IrMD, ID, OutIRFileName);
saveModule(Tables, IrMD, ID, OutputPrefix, OutIRFileName);
}

++ID;
Expand All @@ -737,20 +717,35 @@ processInputModule(std::unique_ptr<Module> M) {
for (size_t i = 0; i != MMsWithDefaultSpecConsts.size(); ++i) {
module_split::ModuleDesc &IrMD = MMsWithDefaultSpecConsts[i];
IsBF16DeviceLibUsed |= isSYCLDeviceLibBF16Used(IrMD.getModule());
saveModule(Tables, IrMD, ID, OutIRFileName);
saveModule(Tables, IrMD, ID, OutputPrefix, OutIRFileName);
}

++ID;
}
}

if (IsBF16DeviceLibUsed && (DeviceLibDir.getNumOccurrences() > 0)) {
saveDeviceLibModule(Tables, ID, "libsycl-fallback-bfloat16.bc");
saveDeviceLibModule(Tables, ID + 1, "libsycl-native-bfloat16.bc");
saveDeviceLibModule(Tables, OutputPrefix, ID,
Copy link
Contributor

@asudarsa asudarsa Jul 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be cleaner to populate a global constant with the list of device library module filenames and use that here? Ideally, it should be passed in as an option to sycl-post-link.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could leave that for later.

"libsycl-fallback-bfloat16.bc");
saveDeviceLibModule(Tables, OutputPrefix, ID + 1,
"libsycl-native-bfloat16.bc");
}
return Tables;
}

/// Gets output prefix used for all output files from this tool.
std::string getOutputPrefix() {
StringRef Dir0 = OutputDir.getNumOccurrences() > 0
? OutputDir
: sys::path::parent_path(OutputFiles[0].Filename);
StringRef Sep = sys::path::get_separator();
std::string Dir = Dir0.str();
if (!Dir0.empty() && !Dir0.ends_with(Sep))
Dir += Sep.str();

return (Dir + sys::path::stem(OutputFiles[0].Filename)).str();
}

} // namespace

int main(int argc, char **argv) {
Expand Down Expand Up @@ -890,13 +885,13 @@ int main(int argc, char **argv) {
}

if (OutputFiles.getNumOccurrences() == 0) {
StringRef S =
IROutputOnly ? (OutputAssembly ? ".out.ll" : "out.bc") : ".files";
StringRef S = IROutputOnly ? ".out" : ".files";
OutputFiles.push_back({{}, (sys::path::stem(InputFilename) + S).str()});
}

std::string OutputPrefix = getOutputPrefix();
std::vector<std::unique_ptr<util::SimpleTable>> Tables =
processInputModule(std::move(M));
processInputModule(std::move(M), OutputPrefix);

// Input module was processed and a single output file was requested.
if (IROutputOnly)
Expand Down
Loading