Allow ocloc to link files

Added 'link' option to ocloc CLI, which allows linking of
several IR files to single output file. Supported formats
of output file are ELF and LLVM BC.

Related-To: NEO-6163
Signed-off-by: Patryk Wrobel <patryk.wrobel@intel.com>
This commit is contained in:
Patryk Wrobel
2022-01-12 15:27:20 +00:00
committed by Compute-Runtime-Automation
parent 504b49effa
commit 53482e6821
24 changed files with 1747 additions and 93 deletions

View File

@@ -1,5 +1,5 @@
#
# Copyright (C) 2020-2021 Intel Corporation
# Copyright (C) 2020-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
#
@@ -60,6 +60,7 @@ set(CLOC_LIB_SRCS_LIB
${OCLOC_DIRECTORY}/source/ocloc_api.h
${OCLOC_DIRECTORY}/source/ocloc_arg_helper.cpp
${OCLOC_DIRECTORY}/source/ocloc_arg_helper.h
${OCLOC_DIRECTORY}/source/ocloc_error_code.h
${OCLOC_DIRECTORY}/source/ocloc_fatbinary.cpp
${OCLOC_DIRECTORY}/source/ocloc_fatbinary.h
${OCLOC_DIRECTORY}/source/ocloc_validator.cpp
@@ -68,6 +69,8 @@ set(CLOC_LIB_SRCS_LIB
${OCLOC_DIRECTORY}/source/offline_compiler.h
${OCLOC_DIRECTORY}/source/offline_compiler_helper.cpp
${OCLOC_DIRECTORY}/source/offline_compiler_options.cpp
${OCLOC_DIRECTORY}/source/offline_linker.cpp
${OCLOC_DIRECTORY}/source/offline_linker.h
${OCLOC_DIRECTORY}/source/queries.h
${OCLOC_DIRECTORY}/source/utilities/get_git_version_info.h
${OCLOC_DIRECTORY}/source/utilities/get_git_version_info.cpp

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2021 Intel Corporation
* Copyright (C) 2019-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -7,6 +7,7 @@
#include "shared/offline_compiler/source/multi_command.h"
#include "shared/offline_compiler/source/ocloc_error_code.h"
#include "shared/offline_compiler/source/ocloc_fatbinary.h"
#include "shared/source/utilities/const_stringref.h"
@@ -14,13 +15,13 @@
namespace NEO {
int MultiCommand::singleBuild(const std::vector<std::string> &args) {
int retVal = OfflineCompiler::ErrorCode::SUCCESS;
int retVal = OclocErrorCode::SUCCESS;
if (requestedFatBinary(args, argHelper)) {
retVal = buildFatBinary(args, argHelper);
} else {
std::unique_ptr<OfflineCompiler> pCompiler{OfflineCompiler::create(args.size(), args, true, retVal, argHelper)};
if (retVal == OfflineCompiler::ErrorCode::SUCCESS) {
if (retVal == OclocErrorCode::SUCCESS) {
retVal = buildWithSafetyGuard(pCompiler.get());
std::string &buildLog = pCompiler->getBuildLog();
@@ -30,14 +31,14 @@ int MultiCommand::singleBuild(const std::vector<std::string> &args) {
}
outFileName += ".bin";
}
if (retVal == OfflineCompiler::ErrorCode::SUCCESS) {
if (retVal == OclocErrorCode::SUCCESS) {
if (!quiet)
argHelper->printf("Build succeeded.\n");
} else {
argHelper->printf("Build failed with error code: %d\n", retVal);
}
if (retVal == OfflineCompiler::ErrorCode::SUCCESS) {
if (retVal == OclocErrorCode::SUCCESS) {
outputFile << getCurrentDirectoryOwn(outDirForBuilds) + outFileName;
} else {
outputFile << "Unsuccesful build";
@@ -48,7 +49,7 @@ int MultiCommand::singleBuild(const std::vector<std::string> &args) {
}
MultiCommand *MultiCommand::create(const std::vector<std::string> &args, int &retVal, OclocArgHelper *helper) {
retVal = OfflineCompiler::ErrorCode::SUCCESS;
retVal = OclocErrorCode::SUCCESS;
auto pMultiCommand = new MultiCommand();
if (pMultiCommand) {
@@ -56,7 +57,7 @@ MultiCommand *MultiCommand::create(const std::vector<std::string> &args, int &re
retVal = pMultiCommand->initialize(args);
}
if (retVal != OfflineCompiler::ErrorCode::SUCCESS) {
if (retVal != OclocErrorCode::SUCCESS) {
delete pMultiCommand;
pMultiCommand = nullptr;
}
@@ -107,7 +108,7 @@ int MultiCommand::initialize(const std::vector<std::string> &args) {
} else {
argHelper->printf("Invalid option (arg %zu): %s\n", argIndex, currArg.c_str());
printHelp();
return OfflineCompiler::ErrorCode::INVALID_COMMAND_LINE;
return OclocErrorCode::INVALID_COMMAND_LINE;
}
}
@@ -116,11 +117,11 @@ int MultiCommand::initialize(const std::vector<std::string> &args) {
argHelper->readFileToVectorOfStrings(pathToCommandFile, lines);
if (lines.empty()) {
argHelper->printf("Command file was empty.\n");
return OfflineCompiler::ErrorCode::INVALID_FILE;
return OclocErrorCode::INVALID_FILE;
}
} else {
argHelper->printf("Could not find/open file with builds argument.s\n");
return OfflineCompiler::ErrorCode::INVALID_FILE;
return OclocErrorCode::INVALID_FILE;
}
runBuilds(args[0]);
@@ -136,7 +137,7 @@ void MultiCommand::runBuilds(const std::string &argZero) {
std::vector<std::string> args = {argZero};
int retVal = splitLineInSeparateArgs(args, lines[i], i);
if (retVal != OfflineCompiler::ErrorCode::SUCCESS) {
if (retVal != OclocErrorCode::SUCCESS) {
retValues.push_back(retVal);
continue;
}
@@ -188,22 +189,22 @@ int MultiCommand::splitLineInSeparateArgs(std::vector<std::string> &qargs, const
}
if (end == std::string::npos) {
argHelper->printf("One of the quotes is open in build number %zu\n", numberOfBuild + 1);
return OfflineCompiler::ErrorCode::INVALID_FILE;
return OclocErrorCode::INVALID_FILE;
}
argLen = end - start;
i = end;
qargs.push_back(commandsLine.substr(start, argLen));
}
return OfflineCompiler::ErrorCode::SUCCESS;
return OclocErrorCode::SUCCESS;
}
int MultiCommand::showResults() {
int retValue = OfflineCompiler::ErrorCode::SUCCESS;
int retValue = OclocErrorCode::SUCCESS;
int indexRetVal = 0;
for (int retVal : retValues) {
retValue |= retVal;
if (!quiet) {
if (retVal != OfflineCompiler::ErrorCode::SUCCESS) {
if (retVal != OclocErrorCode::SUCCESS) {
argHelper->printf("Build command %d: failed. Error code: %d\n", indexRetVal, retVal);
} else {
argHelper->printf("Build command %d: successful\n", indexRetVal);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Intel Corporation
* Copyright (C) 2020-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -10,9 +10,11 @@
#include "shared/offline_compiler/source/decoder/binary_decoder.h"
#include "shared/offline_compiler/source/decoder/binary_encoder.h"
#include "shared/offline_compiler/source/multi_command.h"
#include "shared/offline_compiler/source/ocloc_error_code.h"
#include "shared/offline_compiler/source/ocloc_fatbinary.h"
#include "shared/offline_compiler/source/ocloc_validator.h"
#include "shared/offline_compiler/source/offline_compiler.h"
#include "shared/offline_compiler/source/offline_linker.h"
#include <iostream>
@@ -36,6 +38,7 @@ Use 'ocloc <command> --help' to get help about specific command.
Commands:
compile Compiles input to Intel Compute GPU device binary.
link Links several IR files.
disasm Disassembles Intel Compute GPU device binary.
asm Assembles Intel Compute GPU device binary.
multi Compiles multiple files using a config file.
@@ -48,6 +51,9 @@ Examples:
Compile file to Intel Compute GPU device binary (out = source_file_Gen9core.bin)
ocloc -file source_file.cl -device skl
Link two SPIR-V files.
ocloc link -file sample1.spv -file sample2.spv -out_format LLVM_BC -out samples_merged.llvm_bc
Disassemble Intel Compute GPU device binary
ocloc disasm -file source_file_Gen9core.bin
@@ -108,7 +114,7 @@ int oclocInvoke(unsigned int numArgs, const char *argv[],
try {
if (numArgs == 1 || (numArgs > 1 && (ConstStringRef("-h") == allArgs[1] || ConstStringRef("--help") == allArgs[1]))) {
helper->printf("%s", help);
return OfflineCompiler::ErrorCode::SUCCESS;
return OclocErrorCode::SUCCESS;
} else if (numArgs > 1 && ConstStringRef("disasm") == allArgs[1]) {
BinaryDecoder disasm(helper.get());
int retVal = disasm.validateInput(allArgs);
@@ -138,7 +144,7 @@ int oclocInvoke(unsigned int numArgs, const char *argv[],
return retVal;
}
} else if (numArgs > 1 && ConstStringRef("multi") == allArgs[1]) {
int retValue = OfflineCompiler::ErrorCode::SUCCESS;
int retValue = OclocErrorCode::SUCCESS;
std::unique_ptr<MultiCommand> pMulti{(MultiCommand::create(allArgs, retValue, helper.get()))};
return retValue;
} else if (requestedFatBinary(allArgs, helper.get())) {
@@ -147,11 +153,26 @@ int oclocInvoke(unsigned int numArgs, const char *argv[],
return NEO::Ocloc::validate(allArgs, helper.get());
} else if (numArgs > 1 && ConstStringRef("query") == allArgs[1]) {
return OfflineCompiler::query(numArgs, allArgs, helper.get());
} else if (numArgs > 1 && ConstStringRef("link") == allArgs[1]) {
int createResult{OclocErrorCode::SUCCESS};
const auto linker{OfflineLinker::create(numArgs, allArgs, createResult, helper.get())};
const auto linkingResult{linkWithSafetyGuard(linker.get())};
const auto buildLog = linker->getBuildLog();
if (!buildLog.empty()) {
helper->printf("%s\n", buildLog.c_str());
}
if (createResult == OclocErrorCode::SUCCESS && linkingResult == OclocErrorCode::SUCCESS) {
helper->printf("Linker execution has succeeded!\n");
}
return createResult | linkingResult;
} else {
int retVal = OfflineCompiler::ErrorCode::SUCCESS;
int retVal = OclocErrorCode::SUCCESS;
std::unique_ptr<OfflineCompiler> pCompiler{OfflineCompiler::create(numArgs, allArgs, true, retVal, helper.get())};
if (retVal == OfflineCompiler::ErrorCode::SUCCESS) {
if (retVal == OclocErrorCode::SUCCESS) {
retVal = buildWithSafetyGuard(pCompiler.get());
std::string buildLog = pCompiler->getBuildLog();
@@ -159,7 +180,7 @@ int oclocInvoke(unsigned int numArgs, const char *argv[],
helper->printf("%s\n", buildLog.c_str());
}
if (retVal == OfflineCompiler::ErrorCode::SUCCESS) {
if (retVal == OclocErrorCode::SUCCESS) {
if (!pCompiler->isQuiet())
helper->printf("Build succeeded.\n");
} else {
@@ -167,7 +188,7 @@ int oclocInvoke(unsigned int numArgs, const char *argv[],
}
}
if (retVal != OfflineCompiler::ErrorCode::SUCCESS) {
if (retVal != OclocErrorCode::SUCCESS) {
printOclocOptionsReadFromFile(pCompiler.get());
printOclocCmdLine(numArgs, argv, helper);
}

View File

@@ -132,7 +132,7 @@ class OclocArgHelper {
bool isFatbinary() {
return fatBinary;
}
void saveOutput(const std::string &filename, const void *pData, const size_t &dataSize);
MOCKABLE_VIRTUAL void saveOutput(const std::string &filename, const void *pData, const size_t &dataSize);
void saveOutput(const std::string &filename, const std::ostream &stream);
MessagePrinter &getPrinterRef() { return messagePrinter; }

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#pragma once
namespace NEO::OclocErrorCode {
enum {
SUCCESS = 0,
OUT_OF_HOST_MEMORY = -6,
BUILD_PROGRAM_FAILURE = -11,
INVALID_DEVICE = -33,
INVALID_PROGRAM = -44,
INVALID_COMMAND_LINE = -5150,
INVALID_FILE = -5151,
};
} // namespace NEO::OclocErrorCode

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Intel Corporation
* Copyright (C) 2020-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -7,6 +7,7 @@
#include "shared/offline_compiler/source/ocloc_fatbinary.h"
#include "shared/offline_compiler/source/ocloc_error_code.h"
#include "shared/offline_compiler/source/utilities/safety_caller.h"
#include "shared/source/helpers/file_io.h"
#include "shared/source/helpers/hw_info.h"
@@ -378,7 +379,7 @@ int buildFatBinary(const std::vector<std::string> &args, OclocArgHelper *argHelp
argsCopy[deviceArgIndex] = targetPlatform.str();
std::unique_ptr<OfflineCompiler> pCompiler{OfflineCompiler::create(argsCopy.size(), argsCopy, false, retVal, argHelper)};
if (OfflineCompiler::ErrorCode::SUCCESS != retVal) {
if (OclocErrorCode::SUCCESS != retVal) {
argHelper->printf("Error! Couldn't create OfflineCompiler. Exiting.\n");
return retVal;
}
@@ -402,7 +403,7 @@ int buildFatBinary(const std::vector<std::string> &args, OclocArgHelper *argHelp
argHelper->setFatbinary(true);
argHelper->setDeviceInfoForFatbinaryTarget(targetConfig);
std::unique_ptr<OfflineCompiler> pCompiler{OfflineCompiler::create(argsCopy.size(), argsCopy, false, retVal, argHelper)};
if (OfflineCompiler::ErrorCode::SUCCESS != retVal) {
if (OclocErrorCode::SUCCESS != retVal) {
argHelper->printf("Error! Couldn't create OfflineCompiler. Exiting.\n");
return retVal;
}

View File

@@ -7,6 +7,7 @@
#include "offline_compiler.h"
#include "shared/offline_compiler/source/ocloc_error_code.h"
#include "shared/offline_compiler/source/queries.h"
#include "shared/offline_compiler/source/utilities/get_git_version_info.h"
#include "shared/source/compiler_interface/intermediate_representations.h"
@@ -50,6 +51,8 @@
#define GetCurrentWorkingDirectory getcwd
#endif
using namespace NEO::OclocErrorCode;
namespace NEO {
CIF::CIFMain *createMainNoSanitize(CIF::CreateCIFMainFunc_t createFunc);

View File

@@ -33,16 +33,6 @@ std::string getDevicesTypes();
class OfflineCompiler {
public:
enum ErrorCode {
SUCCESS = 0,
OUT_OF_HOST_MEMORY = -6,
BUILD_PROGRAM_FAILURE = -11,
INVALID_DEVICE = -33,
INVALID_PROGRAM = -44,
INVALID_COMMAND_LINE = -5150,
INVALID_FILE = -5151,
};
static int query(size_t numArgs, const std::vector<std::string> &allArgs, OclocArgHelper *helper);
static OfflineCompiler *create(size_t numArgs, const std::vector<std::string> &allArgs, bool dumpFiles, int &retVal, OclocArgHelper *helper);

View File

@@ -0,0 +1,409 @@
/*
* Copyright (C) 2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "offline_linker.h"
#include "shared/offline_compiler/source/ocloc_arg_helper.h"
#include "shared/offline_compiler/source/ocloc_error_code.h"
#include "shared/source/compiler_interface/intermediate_representations.h"
#include "shared/source/device_binary_format/elf/elf_encoder.h"
#include "shared/source/device_binary_format/elf/ocl_elf.h"
#include "shared/source/helpers/compiler_hw_info_config.h"
#include "shared/source/helpers/string.h"
#include "shared/source/os_interface/os_inc_base.h"
#include "shared/source/os_interface/os_library.h"
#include "cif/common/cif_main.h"
#include "ocl_igc_interface/platform_helper.h"
#include <algorithm>
#include <array>
namespace NEO {
CIF::CIFMain *createMainNoSanitize(CIF::CreateCIFMainFunc_t createFunc);
std::unique_ptr<OfflineLinker> OfflineLinker::create(size_t argsCount, const std::vector<std::string> &args, int &errorCode, OclocArgHelper *argHelper) {
std::unique_ptr<OfflineLinker> linker{new OfflineLinker{argHelper}};
errorCode = linker->initialize(argsCount, args);
return linker;
}
OfflineLinker::OfflineLinker(OclocArgHelper *argHelper)
: argHelper{argHelper}, operationMode{OperationMode::SKIP_EXECUTION}, outputFilename{"linker_output"}, outputFormat{IGC::CodeType::llvmBc} {}
OfflineLinker::~OfflineLinker() = default;
int OfflineLinker::initialize(size_t argsCount, const std::vector<std::string> &args) {
const auto parsingResult{parseCommand(argsCount, args)};
if (parsingResult != OclocErrorCode::SUCCESS) {
return parsingResult;
}
// If a user requested help, then stop here.
if (operationMode == OperationMode::SHOW_HELP) {
return OclocErrorCode::SUCCESS;
}
const auto verificationResult{verifyLinkerCommand()};
if (verificationResult != OclocErrorCode::SUCCESS) {
return verificationResult;
}
const auto loadingResult{loadInputFilesContent()};
if (loadingResult != OclocErrorCode::SUCCESS) {
return loadingResult;
}
const auto hwInfoInitializationResult{initHardwareInfo()};
if (hwInfoInitializationResult != OclocErrorCode::SUCCESS) {
return hwInfoInitializationResult;
}
const auto igcPreparationResult{prepareIgc()};
if (igcPreparationResult != OclocErrorCode::SUCCESS) {
return igcPreparationResult;
}
operationMode = OperationMode::LINK_FILES;
return OclocErrorCode::SUCCESS;
}
int OfflineLinker::parseCommand(size_t argsCount, const std::vector<std::string> &args) {
if (argsCount < 2u) {
operationMode = OperationMode::SHOW_HELP;
return OclocErrorCode::INVALID_COMMAND_LINE;
}
for (size_t argIndex = 1u; argIndex < argsCount; ++argIndex) {
const auto &currentArg{args[argIndex]};
const auto hasMoreArgs{argIndex + 1 < argsCount};
if (currentArg == "link") {
continue;
} else if ((currentArg == "-file") && hasMoreArgs) {
inputFilenames.push_back(args[argIndex + 1]);
++argIndex;
} else if (currentArg == "-out" && hasMoreArgs) {
outputFilename = args[argIndex + 1];
++argIndex;
} else if ((currentArg == "-out_format") && hasMoreArgs) {
outputFormat = parseOutputFormat(args[argIndex + 1]);
++argIndex;
} else if ((currentArg == "-options") && hasMoreArgs) {
options = args[argIndex + 1];
++argIndex;
} else if ((currentArg == "-internal_options") && hasMoreArgs) {
internalOptions = args[argIndex + 1];
++argIndex;
} else if (currentArg == "--help") {
operationMode = OperationMode::SHOW_HELP;
return OclocErrorCode::SUCCESS;
} else {
argHelper->printf("Invalid option (arg %zd): %s\n", argIndex, currentArg.c_str());
return OclocErrorCode::INVALID_COMMAND_LINE;
}
}
return OclocErrorCode::SUCCESS;
}
IGC::CodeType::CodeType_t OfflineLinker::parseOutputFormat(const std::string &outputFormatName) {
constexpr static std::array supportedFormatNames = {
std::pair{"ELF", IGC::CodeType::elf},
std::pair{"LLVM_BC", IGC::CodeType::llvmBc}};
for (const auto &[name, format] : supportedFormatNames) {
if (name == outputFormatName) {
return format;
}
}
return IGC::CodeType::invalid;
}
int OfflineLinker::verifyLinkerCommand() {
if (inputFilenames.empty()) {
argHelper->printf("Error: Input name is missing! At least one input file is required!\n");
return OclocErrorCode::INVALID_COMMAND_LINE;
}
for (const auto &filename : inputFilenames) {
if (filename.empty()) {
argHelper->printf("Error: Empty filename cannot be used!\n");
return OclocErrorCode::INVALID_COMMAND_LINE;
}
if (!argHelper->fileExists(filename)) {
argHelper->printf("Error: Input file %s missing.\n", filename.c_str());
return OclocErrorCode::INVALID_FILE;
}
}
if (outputFormat == IGC::CodeType::invalid) {
argHelper->printf("Error: Invalid output type!\n");
return OclocErrorCode::INVALID_COMMAND_LINE;
}
return OclocErrorCode::SUCCESS;
}
int OfflineLinker::loadInputFilesContent() {
std::unique_ptr<char[]> bytes{};
size_t size{};
IGC::CodeType::CodeType_t codeType{};
inputFilesContent.reserve(inputFilenames.size());
for (const auto &filename : inputFilenames) {
size = 0;
bytes = argHelper->loadDataFromFile(filename, size);
if (bytes == nullptr || size == 0) {
argHelper->printf("Error: Cannot read input file: %s\n", filename.c_str());
return OclocErrorCode::INVALID_FILE;
}
codeType = detectCodeType(bytes.get(), size);
if (codeType == IGC::CodeType::invalid) {
argHelper->printf("Error: Unsupported format of input file: %s\n", filename.c_str());
return OclocErrorCode::INVALID_PROGRAM;
}
inputFilesContent.emplace_back(std::move(bytes), size, codeType);
}
return OclocErrorCode::SUCCESS;
}
IGC::CodeType::CodeType_t OfflineLinker::detectCodeType(char *bytes, size_t size) const {
const auto bytesArray = ArrayRef<const uint8_t>::fromAny(bytes, size);
if (isSpirVBitcode(bytesArray)) {
return IGC::CodeType::spirV;
}
if (isLlvmBitcode(bytesArray)) {
return IGC::CodeType::llvmBc;
}
return IGC::CodeType::invalid;
}
int OfflineLinker::initHardwareInfo() {
// In spite of linking input files to intermediate representation instead of native binaries,
// we have to initialize hardware info. Without that, initialization of IGC fails.
// Therefore, we select the first valid hardware info entry and use it.
const auto hwInfoTable{getHardwareInfoTable()};
for (auto productId = 0u; productId < hwInfoTable.size(); ++productId) {
if (hwInfoTable[productId]) {
hwInfo = *hwInfoTable[productId];
const auto hwInfoConfig = defaultHardwareInfoConfigTable[hwInfo.platform.eProductFamily];
setHwInfoValuesFromConfig(hwInfoConfig, hwInfo);
hardwareInfoSetup[hwInfo.platform.eProductFamily](&hwInfo, true, hwInfoConfig);
return OclocErrorCode::SUCCESS;
}
}
argHelper->printf("Error! Cannot retrieve any valid hardware information!\n");
return OclocErrorCode::INVALID_DEVICE;
}
ArrayRef<const HardwareInfo *> OfflineLinker::getHardwareInfoTable() const {
return {hardwareInfoTable};
}
int OfflineLinker::prepareIgc() {
igcLib = loadIgcLibrary();
if (!igcLib) {
argHelper->printf("Error! Loading of IGC library has failed! Filename: %s\n", Os::igcDllName);
return OclocErrorCode::OUT_OF_HOST_MEMORY;
}
const auto igcCreateMainFunction = loadCreateIgcMainFunction();
if (!igcCreateMainFunction) {
argHelper->printf("Error! Cannot load required functions from IGC library.\n");
return OclocErrorCode::OUT_OF_HOST_MEMORY;
}
igcMain = createIgcMain(igcCreateMainFunction);
if (!igcMain) {
argHelper->printf("Error! Cannot create IGC main component!\n");
return OclocErrorCode::OUT_OF_HOST_MEMORY;
}
igcDeviceCtx = createIgcDeviceContext();
if (!igcDeviceCtx) {
argHelper->printf("Error! Cannot create IGC device context!\n");
return OclocErrorCode::OUT_OF_HOST_MEMORY;
}
const auto igcPlatform = getIgcPlatformHandle();
const auto igcGtSystemInfo = getGTSystemInfoHandle();
if (!igcPlatform || !igcGtSystemInfo) {
argHelper->printf("Error! IGC device context has not been properly created!\n");
return OclocErrorCode::OUT_OF_HOST_MEMORY;
}
IGC::PlatformHelper::PopulateInterfaceWith(*igcPlatform.get(), hwInfo.platform);
IGC::GtSysInfoHelper::PopulateInterfaceWith(*igcGtSystemInfo.get(), hwInfo.gtSystemInfo);
return OclocErrorCode::SUCCESS;
}
std::unique_ptr<OsLibrary> OfflineLinker::loadIgcLibrary() const {
return std::unique_ptr<OsLibrary>{OsLibrary::load(Os::igcDllName)};
}
CIF::CreateCIFMainFunc_t OfflineLinker::loadCreateIgcMainFunction() const {
return reinterpret_cast<CIF::CreateCIFMainFunc_t>(igcLib->getProcAddress(CIF::CreateCIFMainFuncName));
}
CIF::RAII::UPtr_t<CIF::CIFMain> OfflineLinker::createIgcMain(CIF::CreateCIFMainFunc_t createMainFunction) const {
return CIF::RAII::UPtr(createMainNoSanitize(createMainFunction));
}
CIF::RAII::UPtr_t<IGC::IgcOclDeviceCtxTagOCL> OfflineLinker::createIgcDeviceContext() const {
return igcMain->CreateInterface<IGC::IgcOclDeviceCtxTagOCL>();
}
CIF::RAII::UPtr_t<IGC::PlatformTagOCL> OfflineLinker::getIgcPlatformHandle() const {
return igcDeviceCtx->GetPlatformHandle();
}
CIF::RAII::UPtr_t<IGC::GTSystemInfoTagOCL> OfflineLinker::getGTSystemInfoHandle() const {
return igcDeviceCtx->GetGTSystemInfoHandle();
}
int OfflineLinker::execute() {
switch (operationMode) {
case OperationMode::SHOW_HELP:
return showHelp();
case OperationMode::LINK_FILES:
return link();
case OperationMode::SKIP_EXECUTION:
[[fallthrough]];
default:
argHelper->printf("Error: Linker cannot be executed due to unsuccessful initialization!\n");
return OclocErrorCode::INVALID_COMMAND_LINE;
}
}
int OfflineLinker::showHelp() {
constexpr auto help{R"===(Links several IR files to selected output format (LLVM BC, ELF).
Input files can be given in SPIR-V or LLVM BC.
Usage: ocloc link [-file <filename>]... -out <filename> [-out_format <format>] [-options <options>] [-internal_options <options>] [--help]
-file <filename> The input file to be linked.
Multiple files can be passed using repetition of this arguments.
Please see examples below.
-out <filename> Output filename.
-out_format <format> Output file format. Supported ones are ELF and LLVM_BC.
When not specified, LLVM_BC is used.
-options <options> Optional OpenCL C compilation options
as defined by OpenCL specification.
-internal_options <options> Optional compiler internal options
as defined by compilers used underneath.
Check intel-graphics-compiler (IGC) project
for details on available internal options.
You also may provide explicit --help to inquire
information about option, mentioned in -options.
--help Print this usage message.
Examples:
Link two SPIR-V files to LLVM BC output
ocloc link -file first_file.spv -file second_file.spv -out linker_output.llvmbc
Link two LLVM BC files to ELF output
ocloc link -file first_file.llvmbc -file second_file.llvmbc -out_format ELF -out translated.elf
)==="};
argHelper->printf(help);
return OclocErrorCode::SUCCESS;
}
int OfflineLinker::link() {
const auto encodedElfFile{createSingleInputFile()};
if (outputFormat == IGC::CodeType::elf) {
argHelper->saveOutput(outputFilename, encodedElfFile.data(), encodedElfFile.size());
return OclocErrorCode::SUCCESS;
}
const auto [translationResult, translatedBitcode] = translateToOutputFormat(encodedElfFile);
if (translationResult == OclocErrorCode::SUCCESS) {
argHelper->saveOutput(outputFilename, translatedBitcode.data(), translatedBitcode.size());
}
return translationResult;
}
std::vector<uint8_t> OfflineLinker::createSingleInputFile() const {
NEO::Elf::ElfEncoder<> elfEncoder{true, false, 1U};
elfEncoder.getElfFileHeader().type = Elf::ET_OPENCL_OBJECTS;
for (const auto &[bytes, size, codeType] : inputFilesContent) {
const auto isSpirv = codeType == IGC::CodeType::spirV;
const auto sectionType = isSpirv ? Elf::SHT_OPENCL_SPIRV : Elf::SHT_OPENCL_LLVM_BINARY;
const auto sectionName = isSpirv ? Elf::SectionNamesOpenCl::spirvObject : Elf::SectionNamesOpenCl::llvmObject;
const auto bytesArray = ArrayRef<const uint8_t>::fromAny(bytes.get(), size);
elfEncoder.appendSection(sectionType, sectionName, bytesArray);
}
return elfEncoder.encode();
}
std::pair<int, std::vector<uint8_t>> OfflineLinker::translateToOutputFormat(const std::vector<uint8_t> &elfInput) {
auto igcSrc = CIF::Builtins::CreateConstBuffer(igcMain.get(), elfInput.data(), elfInput.size());
auto igcOptions = CIF::Builtins::CreateConstBuffer(igcMain.get(), options.c_str(), options.size());
auto igcInternalOptions = CIF::Builtins::CreateConstBuffer(igcMain.get(), internalOptions.c_str(), internalOptions.size());
auto igcTranslationCtx = igcDeviceCtx->CreateTranslationCtx(IGC::CodeType::elf, outputFormat);
const auto tracingOptions{nullptr};
const auto tracingOptionsSize{0};
const auto igcOutput = igcTranslationCtx->Translate(igcSrc.get(), igcOptions.get(), igcInternalOptions.get(), tracingOptions, tracingOptionsSize);
std::vector<uint8_t> outputFileContent{};
if (!igcOutput) {
argHelper->printf("Error: Translation has failed! IGC output is nullptr!\n");
return {OclocErrorCode::OUT_OF_HOST_MEMORY, std::move(outputFileContent)};
}
if (igcOutput->GetOutput()->GetSizeRaw() != 0) {
outputFileContent.resize(igcOutput->GetOutput()->GetSizeRaw());
memcpy_s(outputFileContent.data(), outputFileContent.size(), igcOutput->GetOutput()->GetMemory<char>(), igcOutput->GetOutput()->GetSizeRaw());
}
tryToStoreBuildLog(igcOutput->GetBuildLog()->GetMemory<char>(), igcOutput->GetBuildLog()->GetSizeRaw());
const auto errorCode{igcOutput->Successful() ? OclocErrorCode::SUCCESS : OclocErrorCode::BUILD_PROGRAM_FAILURE};
if (errorCode != OclocErrorCode::SUCCESS) {
argHelper->printf("Error: Translation has failed! IGC returned empty output.\n");
}
return {errorCode, std::move(outputFileContent)};
}
std::string OfflineLinker::getBuildLog() const {
return buildLog;
}
void OfflineLinker::tryToStoreBuildLog(const char *buildLogRaw, size_t size) {
if (buildLogRaw && size != 0) {
buildLog = std::string{buildLogRaw, buildLogRaw + size};
}
}
} // namespace NEO

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#pragma once
#include "shared/source/helpers/hw_info.h"
#include "shared/source/utilities/arrayref.h"
#include "cif/common/cif_main.h"
#include "cif/import/library_api.h"
#include "ocl_igc_interface/code_type.h"
#include "ocl_igc_interface/igc_ocl_device_ctx.h"
#include <cstddef>
#include <memory>
#include <string>
#include <utility>
#include <vector>
class OclocArgHelper;
namespace NEO {
class OsLibrary;
class OfflineLinker {
protected:
enum class OperationMode {
SKIP_EXECUTION = 0,
SHOW_HELP = 1,
LINK_FILES = 2,
};
struct InputFileContent {
InputFileContent(std::unique_ptr<char[]> bytes, size_t size, IGC::CodeType::CodeType_t codeType)
: bytes{std::move(bytes)}, size{size}, codeType{codeType} {}
std::unique_ptr<char[]> bytes{};
size_t size{};
IGC::CodeType::CodeType_t codeType{};
};
public:
static std::unique_ptr<OfflineLinker> create(size_t argsCount, const std::vector<std::string> &args, int &errorCode, OclocArgHelper *argHelper);
MOCKABLE_VIRTUAL ~OfflineLinker();
int execute();
std::string getBuildLog() const;
protected:
explicit OfflineLinker(OclocArgHelper *argHelper);
int initialize(size_t argsCount, const std::vector<std::string> &args);
int parseCommand(size_t argsCount, const std::vector<std::string> &args);
IGC::CodeType::CodeType_t parseOutputFormat(const std::string &outputFormatName);
int verifyLinkerCommand();
int loadInputFilesContent();
IGC::CodeType::CodeType_t detectCodeType(char *bytes, size_t size) const;
int initHardwareInfo();
int prepareIgc();
int link();
int showHelp();
std::vector<uint8_t> createSingleInputFile() const;
std::pair<int, std::vector<uint8_t>> translateToOutputFormat(const std::vector<uint8_t> &elfInput);
void tryToStoreBuildLog(const char *buildLogRaw, size_t size);
MOCKABLE_VIRTUAL ArrayRef<const HardwareInfo *> getHardwareInfoTable() const;
MOCKABLE_VIRTUAL std::unique_ptr<OsLibrary> loadIgcLibrary() const;
MOCKABLE_VIRTUAL CIF::CreateCIFMainFunc_t loadCreateIgcMainFunction() const;
MOCKABLE_VIRTUAL CIF::RAII::UPtr_t<CIF::CIFMain> createIgcMain(CIF::CreateCIFMainFunc_t createMainFunction) const;
MOCKABLE_VIRTUAL CIF::RAII::UPtr_t<IGC::IgcOclDeviceCtxTagOCL> createIgcDeviceContext() const;
MOCKABLE_VIRTUAL CIF::RAII::UPtr_t<IGC::PlatformTagOCL> getIgcPlatformHandle() const;
MOCKABLE_VIRTUAL CIF::RAII::UPtr_t<IGC::GTSystemInfoTagOCL> getGTSystemInfoHandle() const;
OclocArgHelper *argHelper{};
OperationMode operationMode{};
std::vector<std::string> inputFilenames{};
std::vector<InputFileContent> inputFilesContent{};
std::string outputFilename{};
IGC::CodeType::CodeType_t outputFormat{};
std::string options{};
std::string internalOptions{};
std::unique_ptr<OsLibrary> igcLib{};
CIF::RAII::UPtr_t<CIF::CIFMain> igcMain{};
CIF::RAII::UPtr_t<IGC::IgcOclDeviceCtxTagOCL> igcDeviceCtx{};
HardwareInfo hwInfo{};
std::string buildLog{};
};
} // namespace NEO

View File

@@ -1,11 +1,12 @@
/*
* Copyright (C) 2020-2021 Intel Corporation
* Copyright (C) 2020-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/offline_compiler/source/offline_compiler.h"
#include "shared/offline_compiler/source/offline_linker.h"
#include "shared/offline_compiler/source/utilities/linux/safety_guard_linux.h"
#include "shared/source/os_interface/os_library.h"
@@ -17,3 +18,10 @@ int buildWithSafetyGuard(OfflineCompiler *compiler) {
return safetyGuard.call<int, OfflineCompiler, decltype(&OfflineCompiler::build)>(compiler, &OfflineCompiler::build, retVal);
}
int linkWithSafetyGuard(OfflineLinker *linker) {
SafetyGuardLinux safetyGuard{};
int returnValueOnCrash{-1};
return safetyGuard.call(linker, &OfflineLinker::execute, returnValueOnCrash);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2020 Intel Corporation
* Copyright (C) 2018-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -8,6 +8,8 @@
#pragma once
namespace NEO {
class OfflineCompiler;
}
class OfflineLinker;
} // namespace NEO
extern int buildWithSafetyGuard(NEO::OfflineCompiler *compiler);
extern int buildWithSafetyGuard(NEO::OfflineCompiler *compiler);
extern int linkWithSafetyGuard(NEO::OfflineLinker *linker);

View File

@@ -1,11 +1,12 @@
/*
* Copyright (C) 2020-2021 Intel Corporation
* Copyright (C) 2020-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/offline_compiler/source/offline_compiler.h"
#include "shared/offline_compiler/source/offline_linker.h"
#include "shared/offline_compiler/source/utilities/windows/safety_guard_windows.h"
using namespace NEO;
@@ -15,3 +16,10 @@ int buildWithSafetyGuard(OfflineCompiler *compiler) {
int retVal = 0;
return safetyGuard.call<int, OfflineCompiler, decltype(&OfflineCompiler::build)>(compiler, &OfflineCompiler::build, retVal);
}
int linkWithSafetyGuard(OfflineLinker *linker) {
SafetyGuardWindows safetyGuard{};
int returnValueOnCrash{-1};
return safetyGuard.call(linker, &OfflineLinker::execute, returnValueOnCrash);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2021 Intel Corporation
* Copyright (C) 2018-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -521,6 +521,10 @@ IGC::OclTranslationOutputBase *MockIgcOclTranslationCtx::TranslateImpl(
CIF::Builtins::BufferSimple *internalOptions,
CIF::Builtins::BufferSimple *tracingOptions,
uint32_t tracingOptionsCount) {
if (igcDebugVars->shouldReturnInvalidTranslationOutput) {
return nullptr;
}
auto out = new MockOclTranslationOutput();
translate(true, src, options, internalOptions, out);
return out;
@@ -534,6 +538,10 @@ IGC::OclTranslationOutputBase *MockIgcOclTranslationCtx::TranslateImpl(
CIF::Builtins::BufferSimple *tracingOptions,
uint32_t tracingOptionsCount,
void *gtpinInput) {
if (igcDebugVars->shouldReturnInvalidTranslationOutput) {
return nullptr;
}
auto out = new MockOclTranslationOutput();
translate(true, src, options, internalOptions, out);
return out;
@@ -556,6 +564,10 @@ IGC::OclTranslationOutputBase *MockIgcOclTranslationCtx::TranslateImpl(
CIF::Builtins::BufferSimple *tracingOptions,
uint32_t tracingOptionsCount,
void *gtPinInput) {
if (igcDebugVars->shouldReturnInvalidTranslationOutput) {
return nullptr;
}
auto out = new MockOclTranslationOutput();
translate(true, src, options, internalOptions, out);
return out;
@@ -618,6 +630,10 @@ IGC::OclTranslationOutputBase *MockFclOclTranslationCtx::TranslateImpl(
CIF::Builtins::BufferSimple *internalOptions,
CIF::Builtins::BufferSimple *tracingOptions,
uint32_t tracingOptionsCount) {
if (fclDebugVars->shouldReturnInvalidTranslationOutput) {
return nullptr;
}
auto out = new MockOclTranslationOutput();
translate(false, src, options, internalOptions, out);
return out;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2021 Intel Corporation
* Copyright (C) 2018-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -25,6 +25,7 @@ struct MockCompilerDebugVars {
bindful,
bindless
};
bool shouldReturnInvalidTranslationOutput = false;
bool forceBuildFailure = false;
bool forceCreateFailure = false;
bool forceRegisterFail = false;