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) 2018-2021 Intel Corporation
# Copyright (C) 2018-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
#
@ -10,6 +10,7 @@ set(IGDRCL_SRCS_cloc
${OCLOC_DIRECTORY}/source/decoder/binary_decoder.cpp
${OCLOC_DIRECTORY}/source/decoder/binary_encoder.cpp
${OCLOC_DIRECTORY}/source/offline_compiler.cpp
${OCLOC_DIRECTORY}/source/offline_linker.cpp
${OCLOC_DIRECTORY}/source/ocloc_fatbinary.cpp
)
@ -19,12 +20,13 @@ set(IGDRCL_SRCS_offline_compiler_mock
${CMAKE_CURRENT_SOURCE_DIR}/decoder/mock/mock_iga_wrapper.h
${CMAKE_CURRENT_SOURCE_DIR}/mock/mock_argument_helper.h
${CMAKE_CURRENT_SOURCE_DIR}/mock/mock_offline_compiler.h
${CMAKE_CURRENT_SOURCE_DIR}/mock/mock_offline_linker.h
${CMAKE_CURRENT_SOURCE_DIR}/mock/mock_sip_ocloc_tests.cpp
)
set(CLOC_LIB_SRCS_UTILITIES
${OCLOC_DIRECTORY}/source/utilities/safety_caller.h
${OCLOC_DIRECTORY}/source/utilities//get_current_dir.h
${OCLOC_DIRECTORY}/source/utilities/get_current_dir.h
)
if(WIN32)
@ -58,6 +60,8 @@ set(IGDRCL_SRCS_offline_compiler_tests
${CMAKE_CURRENT_SOURCE_DIR}/ocloc_validator_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/offline_compiler_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/offline_compiler_tests.h
${CMAKE_CURRENT_SOURCE_DIR}/offline_linker_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/offline_linker_tests.h
${NEO_SHARED_DIRECTORY}/helpers/abort.cpp
${NEO_SHARED_DIRECTORY}/helpers/file_io.cpp
${NEO_SHARED_DIRECTORY}/memory_manager/deferred_deleter.cpp

View File

@ -1,12 +1,14 @@
/*
* Copyright (C) 2020-2021 Intel Corporation
* Copyright (C) 2020-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/offline_compiler/source/ocloc_arg_helper.h"
#include "shared/source/helpers/string.h"
#include <algorithm>
#include <map>
#include <string>
@ -17,6 +19,10 @@ class MockOclocArgHelper : public OclocArgHelper {
using FilesMap = std::map<FileName, FileData>;
using OclocArgHelper::deviceProductTable;
FilesMap &filesMap;
bool interceptOutput{false};
bool shouldReturnReadingError{false};
FilesMap interceptedFiles;
MockOclocArgHelper(FilesMap &filesMap) : OclocArgHelper(
0, nullptr, nullptr, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr),
filesMap(filesMap){};
@ -30,4 +36,33 @@ class MockOclocArgHelper : public OclocArgHelper {
auto file = filesMap[filename];
return std::vector<char>(file.begin(), file.end());
}
std::unique_ptr<char[]> loadDataFromFile(const std::string &filename, size_t &retSize) override {
if (shouldReturnReadingError) {
return nullptr;
}
if (!fileExists(filename)) {
return OclocArgHelper::loadDataFromFile(filename, retSize);
}
const auto &file = filesMap[filename];
std::unique_ptr<char[]> result{new char[file.size()]};
std::copy(file.begin(), file.end(), result.get());
retSize = file.size();
return result;
}
void saveOutput(const std::string &filename, const void *pData, const size_t &dataSize) override {
if (interceptOutput) {
auto &fileContent = interceptedFiles[filename];
fileContent.resize(dataSize, '\0');
memcpy_s(fileContent.data(), fileContent.size(), pData, dataSize);
} else {
OclocArgHelper::saveOutput(filename, pData, dataSize);
}
}
};

View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#pragma once
#include "shared/offline_compiler/source/offline_linker.h"
namespace NEO {
class MockOfflineLinker : public OfflineLinker {
public:
using OfflineLinker::InputFileContent;
using OfflineLinker::OperationMode;
using OfflineLinker::initHardwareInfo;
using OfflineLinker::initialize;
using OfflineLinker::loadInputFilesContent;
using OfflineLinker::parseCommand;
using OfflineLinker::prepareIgc;
using OfflineLinker::tryToStoreBuildLog;
using OfflineLinker::verifyLinkerCommand;
using OfflineLinker::hwInfo;
using OfflineLinker::inputFilenames;
using OfflineLinker::inputFilesContent;
using OfflineLinker::internalOptions;
using OfflineLinker::operationMode;
using OfflineLinker::options;
using OfflineLinker::outputFilename;
using OfflineLinker::outputFormat;
bool shouldReturnEmptyHardwareInfoTable{false};
bool shouldFailLoadingOfIgcLib{false};
bool shouldFailLoadingOfIgcCreateMainFunction{false};
bool shouldFailCreationOfIgcMain{false};
bool shouldFailCreationOfIgcDeviceContext{false};
bool shouldReturnInvalidIgcPlatformHandle{false};
bool shouldReturnInvalidGTSystemInfoHandle{false};
MockOfflineLinker(OclocArgHelper *argHelper) : OfflineLinker{argHelper} {}
ArrayRef<const HardwareInfo *> getHardwareInfoTable() const override {
if (shouldReturnEmptyHardwareInfoTable) {
return {};
} else {
return OfflineLinker::getHardwareInfoTable();
}
}
std::unique_ptr<OsLibrary> loadIgcLibrary() const override {
if (shouldFailLoadingOfIgcLib) {
return nullptr;
} else {
return OfflineLinker::loadIgcLibrary();
}
}
CIF::CreateCIFMainFunc_t loadCreateIgcMainFunction() const override {
if (shouldFailLoadingOfIgcCreateMainFunction) {
return nullptr;
} else {
return OfflineLinker::loadCreateIgcMainFunction();
}
}
CIF::RAII::UPtr_t<CIF::CIFMain> createIgcMain(CIF::CreateCIFMainFunc_t createMainFunction) const override {
if (shouldFailCreationOfIgcMain) {
return nullptr;
} else {
return OfflineLinker::createIgcMain(createMainFunction);
}
}
CIF::RAII::UPtr_t<IGC::IgcOclDeviceCtxTagOCL> createIgcDeviceContext() const override {
if (shouldFailCreationOfIgcDeviceContext) {
return nullptr;
} else {
return OfflineLinker::createIgcDeviceContext();
}
}
CIF::RAII::UPtr_t<IGC::PlatformTagOCL> getIgcPlatformHandle() const override {
if (shouldReturnInvalidIgcPlatformHandle) {
return nullptr;
} else {
return OfflineLinker::getIgcPlatformHandle();
}
}
CIF::RAII::UPtr_t<IGC::GTSystemInfoTagOCL> getGTSystemInfoHandle() const override {
if (shouldReturnInvalidGTSystemInfoHandle) {
return nullptr;
} else {
return OfflineLinker::getGTSystemInfoHandle();
}
}
};
} // 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/ocloc_api.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/device_binary_format/elf/elf_decoder.h"
@ -43,7 +44,7 @@ TEST(OclocApiTests, WhenGoodArgsAreGivenThenSuccessIsReturned) {
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
EXPECT_EQ(std::string::npos, output.find("Command was: ocloc -file test_files/copybuffer.cl -device "s + argv[4]));
}
@ -61,7 +62,7 @@ TEST(OclocApiTests, GivenNeoRevisionQueryWhenQueryingThenNeoRevisionIsReturned)
0, nullptr, nullptr, nullptr,
0, nullptr, nullptr, nullptr,
&numOutputs, &dataOutputs, &lenOutputs, &nameOutputs);
EXPECT_EQ(retVal, NEO::OfflineCompiler::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
EXPECT_EQ(numOutputs, 2u);
int queryOutputIndex = -1;
@ -92,7 +93,7 @@ TEST(OclocApiTests, GivenOclDriverVersionQueryWhenQueryingThenNeoRevisionIsRetur
0, nullptr, nullptr, nullptr,
0, nullptr, nullptr, nullptr,
&numOutputs, &dataOutputs, &lenOutputs, &nameOutputs);
EXPECT_EQ(retVal, NEO::OfflineCompiler::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
EXPECT_EQ(numOutputs, 2u);
int queryOutputIndex = -1;
@ -121,7 +122,7 @@ TEST(OclocApiTests, GivenNoQueryWhenQueryingThenErrorIsReturned) {
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::INVALID_COMMAND_LINE);
EXPECT_EQ(retVal, NEO::OclocErrorCode::INVALID_COMMAND_LINE);
EXPECT_STREQ("Error: Invalid command line. Expected ocloc query <argument>", output.c_str());
}
@ -138,7 +139,7 @@ TEST(OclocApiTests, GivenInvalidQueryWhenQueryingThenErrorIsReturned) {
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::INVALID_COMMAND_LINE);
EXPECT_EQ(retVal, NEO::OclocErrorCode::INVALID_COMMAND_LINE);
EXPECT_STREQ("Error: Invalid command line. Uknown argument unknown_query.", output.c_str());
}
@ -158,7 +159,7 @@ TEST(OclocApiTests, WhenGoodFamilyNameIsProvidedThenSuccessIsReturned) {
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
EXPECT_EQ(std::string::npos, output.find("Command was: ocloc -file test_files/copybuffer.cl -device "s + argv[4]));
}
@ -179,7 +180,7 @@ TEST(OclocApiTests, WhenArgsWithMissingFileAreGivenThenErrorMessageIsProduced) {
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::INVALID_FILE);
EXPECT_EQ(retVal, NEO::OclocErrorCode::INVALID_FILE);
EXPECT_NE(std::string::npos, output.find("Command was: ocloc -q -file test_files/IDoNotExist.cl -device "s + argv[5]));
}
@ -200,7 +201,7 @@ TEST(OclocApiTests, givenInputOptionsAndInternalOptionsWhenCmdlineIsPrintedThenB
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_NE(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_NE(retVal, NEO::OclocErrorCode::SUCCESS);
EXPECT_TRUE(output.find("Command was: ocloc -q -file test_files/IDoNotExist.cl -device "s +
gEnvironment->devicePrefix.c_str() +
" -options \"-D DEBUG -cl-kernel-arg-info\" -internal_options \"-internalOption1 -internal-option-2\"") != std::string::npos);
@ -226,7 +227,7 @@ TEST(OclocApiTests, givenInputOptionsCalledOptionsWhenCmdlineIsPrintedThenQuotes
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_NE(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_NE(retVal, NEO::OclocErrorCode::SUCCESS);
EXPECT_TRUE(output.find("Command was: ocloc -q -file test_files/IDoNotExist.cl -device "s +
gEnvironment->devicePrefix.c_str() +
" -options \"-options\" -internal_options \"-internalOption\"") != std::string::npos);
@ -255,7 +256,7 @@ TEST(OclocApiTests, givenInvalidInputOptionsAndInternalOptionsFilesWhenCmdlineIs
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_NE(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_NE(retVal, NEO::OclocErrorCode::SUCCESS);
EXPECT_TRUE(output.find("Compiling options read from file were:\n"
"-shouldfailOptions") != std::string::npos);
@ -283,7 +284,7 @@ TEST(OclocApiTests, givenInvalidOclocOptionsFileWhenCmdlineIsPrintedThenTheyAreP
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_NE(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_NE(retVal, NEO::OclocErrorCode::SUCCESS);
EXPECT_TRUE(output.find("Failed with ocloc options from file:\n"
"-invalid_ocloc_option") != std::string::npos);
@ -396,7 +397,7 @@ TEST(OclocApiTests, GivenHelpParameterWhenDecodingThenHelpMsgIsPrintedAndSuccess
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_FALSE(output.empty());
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
}
TEST(OclocApiTests, GivenHelpParameterWhenEncodingThenHelpMsgIsPrintedAndSuccessIsReturned) {
@ -413,5 +414,109 @@ TEST(OclocApiTests, GivenHelpParameterWhenEncodingThenHelpMsgIsPrintedAndSuccess
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_FALSE(output.empty());
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
}
TEST(OclocApiTests, GivenNonexistentFileWhenValidateIsInvokedThenErrorIsPrinted) {
const char *argv[] = {
"ocloc",
"validate",
"-file",
"some_special_nonexistent_file.gen"};
unsigned int argc = sizeof(argv) / sizeof(argv[0]);
testing::internal::CaptureStdout();
int retVal = oclocInvoke(argc, argv,
0, nullptr, nullptr, nullptr,
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
const auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(-1, retVal);
const std::string expectedErrorMessage{"Error : Input file missing : some_special_nonexistent_file.gen\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST(OclocApiTests, GivenZeroArgumentsWhenOclocIsInvokedThenHelpIsPrinted) {
testing::internal::CaptureStdout();
int retVal = oclocInvoke(0, nullptr,
0, nullptr, nullptr, nullptr,
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
const auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(NEO::OclocErrorCode::INVALID_COMMAND_LINE, retVal);
EXPECT_FALSE(output.empty());
}
TEST(OclocApiTests, GivenCommandWithoutArgsWhenOclocIsInvokedThenHelpIsPrinted) {
const char *argv[] = {
"ocloc"};
unsigned int argc = sizeof(argv) / sizeof(argv[0]);
testing::internal::CaptureStdout();
int retVal = oclocInvoke(argc, argv,
0, nullptr, nullptr, nullptr,
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
const auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(NEO::OclocErrorCode::SUCCESS, retVal);
EXPECT_FALSE(output.empty());
}
TEST(OclocApiTests, GivenLongHelpArgumentWhenOclocIsInvokedThenHelpIsPrinted) {
const char *argv[] = {
"ocloc",
"--help"};
unsigned int argc = sizeof(argv) / sizeof(argv[0]);
testing::internal::CaptureStdout();
int retVal = oclocInvoke(argc, argv,
0, nullptr, nullptr, nullptr,
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
const auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(NEO::OclocErrorCode::SUCCESS, retVal);
EXPECT_FALSE(output.empty());
}
TEST(OclocApiTests, GivenHelpParameterWhenLinkingThenHelpMsgIsPrintedAndSuccessIsReturned) {
const char *argv[] = {
"ocloc",
"link",
"--help"};
unsigned int argc = sizeof(argv) / sizeof(argv[0]);
testing::internal::CaptureStdout();
int retVal = oclocInvoke(argc, argv,
0, nullptr, nullptr, nullptr,
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_FALSE(output.empty());
EXPECT_EQ(NEO::OclocErrorCode::SUCCESS, retVal);
}
TEST(OclocApiTests, GivenInvalidParameterWhenLinkingThenErrorIsReturned) {
const char *argv[] = {
"ocloc",
"link",
"--dummy_param"};
unsigned int argc = sizeof(argv) / sizeof(argv[0]);
testing::internal::CaptureStdout();
int retVal = oclocInvoke(argc, argv,
0, nullptr, nullptr, nullptr,
0, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr);
std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(NEO::OclocErrorCode::INVALID_COMMAND_LINE, retVal);
const std::string expectedInitError{"Invalid option (arg 2): --dummy_param\n"};
const std::string expectedExecuteError{"Error: Linker cannot be executed due to unsuccessful initialization!\n"};
const std::string expectedErrorMessage = expectedInitError + expectedExecuteError;
EXPECT_EQ(expectedErrorMessage, output);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Intel Corporation
* Copyright (C) 2020-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@ -8,6 +8,7 @@
#include "opencl/test/unit_test/offline_compiler/ocloc_fatbinary_tests.h"
#include "shared/offline_compiler/source/ocloc_arg_helper.h"
#include "shared/offline_compiler/source/ocloc_error_code.h"
#include "shared/source/helpers/hw_helper.h"
#include <algorithm>
@ -505,7 +506,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenMutiplePlatformWhenSecon
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_NE(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_NE(retVal, NEO::OclocErrorCode::SUCCESS);
resString << "Unknown device : unk\n";
resString << "Failed to parse target devices from : " << platformTarget << "\n";
@ -536,7 +537,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenClosedRangeTooExtensiveW
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_NE(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_NE(retVal, NEO::OclocErrorCode::SUCCESS);
resString << "Invalid range : " << configString.str() << " - should be from-to or -to or from-"
<< "\n";
resString << "Failed to parse target devices from : " << configString.str() << "\n";
@ -641,7 +642,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenTwoPlatformsWhenFatBinar
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
for (uint32_t i = 0; i < got.size(); i++) {
resString << "Build succeeded for : " << expected[i].str() + "." + platformsRevision[i] + ".\n";
@ -695,7 +696,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformsClosedRangeWhen
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
for (uint32_t i = 0; i < got.size(); i++) {
resString << "Build succeeded for : " << expected[i].str() + "." + platformsRevisions[i] + ".\n";
@ -744,7 +745,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformsOpenRangeToWhen
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
for (uint32_t i = 0; i < got.size(); i++) {
resString << "Build succeeded for : " << expected[i].str() + "." + platformsRevisions[i] + ".\n";
@ -793,7 +794,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenPlatformsOpenRangeFromWh
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
for (uint32_t i = 0; i < got.size(); i++) {
resString << "Build succeeded for : " << expected[i].str() + "." + platformsRevisions[i] + ".\n";
@ -847,7 +848,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenTwoConfigsWhenFatBinaryB
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
for (auto deviceConfig : expected) {
std::string platformName = hardwarePrefix[deviceConfig.hwInfo->platform.eProductFamily];
@ -893,7 +894,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigOpenRangeFr
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
for (auto deviceConfig : expected) {
std::string platformName = hardwarePrefix[deviceConfig.hwInfo->platform.eProductFamily];
@ -939,7 +940,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigOpenRangeTo
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
for (auto deviceConfig : expected) {
std::string platformName = hardwarePrefix[deviceConfig.hwInfo->platform.eProductFamily];
@ -993,7 +994,7 @@ TEST_F(OclocFatBinaryGetTargetConfigsForFatbinary, GivenProductConfigClosedRange
testing::internal::CaptureStdout();
int retVal = buildFatBinary(argv, argHelper.get());
auto output = testing::internal::GetCapturedStdout();
EXPECT_EQ(retVal, NEO::OfflineCompiler::ErrorCode::SUCCESS);
EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS);
for (auto deviceConfig : expected) {
std::string platformName = hardwarePrefix[deviceConfig.hwInfo->platform.eProductFamily];

View File

@ -238,7 +238,7 @@ TEST_F(MultiCommandTests, GivenMissingTextFileWithArgsWhenBuildingMultiCommandTh
EXPECT_STRNE(output.c_str(), "");
EXPECT_EQ(nullptr, pMultiCommand);
EXPECT_EQ(OfflineCompiler::ErrorCode::INVALID_FILE, retVal);
EXPECT_EQ(OclocErrorCode::INVALID_FILE, retVal);
DebugManager.flags.PrintDebugMessages.set(false);
}
TEST_F(MultiCommandTests, GivenLackOfClFileWhenBuildingMultiCommandThenInvalidFileErrorIsReturned) {
@ -263,7 +263,7 @@ TEST_F(MultiCommandTests, GivenLackOfClFileWhenBuildingMultiCommandThenInvalidFi
std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(nullptr, pMultiCommand);
EXPECT_EQ(OfflineCompiler::ErrorCode::INVALID_FILE, retVal);
EXPECT_EQ(OclocErrorCode::INVALID_FILE, retVal);
DebugManager.flags.PrintDebugMessages.set(false);
deleteFileWithArgs();
@ -318,7 +318,7 @@ TEST_F(OfflineCompilerTests, GivenHelpOptionOnQueryThenSuccessIsReturned) {
std::string output = testing::internal::GetCapturedStdout();
EXPECT_STREQ(OfflineCompiler::queryHelp.data(), output.c_str());
EXPECT_EQ(OfflineCompiler::ErrorCode::SUCCESS, retVal);
EXPECT_EQ(OclocErrorCode::SUCCESS, retVal);
}
TEST_F(OfflineCompilerTests, GivenArgsWhenQueryIsCalledThenSuccessIsReturned) {
@ -329,7 +329,7 @@ TEST_F(OfflineCompilerTests, GivenArgsWhenQueryIsCalledThenSuccessIsReturned) {
int retVal = OfflineCompiler::query(argv.size(), argv, oclocArgHelperWithoutInput.get());
EXPECT_EQ(OfflineCompiler::ErrorCode::SUCCESS, retVal);
EXPECT_EQ(OclocErrorCode::SUCCESS, retVal);
}
TEST_F(OfflineCompilerTests, GivenArgsWhenOfflineCompilerIsCreatedThenSuccessIsReturned) {
@ -587,7 +587,7 @@ TEST_F(OfflineCompilerTests, givenExcludeIrArgumentWhenCompilingKernelThenIrShou
mockOfflineCompiler.initialize(argv.size(), argv);
const auto buildResult{mockOfflineCompiler.build()};
ASSERT_EQ(OfflineCompiler::SUCCESS, buildResult);
ASSERT_EQ(OclocErrorCode::SUCCESS, buildResult);
std::string errorReason{};
std::string warning{};
@ -611,7 +611,7 @@ TEST_F(OfflineCompilerTests, givenLackOfExcludeIrArgumentWhenCompilingKernelThen
mockOfflineCompiler.initialize(argv.size(), argv);
const auto buildResult{mockOfflineCompiler.build()};
ASSERT_EQ(OfflineCompiler::SUCCESS, buildResult);
ASSERT_EQ(OclocErrorCode::SUCCESS, buildResult);
std::string errorReason{};
std::string warning{};
@ -872,7 +872,7 @@ TEST_F(OfflineCompilerTests, GivenHelpOptionThenBuildDoesNotOccur) {
pOfflineCompiler = OfflineCompiler::create(argv.size(), argv, true, retVal, oclocArgHelperWithoutInput.get());
std::string output = testing::internal::GetCapturedStdout();
EXPECT_STRNE("", output.c_str());
EXPECT_EQ(OfflineCompiler::ErrorCode::SUCCESS, retVal);
EXPECT_EQ(OclocErrorCode::SUCCESS, retVal);
delete pOfflineCompiler;
}
@ -890,7 +890,7 @@ TEST_F(OfflineCompilerTests, GivenInvalidFileWhenBuildingThenInvalidFileErrorIsR
std::string output = testing::internal::GetCapturedStdout();
EXPECT_STRNE(output.c_str(), "");
EXPECT_EQ(nullptr, pOfflineCompiler);
EXPECT_EQ(OfflineCompiler::ErrorCode::INVALID_FILE, retVal);
EXPECT_EQ(OclocErrorCode::INVALID_FILE, retVal);
DebugManager.flags.PrintDebugMessages.set(false);
delete pOfflineCompiler;
}
@ -908,7 +908,7 @@ TEST_F(OfflineCompilerTests, GivenInvalidFlagWhenBuildingThenInvalidCommandLineE
std::string output = testing::internal::GetCapturedStdout();
EXPECT_STRNE(output.c_str(), "");
EXPECT_EQ(nullptr, pOfflineCompiler);
EXPECT_EQ(OfflineCompiler::ErrorCode::INVALID_COMMAND_LINE, retVal);
EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, retVal);
delete pOfflineCompiler;
}
@ -925,7 +925,7 @@ TEST_F(OfflineCompilerTests, GivenInvalidOptionsWhenBuildingThenInvalidCommandLi
EXPECT_STRNE(output.c_str(), "");
EXPECT_EQ(nullptr, pOfflineCompiler);
EXPECT_EQ(OfflineCompiler::ErrorCode::INVALID_COMMAND_LINE, retVal);
EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, retVal);
delete pOfflineCompiler;
@ -939,7 +939,7 @@ TEST_F(OfflineCompilerTests, GivenInvalidOptionsWhenBuildingThenInvalidCommandLi
output = testing::internal::GetCapturedStdout();
EXPECT_STRNE(output.c_str(), "");
EXPECT_EQ(nullptr, pOfflineCompiler);
EXPECT_EQ(OfflineCompiler::ErrorCode::INVALID_COMMAND_LINE, retVal);
EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, retVal);
delete pOfflineCompiler;
}
@ -1724,7 +1724,7 @@ TEST(OfflineCompilerTest, givenCompilerWhenBuildSourceCodeFailsThenGenerateElfBi
MockOfflineCompiler compiler;
compiler.overrideBuildSourceCodeStatus = true;
auto expectedError = OfflineCompiler::ErrorCode::BUILD_PROGRAM_FAILURE;
auto expectedError = OclocErrorCode::BUILD_PROGRAM_FAILURE;
compiler.buildSourceCodeStatus = expectedError;
EXPECT_EQ(0u, compiler.generateElfBinaryCalled);
@ -1755,7 +1755,7 @@ TEST(OfflineCompilerTest, givenDeviceSpecificKernelFileWhenCompilerIsInitialized
gEnvironment->devicePrefix.c_str()};
int retVal = mockOfflineCompiler->initialize(argv.size(), argv);
EXPECT_EQ(OfflineCompiler::ErrorCode::SUCCESS, retVal);
EXPECT_EQ(OclocErrorCode::SUCCESS, retVal);
EXPECT_STREQ("-cl-opt-disable", mockOfflineCompiler->options.c_str());
}
@ -1774,7 +1774,7 @@ TEST(OfflineCompilerTest, givenHexadecimalRevisionIdWhenCompilerIsInitializedThe
"0x11"};
int retVal = mockOfflineCompiler->initialize(argv.size(), argv);
EXPECT_EQ(OfflineCompiler::ErrorCode::SUCCESS, retVal);
EXPECT_EQ(OclocErrorCode::SUCCESS, retVal);
EXPECT_EQ(mockOfflineCompiler->hwInfo.platform.usRevId, 17);
}
@ -1796,7 +1796,7 @@ TEST(OfflineCompilerTest, givenDebugVariableSetWhenInitializingThenOverrideRevis
"0x11"};
int retVal = mockOfflineCompiler->initialize(argv.size(), argv);
EXPECT_EQ(OfflineCompiler::ErrorCode::SUCCESS, retVal);
EXPECT_EQ(OclocErrorCode::SUCCESS, retVal);
EXPECT_EQ(mockOfflineCompiler->hwInfo.platform.usRevId, 123);
}
@ -1815,7 +1815,7 @@ TEST(OfflineCompilerTest, givenDecimalRevisionIdWhenCompilerIsInitializedThenPas
"17"};
int retVal = mockOfflineCompiler->initialize(argv.size(), argv);
EXPECT_EQ(OfflineCompiler::ErrorCode::SUCCESS, retVal);
EXPECT_EQ(OclocErrorCode::SUCCESS, retVal);
EXPECT_EQ(mockOfflineCompiler->hwInfo.platform.usRevId, 17);
}
@ -1834,7 +1834,7 @@ TEST(OfflineCompilerTest, givenNoRevisionIdWhenCompilerIsInitializedThenHwInfoHa
mockOfflineCompiler->initHardwareInfo(gEnvironment->devicePrefix.c_str());
auto revId = mockOfflineCompiler->hwInfo.platform.usRevId;
int retVal = mockOfflineCompiler->initialize(argv.size(), argv);
EXPECT_EQ(OfflineCompiler::ErrorCode::SUCCESS, retVal);
EXPECT_EQ(OclocErrorCode::SUCCESS, retVal);
EXPECT_EQ(mockOfflineCompiler->hwInfo.platform.usRevId, revId);
}
@ -1851,7 +1851,7 @@ TEST(OfflineCompilerTest, whenDeviceIsSpecifiedThenDefaultConfigFromTheDeviceIsU
gEnvironment->devicePrefix.c_str()};
int retVal = mockOfflineCompiler->initialize(argv.size(), argv);
EXPECT_EQ(OfflineCompiler::ErrorCode::SUCCESS, retVal);
EXPECT_EQ(OclocErrorCode::SUCCESS, retVal);
HardwareInfo hwInfo = mockOfflineCompiler->hwInfo;

View File

@ -1,12 +1,14 @@
/*
* Copyright (C) 2018-2021 Intel Corporation
* Copyright (C) 2018-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#pragma once
#include "shared/offline_compiler/source/multi_command.h"
#include "shared/offline_compiler/source/ocloc_error_code.h"
#include "shared/offline_compiler/source/offline_compiler.h"
#include "gtest/gtest.h"
@ -19,7 +21,7 @@ namespace NEO {
class OfflineCompilerTests : public ::testing::Test {
public:
OfflineCompiler *pOfflineCompiler = nullptr;
int retVal = OfflineCompiler::ErrorCode::SUCCESS;
int retVal = OclocErrorCode::SUCCESS;
std::unique_ptr<OclocArgHelper> oclocArgHelperWithoutInput = std::make_unique<OclocArgHelper>();
};
@ -31,7 +33,7 @@ class MultiCommandTests : public ::testing::Test {
MultiCommand *pMultiCommand = nullptr;
std::string nameOfFileWithArgs;
std::string outFileList;
int retVal = OfflineCompiler::ErrorCode::SUCCESS;
int retVal = OclocErrorCode::SUCCESS;
std::unique_ptr<OclocArgHelper> oclocArgHelperWithoutInput = std::make_unique<OclocArgHelper>();
};
} // namespace NEO

View File

@ -0,0 +1,793 @@
/*
* Copyright (C) 2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "offline_linker_tests.h"
#include "shared/offline_compiler/source/ocloc_error_code.h"
#include "shared/source/device_binary_format/elf/elf_decoder.h"
#include "shared/source/device_binary_format/elf/ocl_elf.h"
#include "shared/source/helpers/string.h"
#include "shared/source/os_interface/os_inc_base.h"
#include "shared/test/common/mocks/mock_compilers.h"
#include "environment.h"
#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
extern Environment *gEnvironment;
namespace NEO {
using OperationMode = MockOfflineLinker::OperationMode;
void OfflineLinkerTest::SetUp() {
MockCompilerDebugVars igcDebugVars{gEnvironment->igcDebugVars};
igcDebugVars.binaryToReturn = binaryToReturn;
igcDebugVars.binaryToReturnSize = sizeof(binaryToReturn);
setIgcDebugVars(igcDebugVars);
}
void OfflineLinkerTest::TearDown() {
setIgcDebugVars(gEnvironment->igcDebugVars);
}
std::string OfflineLinkerTest::getEmptySpirvFile() const {
std::string spirv{"\x07\x23\x02\x03"};
spirv.resize(64, '\0');
return spirv;
}
std::string OfflineLinkerTest::getEmptyLlvmBcFile() const {
std::string llvmbc{"BC\xc0\xde"};
llvmbc.resize(64, '\0');
return llvmbc;
}
MockOfflineLinker::InputFileContent OfflineLinkerTest::createFileContent(const std::string &content, IGC::CodeType::CodeType_t codeType) const {
std::unique_ptr<char[]> bytes{new char[content.size()]};
std::copy(content.begin(), content.end(), bytes.get());
return {std::move(bytes), content.size(), codeType};
}
TEST_F(OfflineLinkerTest, GivenDefaultConstructedLinkerThenRequiredFieldsHaveDefaultValues) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
EXPECT_EQ(OperationMode::SKIP_EXECUTION, mockOfflineLinker.operationMode);
EXPECT_EQ("linker_output", mockOfflineLinker.outputFilename);
EXPECT_EQ(IGC::CodeType::llvmBc, mockOfflineLinker.outputFormat);
}
TEST_F(OfflineLinkerTest, GivenLessThanTwoArgumentsWhenParsingThenInvalidCommandIsReturned) {
const std::vector<std::string> argv = {
"ocloc.exe"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, result);
}
TEST_F(OfflineLinkerTest, GivenInputFilesArgumentsWhenParsingThenListOfFilenamesIsPopulated) {
const std::string firstFile{"sample_input_1.spv"};
const std::string secondFile{"sample_input_2.spv"};
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
firstFile,
"-file",
secondFile};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, result);
ASSERT_EQ(2u, mockOfflineLinker.inputFilenames.size());
EXPECT_EQ(firstFile, mockOfflineLinker.inputFilenames[0]);
EXPECT_EQ(secondFile, mockOfflineLinker.inputFilenames[1]);
}
TEST_F(OfflineLinkerTest, GivenOutputFilenameArgumentWhenParsingThenOutputFilenameIsSetAccordingly) {
const std::string outputFilename{"my_custom_output_filename"};
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-out",
outputFilename};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, result);
EXPECT_EQ(outputFilename, mockOfflineLinker.outputFilename);
}
TEST_F(OfflineLinkerTest, GivenValidOutputFileFormatWhenParsingThenOutputFormatIsSetAccordingly) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-out_format",
"LLVM_BC"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, result);
EXPECT_EQ(IGC::CodeType::llvmBc, mockOfflineLinker.outputFormat);
}
TEST_F(OfflineLinkerTest, GivenUnknownOutputFileFormatWhenParsingThenInvalidFormatIsSet) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-out_format",
"StrangeFormat"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, result);
EXPECT_EQ(IGC::CodeType::invalid, mockOfflineLinker.outputFormat);
}
TEST_F(OfflineLinkerTest, GivenOptionsArgumentWhenParsingThenOptionsAreSet) {
const std::string options{"-g"};
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-options",
options};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, result);
EXPECT_EQ(options, mockOfflineLinker.options);
}
TEST_F(OfflineLinkerTest, GivenInternalOptionsArgumentWhenParsingThenInternalOptionsAreSet) {
const std::string internalOptions{"-ze-allow-zebin"};
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-internal_options",
internalOptions};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, result);
EXPECT_EQ(internalOptions, mockOfflineLinker.internalOptions);
}
TEST_F(OfflineLinkerTest, GivenHelpArgumentWhenParsingThenShowHelpOperationIsSet) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"--help"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, result);
EXPECT_EQ(OperationMode::SHOW_HELP, mockOfflineLinker.operationMode);
}
TEST_F(OfflineLinkerTest, GivenUnknownArgumentWhenParsingThenErrorIsReported) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-some_new_unknown_command"};
::testing::internal::CaptureStdout();
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto result = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, result);
const std::string expectedErrorMessage{"Invalid option (arg 2): -some_new_unknown_command\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenFlagsWhichRequireMoreArgsWithoutThemWhenParsingThenErrorIsReported) {
const std::array<std::string, 5> flagsToTest = {
"-file", "-out", "-out_format", "-options", "-internal_options"};
for (const auto &flag : flagsToTest) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
flag};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
::testing::internal::CaptureStdout();
const auto result = mockOfflineLinker.parseCommand(argv.size(), argv);
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, result);
const std::string expectedErrorMessage{"Invalid option (arg 2): " + flag + "\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
}
TEST_F(OfflineLinkerTest, GivenCommandWithoutInputFilesWhenVerificationIsPerformedThenErrorIsReturned) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.inputFilenames = {};
::testing::internal::CaptureStdout();
const auto verificationResult = mockOfflineLinker.verifyLinkerCommand();
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, verificationResult);
const std::string expectedErrorMessage{"Error: Input name is missing! At least one input file is required!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenCommandWithEmptyFilenameWhenVerificationIsPerformedThenErrorIsReturned) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
""};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
::testing::internal::CaptureStdout();
const auto verificationResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, verificationResult);
const std::string expectedErrorMessage{"Error: Empty filename cannot be used!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenCommandWithNonexistentInputFileWhenVerificationIsPerformedThenErrorIsReturned) {
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
"some_file1.spv",
"-file",
"some_file2.spv"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto parsingResult = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, parsingResult);
::testing::internal::CaptureStdout();
const auto verificationResult = mockOfflineLinker.verifyLinkerCommand();
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::INVALID_FILE, verificationResult);
const std::string expectedErrorMessage{"Error: Input file some_file1.spv missing.\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenCommandWithInvalidOutputFormatWhenVerificationIsPerformedThenErrorIsReturned) {
mockArgHelperFilesMap["some_file.spv"] = getEmptySpirvFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
"some_file.spv",
"-out_format",
"SomeDummyUnknownFormat"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto parsingResult = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, parsingResult);
::testing::internal::CaptureStdout();
const auto verificationResult = mockOfflineLinker.verifyLinkerCommand();
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, verificationResult);
const std::string expectedErrorMessage{"Error: Invalid output type!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenValidCommandWhenVerificationIsPerformedThenSuccessIsReturned) {
mockArgHelperFilesMap["some_file1.spv"] = getEmptySpirvFile();
mockArgHelperFilesMap["some_file2.spv"] = getEmptySpirvFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
"some_file1.spv",
"-file",
"some_file2.spv"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
const auto parsingResult = mockOfflineLinker.parseCommand(argv.size(), argv);
ASSERT_EQ(OclocErrorCode::SUCCESS, parsingResult);
::testing::internal::CaptureStdout();
const auto verificationResult = mockOfflineLinker.verifyLinkerCommand();
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::SUCCESS, verificationResult);
EXPECT_TRUE(output.empty());
}
TEST_F(OfflineLinkerTest, GivenEmptyFileWhenLoadingInputFilesThenErrorIsReturned) {
const std::string filename{"some_file.spv"};
// Empty file is treated as an error.
mockArgHelperFilesMap[filename] = "";
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
filename};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
::testing::internal::CaptureStdout();
const auto readingResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{::testing::internal::GetCapturedStdout()};
ASSERT_EQ(OclocErrorCode::INVALID_FILE, readingResult);
const std::string expectedErrorMessage{"Error: Cannot read input file: some_file.spv\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenValidFileWithUnknownFormatWhenLoadingInputFilesThenErrorIsReturned) {
const std::string filename{"some_file.unknown"};
// Spir-V or LLVM-BC magic constants are required. This should be treated as error.
mockArgHelperFilesMap[filename] = "Some unknown format!";
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.inputFilenames.push_back(filename);
::testing::internal::CaptureStdout();
const auto readingResult = mockOfflineLinker.loadInputFilesContent();
const auto output{::testing::internal::GetCapturedStdout()};
ASSERT_EQ(OclocErrorCode::INVALID_PROGRAM, readingResult);
const std::string expectedErrorMessage{"Error: Unsupported format of input file: some_file.unknown\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenReadingErrorWhenLoadingInputFilesThenErrorIsReturned) {
const std::string filename{"some_file1.spv"};
mockArgHelperFilesMap[filename] = getEmptySpirvFile();
mockArgHelper.shouldReturnReadingError = true;
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.inputFilenames.push_back(filename);
::testing::internal::CaptureStdout();
const auto readingResult = mockOfflineLinker.loadInputFilesContent();
const auto output{::testing::internal::GetCapturedStdout()};
ASSERT_EQ(OclocErrorCode::INVALID_FILE, readingResult);
const std::string expectedErrorMessage{"Error: Cannot read input file: some_file1.spv\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenValidFilesWithValidFormatsWhenLoadingInputFilesThenFilesAreLoadedAndSuccessIsReturned) {
const std::string firstFilename{"some_file1.spv"};
const std::string secondFilename{"some_file2.llvmbc"};
mockArgHelperFilesMap[firstFilename] = getEmptySpirvFile();
mockArgHelperFilesMap[secondFilename] = getEmptyLlvmBcFile();
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.inputFilenames.push_back(firstFilename);
mockOfflineLinker.inputFilenames.push_back(secondFilename);
::testing::internal::CaptureStdout();
const auto readingResult = mockOfflineLinker.loadInputFilesContent();
const auto output{::testing::internal::GetCapturedStdout()};
ASSERT_EQ(OclocErrorCode::SUCCESS, readingResult);
EXPECT_TRUE(output.empty());
const auto &firstExpectedContent = mockArgHelperFilesMap[firstFilename];
const auto &firstActualContent = mockOfflineLinker.inputFilesContent[0];
ASSERT_EQ(firstExpectedContent.size(), firstActualContent.size);
const auto isFirstPairEqual = std::equal(firstExpectedContent.begin(), firstExpectedContent.end(), firstActualContent.bytes.get());
EXPECT_TRUE(isFirstPairEqual);
const auto &secondExpectedContent = mockArgHelperFilesMap[secondFilename];
const auto &secondActualContent = mockOfflineLinker.inputFilesContent[1];
ASSERT_EQ(secondExpectedContent.size(), secondActualContent.size);
const auto isSecondPairEqual = std::equal(secondExpectedContent.begin(), secondExpectedContent.end(), secondActualContent.bytes.get());
EXPECT_TRUE(isSecondPairEqual);
}
TEST_F(OfflineLinkerTest, GivenValidFilesWhenInitializationIsSuccessfulThenLinkModeOfOperationIsSet) {
const std::string firstFilename{"some_file1.spv"};
const std::string secondFilename{"some_file2.llvmbc"};
mockArgHelperFilesMap[firstFilename] = getEmptySpirvFile();
mockArgHelperFilesMap[secondFilename] = getEmptyLlvmBcFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
firstFilename,
"-file",
secondFilename};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
::testing::internal::CaptureStdout();
const auto readingResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::SUCCESS, readingResult);
EXPECT_TRUE(output.empty());
EXPECT_EQ(OperationMode::LINK_FILES, mockOfflineLinker.operationMode);
}
TEST_F(OfflineLinkerTest, GivenSPIRVandLLVMBCFilesWhenElfOutputIsRequestedThenElfWithSPIRVAndLLVMSectionsIsCreated) {
auto spirvFileContent = createFileContent(getEmptySpirvFile(), IGC::CodeType::spirV);
auto llvmbcFileContent = createFileContent(getEmptyLlvmBcFile(), IGC::CodeType::llvmBc);
mockArgHelper.interceptOutput = true;
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.inputFilesContent.emplace_back(std::move(spirvFileContent.bytes), spirvFileContent.size, spirvFileContent.codeType);
mockOfflineLinker.inputFilesContent.emplace_back(std::move(llvmbcFileContent.bytes), llvmbcFileContent.size, llvmbcFileContent.codeType);
mockOfflineLinker.outputFormat = IGC::CodeType::elf;
mockOfflineLinker.operationMode = OperationMode::LINK_FILES;
const auto linkingResult{mockOfflineLinker.execute()};
ASSERT_EQ(OclocErrorCode::SUCCESS, linkingResult);
ASSERT_EQ(1u, mockArgHelper.interceptedFiles.count("linker_output"));
const auto &rawOutput{mockArgHelper.interceptedFiles.at("linker_output")};
const auto encodedElf{ArrayRef<const uint8_t>::fromAny(rawOutput.data(), rawOutput.size())};
std::string errorReason{};
std::string warning{};
const auto elf{Elf::decodeElf(encodedElf, errorReason, warning)};
ASSERT_TRUE(errorReason.empty());
EXPECT_TRUE(warning.empty());
// SPIR-V bitcode section.
EXPECT_EQ(Elf::SHT_OPENCL_SPIRV, elf.sectionHeaders[1].header->type);
const auto &expectedFirstSection = mockOfflineLinker.inputFilesContent[0];
const auto &actualFirstSection = elf.sectionHeaders[1];
ASSERT_EQ(expectedFirstSection.size, actualFirstSection.header->size);
const auto isFirstSectionContentEqual = std::memcmp(actualFirstSection.data.begin(), expectedFirstSection.bytes.get(), expectedFirstSection.size) == 0;
EXPECT_TRUE(isFirstSectionContentEqual);
// LLVM bitcode section.
EXPECT_EQ(Elf::SHT_OPENCL_LLVM_BINARY, elf.sectionHeaders[2].header->type);
const auto &expectedSecondSection = mockOfflineLinker.inputFilesContent[1];
const auto &actualSecondSection = elf.sectionHeaders[2];
ASSERT_EQ(expectedSecondSection.size, actualSecondSection.header->size);
const auto isSecondSectionContentEqual = std::memcmp(actualSecondSection.data.begin(), expectedSecondSection.bytes.get(), expectedSecondSection.size) == 0;
EXPECT_TRUE(isSecondSectionContentEqual);
}
TEST_F(OfflineLinkerTest, GivenValidInputFileContentsWhenLlvmBcOutputIsRequestedThenSuccessIsReturnedAndFileIsWritten) {
auto spirvFileContent = createFileContent(getEmptySpirvFile(), IGC::CodeType::spirV);
auto llvmbcFileContent = createFileContent(getEmptyLlvmBcFile(), IGC::CodeType::llvmBc);
mockArgHelper.interceptOutput = true;
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.inputFilesContent.emplace_back(std::move(spirvFileContent.bytes), spirvFileContent.size, spirvFileContent.codeType);
mockOfflineLinker.inputFilesContent.emplace_back(std::move(llvmbcFileContent.bytes), llvmbcFileContent.size, llvmbcFileContent.codeType);
mockOfflineLinker.outputFormat = IGC::CodeType::llvmBc;
mockOfflineLinker.operationMode = OperationMode::LINK_FILES;
const auto igcInitializationResult{mockOfflineLinker.prepareIgc()};
ASSERT_EQ(OclocErrorCode::SUCCESS, igcInitializationResult);
const auto linkingResult{mockOfflineLinker.execute()};
ASSERT_EQ(OclocErrorCode::SUCCESS, linkingResult);
ASSERT_EQ(1u, mockArgHelper.interceptedFiles.count("linker_output"));
const auto &actualOutput{mockArgHelper.interceptedFiles.at("linker_output")};
const auto &expectedOutput{binaryToReturn};
ASSERT_EQ(sizeof(expectedOutput), actualOutput.size());
const auto isActualOutputSameAsExpected{std::equal(std::begin(expectedOutput), std::end(expectedOutput), std::begin(actualOutput))};
EXPECT_TRUE(isActualOutputSameAsExpected);
}
TEST_F(OfflineLinkerTest, GivenValidInputFileContentsAndFailingIGCWhenLlvmBcOutputIsRequestedThenErrorIsReturned) {
MockCompilerDebugVars igcDebugVars{gEnvironment->igcDebugVars};
igcDebugVars.forceBuildFailure = true;
setIgcDebugVars(igcDebugVars);
auto spirvFileContent = createFileContent(getEmptySpirvFile(), IGC::CodeType::spirV);
auto llvmbcFileContent = createFileContent(getEmptyLlvmBcFile(), IGC::CodeType::llvmBc);
mockArgHelper.interceptOutput = true;
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.inputFilesContent.emplace_back(std::move(spirvFileContent.bytes), spirvFileContent.size, spirvFileContent.codeType);
mockOfflineLinker.inputFilesContent.emplace_back(std::move(llvmbcFileContent.bytes), llvmbcFileContent.size, llvmbcFileContent.codeType);
mockOfflineLinker.outputFormat = IGC::CodeType::llvmBc;
mockOfflineLinker.operationMode = OperationMode::LINK_FILES;
const auto igcInitializationResult{mockOfflineLinker.prepareIgc()};
ASSERT_EQ(OclocErrorCode::SUCCESS, igcInitializationResult);
::testing::internal::CaptureStdout();
const auto linkingResult{mockOfflineLinker.execute()};
const auto output{::testing::internal::GetCapturedStdout()};
ASSERT_EQ(OclocErrorCode::BUILD_PROGRAM_FAILURE, linkingResult);
EXPECT_EQ(0u, mockArgHelper.interceptedFiles.count("linker_output"));
const std::string expectedErrorMessage{"Error: Translation has failed! IGC returned empty output.\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenValidInputFileContentsAndInvalidTranslationOutputWhenLlvmBcOutputIsRequestedThenErrorIsReturned) {
MockCompilerDebugVars igcDebugVars{gEnvironment->igcDebugVars};
igcDebugVars.shouldReturnInvalidTranslationOutput = true;
setIgcDebugVars(igcDebugVars);
auto spirvFileContent = createFileContent(getEmptySpirvFile(), IGC::CodeType::spirV);
auto llvmbcFileContent = createFileContent(getEmptyLlvmBcFile(), IGC::CodeType::llvmBc);
mockArgHelper.interceptOutput = true;
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.inputFilesContent.emplace_back(std::move(spirvFileContent.bytes), spirvFileContent.size, spirvFileContent.codeType);
mockOfflineLinker.inputFilesContent.emplace_back(std::move(llvmbcFileContent.bytes), llvmbcFileContent.size, llvmbcFileContent.codeType);
mockOfflineLinker.outputFormat = IGC::CodeType::llvmBc;
mockOfflineLinker.operationMode = OperationMode::LINK_FILES;
const auto igcInitializationResult{mockOfflineLinker.prepareIgc()};
ASSERT_EQ(OclocErrorCode::SUCCESS, igcInitializationResult);
::testing::internal::CaptureStdout();
const auto linkingResult{mockOfflineLinker.execute()};
const auto output{::testing::internal::GetCapturedStdout()};
ASSERT_EQ(OclocErrorCode::OUT_OF_HOST_MEMORY, linkingResult);
EXPECT_EQ(0u, mockArgHelper.interceptedFiles.count("linker_output"));
const std::string expectedErrorMessage{"Error: Translation has failed! IGC output is nullptr!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenUninitializedLinkerWhenExecuteIsInvokedThenErrorIsIssued) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
::testing::internal::CaptureStdout();
const auto executionResult{mockOfflineLinker.execute()};
const auto output{::testing::internal::GetCapturedStdout()};
ASSERT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, executionResult);
ASSERT_FALSE(output.empty());
}
TEST_F(OfflineLinkerTest, GivenHelpRequestWhenExecuteIsInvokedThenHelpIsPrinted) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.operationMode = OperationMode::SHOW_HELP;
::testing::internal::CaptureStdout();
const auto executionResult{mockOfflineLinker.execute()};
const auto output{::testing::internal::GetCapturedStdout()};
ASSERT_EQ(OclocErrorCode::SUCCESS, executionResult);
ASSERT_FALSE(output.empty());
}
TEST_F(OfflineLinkerTest, GivenInvalidOperationModeWhenExecuteIsInvokedThenErrorIsIssued) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.operationMode = static_cast<OperationMode>(7);
::testing::internal::CaptureStdout();
const auto executionResult{mockOfflineLinker.execute()};
const auto output{::testing::internal::GetCapturedStdout()};
ASSERT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, executionResult);
ASSERT_FALSE(output.empty());
}
TEST_F(OfflineLinkerTest, GivenUninitializedHwInfoWhenInitIsCalledThenHwInfoIsInitialized) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
ASSERT_EQ(IGFX_UNKNOWN, mockOfflineLinker.hwInfo.platform.eProductFamily);
const auto hwInfoInitializationResult{mockOfflineLinker.initHardwareInfo()};
ASSERT_EQ(OclocErrorCode::SUCCESS, hwInfoInitializationResult);
EXPECT_NE(IGFX_UNKNOWN, mockOfflineLinker.hwInfo.platform.eProductFamily);
}
TEST_F(OfflineLinkerTest, GivenEmptyHwInfoTableWhenInitializationIsPerformedThenItFailsOnHwInit) {
const std::string firstFilename{"some_file1.spv"};
const std::string secondFilename{"some_file2.llvmbc"};
mockArgHelperFilesMap[firstFilename] = getEmptySpirvFile();
mockArgHelperFilesMap[secondFilename] = getEmptyLlvmBcFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
firstFilename,
"-file",
secondFilename};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.shouldReturnEmptyHardwareInfoTable = true;
::testing::internal::CaptureStdout();
const auto readingResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::INVALID_DEVICE, readingResult);
const std::string expectedErrorMessage{"Error! Cannot retrieve any valid hardware information!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenMissingIgcLibraryWhenInitializationIsPerformedThenItFailsOnIgcPreparation) {
const std::string firstFilename{"some_file1.spv"};
const std::string secondFilename{"some_file2.llvmbc"};
mockArgHelperFilesMap[firstFilename] = getEmptySpirvFile();
mockArgHelperFilesMap[secondFilename] = getEmptyLlvmBcFile();
const std::vector<std::string> argv = {
"ocloc.exe",
"link",
"-file",
firstFilename,
"-file",
secondFilename};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.shouldFailLoadingOfIgcLib = true;
::testing::internal::CaptureStdout();
const auto readingResult = mockOfflineLinker.initialize(argv.size(), argv);
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::OUT_OF_HOST_MEMORY, readingResult);
std::stringstream expectedErrorMessage;
expectedErrorMessage << "Error! Loading of IGC library has failed! Filename: " << Os::igcDllName << "\n";
EXPECT_EQ(expectedErrorMessage.str(), output);
}
TEST_F(OfflineLinkerTest, GivenOfflineLinkerWhenStoringValidBuildLogThenItIsSaved) {
const std::string someValidLog{"Warning: This is a build log!"};
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.tryToStoreBuildLog(someValidLog.data(), someValidLog.size());
EXPECT_EQ(someValidLog, mockOfflineLinker.getBuildLog());
}
TEST_F(OfflineLinkerTest, GivenOfflineLinkerWhenStoringInvalidBuildLogThenItIsIgnored) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.tryToStoreBuildLog(nullptr, 0);
const auto buildLog{mockOfflineLinker.getBuildLog()};
EXPECT_TRUE(buildLog.empty());
// Invalid size has been passed.
const char *log{"Info: This is a log!"};
mockOfflineLinker.tryToStoreBuildLog(log, 0);
const auto buildLog2{mockOfflineLinker.getBuildLog()};
EXPECT_TRUE(buildLog2.empty());
}
TEST_F(OfflineLinkerTest, GivenFailingLoadingOfIgcSymbolsWhenPreparingIgcThenFailureIsReported) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.shouldFailLoadingOfIgcCreateMainFunction = true;
::testing::internal::CaptureStdout();
const auto igcPreparationResult{mockOfflineLinker.prepareIgc()};
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::OUT_OF_HOST_MEMORY, igcPreparationResult);
const std::string expectedErrorMessage{"Error! Cannot load required functions from IGC library.\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenFailingCreationOfIgcMainWhenPreparingIgcThenFailureIsReported) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.shouldFailCreationOfIgcMain = true;
::testing::internal::CaptureStdout();
const auto igcPreparationResult{mockOfflineLinker.prepareIgc()};
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::OUT_OF_HOST_MEMORY, igcPreparationResult);
const std::string expectedErrorMessage{"Error! Cannot create IGC main component!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenFailingCreationOfIgcDeviceContextWhenPreparingIgcThenFailureIsReported) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.shouldFailCreationOfIgcDeviceContext = true;
::testing::internal::CaptureStdout();
const auto igcPreparationResult{mockOfflineLinker.prepareIgc()};
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::OUT_OF_HOST_MEMORY, igcPreparationResult);
const std::string expectedErrorMessage{"Error! Cannot create IGC device context!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenInvalidIgcPlatformHandleWhenPreparingIgcThenFailureIsReported) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.shouldReturnInvalidIgcPlatformHandle = true;
::testing::internal::CaptureStdout();
const auto igcPreparationResult{mockOfflineLinker.prepareIgc()};
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::OUT_OF_HOST_MEMORY, igcPreparationResult);
const std::string expectedErrorMessage{"Error! IGC device context has not been properly created!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
TEST_F(OfflineLinkerTest, GivenInvalidIgcGTSystemInfoHandleWhenPreparingIgcThenFailureIsReported) {
MockOfflineLinker mockOfflineLinker{&mockArgHelper};
mockOfflineLinker.shouldReturnInvalidGTSystemInfoHandle = true;
::testing::internal::CaptureStdout();
const auto igcPreparationResult{mockOfflineLinker.prepareIgc()};
const auto output{::testing::internal::GetCapturedStdout()};
EXPECT_EQ(OclocErrorCode::OUT_OF_HOST_MEMORY, igcPreparationResult);
const std::string expectedErrorMessage{"Error! IGC device context has not been properly created!\n"};
EXPECT_EQ(expectedErrorMessage, output);
}
} // namespace NEO

View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#pragma once
#include "gmock/gmock.h"
#include "mock/mock_argument_helper.h"
#include "mock/mock_offline_linker.h"
namespace NEO {
class OfflineLinkerTest : public ::testing::Test {
public:
void SetUp() override;
void TearDown() override;
std::string getEmptySpirvFile() const;
std::string getEmptyLlvmBcFile() const;
MockOfflineLinker::InputFileContent createFileContent(const std::string &content, IGC::CodeType::CodeType_t codeType) const;
protected:
MockOclocArgHelper::FilesMap mockArgHelperFilesMap{};
MockOclocArgHelper mockArgHelper{mockArgHelperFilesMap};
char binaryToReturn[8]{7, 7, 7, 7, 0, 1, 2, 3};
};
} // namespace NEO

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 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;