mirror of
https://github.com/intel/compute-runtime.git
synced 2026-01-08 14:02:58 +08:00
This change prevents embedding identical SPIR-V section for each target requested in fatbinary build. Instead of duplicating SPIR-V, a new file called 'generic_ir' is added to AR archive. It contains SPIR-V, which was used to build fatbinary. Build fallback in runtime has been also adjusted - if 'generic_ir' file is defined in fatbinary and there is no matching binary, then this generic SPIR-V is used to rebuild for the requested target. Additionally, MockOclocArgumentHelper::loadDataFromFile() was adjusted to ensure null-termination of returned strings. This change also removes possible undefined behavior, which was related to reading names of files from AR archive. Previously, if filename was shorter than requested target name, we tried to read more memory than allowed. Related-To: NEO-6490 Signed-off-by: Patryk Wrobel <patryk.wrobel@intel.com>
773 lines
30 KiB
C++
773 lines
30 KiB
C++
/*
|
|
* 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"};
|
|
mockArgHelperFilesMap[filename] = "";
|
|
mockArgHelper.shouldLoadDataFromFileReturnZeroSize = true;
|
|
|
|
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, 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() + 1, 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() + 1, 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
|