Support for symbol/relocation tables

Change-Id: I87890f6dc36a3454ffdcab1fb9d070fdaf91e689
This commit is contained in:
Chodor, Jaroslaw
2019-07-04 17:14:51 +02:00
committed by sys_ocldev
parent b6792ef049
commit ce061a48ef
20 changed files with 1531 additions and 52 deletions

View File

@ -0,0 +1,13 @@
#
# Copyright (C) 2019 Intel Corporation
#
# SPDX-License-Identifier: MIT
#
set(NEO_COMPILER_INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
${CMAKE_CURRENT_SOURCE_DIR}/linker.h
${CMAKE_CURRENT_SOURCE_DIR}/linker.cpp
)
set_property(GLOBAL PROPERTY NEO_COMPILER_INTERFACE ${NEO_COMPILER_INTERFACE})

View File

@ -0,0 +1,213 @@
/*
* Copyright (C) 2017-2019 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "linker.h"
#include "core/helpers/ptr_math.h"
#include "runtime/helpers/debug_helpers.h"
#if __has_include("RelocationInfo.h")
#include "RelocationInfo.h"
#else
namespace vISA {
static const uint32_t MAX_SYMBOL_NAME_LENGTH = 256;
enum GenSymType { S_NOTYPE = 0,
S_UNDEF = 1,
S_FUNC = 2,
S_GLOBAL_VAR = 3,
S_GLOBAL_VAR_CONST = 4 };
typedef struct {
uint32_t s_type;
uint32_t s_offset;
uint32_t s_size;
char s_name[MAX_SYMBOL_NAME_LENGTH];
} GenSymEntry;
enum GenRelocType { R_NONE = 0,
R_SYM_ADDR = 1 };
typedef struct {
uint32_t r_type;
uint32_t r_offset;
char r_symbol[MAX_SYMBOL_NAME_LENGTH];
} GenRelocEntry;
} // namespace vISA
#endif
#include <sstream>
namespace NEO {
bool LinkerInput::decodeGlobalVariablesSymbolTable(const void *data, uint32_t numEntries) {
auto symbolEntryIt = reinterpret_cast<const vISA::GenSymEntry *>(data);
auto symbolEntryEnd = symbolEntryIt + numEntries;
symbols.reserve(symbols.size() + numEntries);
for (; symbolEntryIt != symbolEntryEnd; ++symbolEntryIt) {
DEBUG_BREAK_IF(symbols.count(symbolEntryIt->s_name) > 0);
SymbolInfo &symbolInfo = symbols[symbolEntryIt->s_name];
symbolInfo.offset = symbolEntryIt->s_offset;
symbolInfo.size = symbolEntryIt->s_size;
switch (symbolEntryIt->s_type) {
default:
DEBUG_BREAK_IF(true);
return false;
case vISA::S_GLOBAL_VAR:
symbolInfo.type = SymbolInfo::GlobalVariable;
traits.exportsGlobalVariables = true;
break;
case vISA::S_GLOBAL_VAR_CONST:
symbolInfo.type = SymbolInfo::GlobalConstant;
traits.exportsGlobalConstants = true;
break;
}
}
return true;
}
bool LinkerInput::decodeExportedFunctionsSymbolTable(const void *data, uint32_t numEntries, uint32_t instructionsSegmentId) {
auto symbolEntryIt = reinterpret_cast<const vISA::GenSymEntry *>(data);
auto symbolEntryEnd = symbolEntryIt + numEntries;
symbols.reserve(symbols.size() + numEntries);
for (; symbolEntryIt != symbolEntryEnd; ++symbolEntryIt) {
SymbolInfo &symbolInfo = symbols[symbolEntryIt->s_name];
symbolInfo.offset = symbolEntryIt->s_offset;
symbolInfo.size = symbolEntryIt->s_size;
switch (symbolEntryIt->s_type) {
default:
DEBUG_BREAK_IF(true);
return false;
case vISA::S_GLOBAL_VAR:
symbolInfo.type = SymbolInfo::GlobalVariable;
traits.exportsGlobalVariables = true;
break;
case vISA::S_GLOBAL_VAR_CONST:
symbolInfo.type = SymbolInfo::GlobalConstant;
traits.exportsGlobalConstants = true;
break;
case vISA::S_FUNC:
symbolInfo.type = SymbolInfo::Function;
traits.exportsFunctions = true;
UNRECOVERABLE_IF((this->exportedFunctionsSegmentId != -1) && (this->exportedFunctionsSegmentId != static_cast<int32_t>(instructionsSegmentId)));
this->exportedFunctionsSegmentId = static_cast<int32_t>(instructionsSegmentId);
break;
}
}
return true;
}
bool LinkerInput::decodeRelocationTable(const void *data, uint32_t numEntries, uint32_t instructionsSegmentId) {
this->traits.requiresPatchingOfInstructionSegments = true;
auto relocEntryIt = reinterpret_cast<const vISA::GenRelocEntry *>(data);
auto relocEntryEnd = relocEntryIt + numEntries;
if (instructionsSegmentId >= relocations.size()) {
static_assert(std::is_nothrow_move_constructible<decltype(relocations[0])>::value, "");
relocations.resize(instructionsSegmentId + 1);
}
auto &outRelocInfo = relocations[instructionsSegmentId];
outRelocInfo.reserve(numEntries);
for (; relocEntryIt != relocEntryEnd; ++relocEntryIt) {
RelocationInfo relocInfo{};
relocInfo.offset = relocEntryIt->r_offset;
relocInfo.symbolName = relocEntryIt->r_symbol;
switch (relocEntryIt->r_type) {
default:
DEBUG_BREAK_IF(true);
return false;
case vISA::R_SYM_ADDR:
break;
}
outRelocInfo.push_back(std::move(relocInfo));
}
return true;
}
bool Linker::processRelocations(const Segment &globalVariables, const Segment &globalConstants, const Segment &exportedFunctions) {
relocatedSymbols.reserve(data.getSymbols().size());
for (auto &symbol : data.getSymbols()) {
const Segment *seg = nullptr;
switch (symbol.second.type) {
default:
DEBUG_BREAK_IF(true);
return false;
case SymbolInfo::GlobalVariable:
seg = &globalVariables;
break;
case SymbolInfo::GlobalConstant:
seg = &globalConstants;
break;
case SymbolInfo::Function:
seg = &exportedFunctions;
break;
}
uintptr_t gpuAddress = seg->gpuAddress + symbol.second.offset;
if (symbol.second.offset + symbol.second.size > seg->segmentSize) {
DEBUG_BREAK_IF(true);
return false;
}
relocatedSymbols[symbol.first] = {symbol.second, gpuAddress};
}
return true;
}
bool Linker::patchInstructionsSegments(const std::vector<PatchableSegment> &instructionsSegments, std::vector<UnresolvedExternal> &outUnresolvedExternals) {
if (false == data.getTraits().requiresPatchingOfInstructionSegments) {
return true;
}
UNRECOVERABLE_IF(data.getRelocations().size() > instructionsSegments.size());
std::vector<UnresolvedExternal> unresolvedExternals;
auto segIt = instructionsSegments.begin();
for (auto relocsIt = data.getRelocations().begin(), relocsEnd = data.getRelocations().end();
relocsIt != relocsEnd; ++relocsIt, ++segIt) {
auto &thisSegmentRelocs = *relocsIt;
const PatchableSegment &instSeg = *segIt;
for (const auto &relocation : thisSegmentRelocs) {
auto relocAddress = ptrOffset(instSeg.hostPointer, relocation.offset);
auto symbolIt = relocatedSymbols.find(relocation.symbolName);
bool invalidOffset = relocation.offset + sizeof(uintptr_t) > instSeg.segmentSize;
bool unresolvedExternal = (symbolIt == relocatedSymbols.end());
DEBUG_BREAK_IF(invalidOffset);
if (invalidOffset || unresolvedExternal) {
uint32_t segId = static_cast<uint32_t>(segIt - instructionsSegments.begin());
unresolvedExternals.push_back(UnresolvedExternal{relocation, segId, invalidOffset});
continue;
}
*reinterpret_cast<uintptr_t *>(relocAddress) = symbolIt->second.gpuAddress;
}
}
outUnresolvedExternals.swap(unresolvedExternals);
return outUnresolvedExternals.size() == 0;
}
std::string constructLinkerErrorMessage(const Linker::UnresolvedExternals &unresolvedExternals, const std::vector<std::string> &instructionsSegmentsNames) {
std::stringstream errorStream;
if (unresolvedExternals.size() == 0) {
errorStream << "Internal linker error";
} else {
for (const auto &unresExtern : unresolvedExternals) {
if (unresExtern.internalError) {
errorStream << "error : internal linker error while handling symbol ";
} else {
errorStream << "error : unresolved external symbol ";
}
errorStream << unresExtern.unresolvedRelocation.symbolName << " at offset " << unresExtern.unresolvedRelocation.offset
<< " in instructions segment #" << unresExtern.instructionsSegmentId;
if (instructionsSegmentsNames.size() > unresExtern.instructionsSegmentId) {
errorStream << " (aka " << instructionsSegmentsNames[unresExtern.instructionsSegmentId] << ")";
}
errorStream << "\n";
}
}
return errorStream.str();
}
} // namespace NEO

View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2017-2019 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#pragma once
#include <cstdint>
#include <limits>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
namespace NEO {
struct SymbolInfo {
enum Type : uint32_t {
Unknown,
GlobalConstant,
GlobalVariable,
Function
};
uint32_t offset = std::numeric_limits<uint32_t>::max();
uint32_t size = std::numeric_limits<uint32_t>::max();
Type type = Unknown;
};
struct LinkerInput {
union Traits {
Traits() : packed(0) {
}
struct {
bool exportsGlobalVariables : 1;
bool exportsGlobalConstants : 1;
bool exportsFunctions : 1;
bool requiresPatchingOfInstructionSegments : 1;
};
uint32_t packed;
};
static_assert(sizeof(Traits) == sizeof(Traits::packed), "");
struct RelocationInfo {
std::string symbolName;
uint32_t offset = std::numeric_limits<uint32_t>::max();
};
using Relocations = std::vector<RelocationInfo>;
using SymbolMap = std::unordered_map<std::string, SymbolInfo>;
using RelocationsPerInstSegment = std::vector<Relocations>;
bool decodeGlobalVariablesSymbolTable(const void *data, uint32_t numEntries);
bool decodeExportedFunctionsSymbolTable(const void *data, uint32_t numEntries, uint32_t instructionsSegmentId);
bool decodeRelocationTable(const void *data, uint32_t numEntries, uint32_t instructionsSegmentId);
const Traits &getTraits() const {
return traits;
}
int32_t getExportedFunctionsSegmentId() const {
return exportedFunctionsSegmentId;
}
const SymbolMap &getSymbols() const {
return symbols;
}
const RelocationsPerInstSegment &getRelocations() const {
return relocations;
}
protected:
Traits traits;
SymbolMap symbols;
RelocationsPerInstSegment relocations;
int32_t exportedFunctionsSegmentId = -1;
};
struct Linker {
using RelocationInfo = LinkerInput::RelocationInfo;
struct Segment {
uintptr_t gpuAddress = std::numeric_limits<uintptr_t>::max();
size_t segmentSize = std::numeric_limits<size_t>::max();
};
struct PatchableSegment {
void *hostPointer = nullptr;
size_t segmentSize = std::numeric_limits<size_t>::max();
};
struct UnresolvedExternal {
RelocationInfo unresolvedRelocation;
uint32_t instructionsSegmentId = std::numeric_limits<uint32_t>::max();
bool internalError = false;
};
struct RelocatedSymbol {
SymbolInfo symbol;
uintptr_t gpuAddress = std::numeric_limits<uintptr_t>::max();
};
using RelocatedSymbolsMap = std::unordered_map<std::string, RelocatedSymbol>;
using PatchableSegments = std::vector<PatchableSegment>;
using UnresolvedExternals = std::vector<UnresolvedExternal>;
Linker(const LinkerInput &data)
: data(data) {
}
bool link(const Segment &globalVariables, const Segment &globalConstants, const Segment &exportedFunctions,
const PatchableSegments &instructionsSegments,
UnresolvedExternals &outUnresolvedExternals) {
return processRelocations(globalVariables, globalConstants, exportedFunctions) && patchInstructionsSegments(instructionsSegments, outUnresolvedExternals);
}
RelocatedSymbolsMap extractRelocatedSymbols() {
return RelocatedSymbolsMap(std::move(relocatedSymbols));
}
protected:
const LinkerInput &data;
RelocatedSymbolsMap relocatedSymbols;
bool processRelocations(const Segment &globalVariables, const Segment &globalConstants, const Segment &exportedFunctions);
bool patchInstructionsSegments(const std::vector<PatchableSegment> &instructionsSegments, std::vector<UnresolvedExternal> &outUnresolvedExternals);
};
std::string constructLinkerErrorMessage(const Linker::UnresolvedExternals &unresolvedExternals, const std::vector<std::string> &instructionsSegmentsNames);
} // namespace NEO

View File

@ -0,0 +1,13 @@
#
# Copyright (C) 2019 Intel Corporation
#
# SPDX-License-Identifier: MIT
#
set(NEO_CORE_COMPILER_INTERFACE_TESTS
${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
${CMAKE_CURRENT_SOURCE_DIR}/linker_mock.h
${CMAKE_CURRENT_SOURCE_DIR}/linker_tests.cpp
)
set_property(GLOBAL PROPERTY NEO_CORE_COMPILER_INTERFACE_TESTS ${NEO_CORE_COMPILER_INTERFACE_TESTS})

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2019 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#pragma once
#include "core/compiler_interface/linker.h"
template <class BaseClass>
struct WhiteBox;
template <>
struct WhiteBox<NEO::LinkerInput> : NEO::LinkerInput {
using BaseClass = NEO::LinkerInput;
using BaseClass::exportedFunctionsSegmentId;
using BaseClass::relocations;
using BaseClass::symbols;
using BaseClass::traits;
};

View File

@ -0,0 +1,522 @@
/*
* Copyright (C) 2019 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "linker_mock.h"
#include <string>
#if __has_include("RelocationInfo.h")
#include "RelocationInfo.h"
#else
namespace vISA {
static const uint32_t MAX_SYMBOL_NAME_LENGTH = 256;
enum GenSymType {
S_NOTYPE = 0,
S_UNDEF = 1,
S_FUNC = 2,
S_GLOBAL_VAR = 3,
S_GLOBAL_VAR_CONST = 4
};
typedef struct {
uint32_t s_type;
uint32_t s_offset;
uint32_t s_size;
char s_name[MAX_SYMBOL_NAME_LENGTH];
} GenSymEntry;
enum GenRelocType {
R_NONE = 0,
R_SYM_ADDR = 1
};
typedef struct {
uint32_t r_type;
uint32_t r_offset;
char r_symbol[MAX_SYMBOL_NAME_LENGTH];
} GenRelocEntry;
} // namespace vISA
#endif
TEST(LinkerInputTests, givenGlobalsSymbolTableThenProperlyDecodesGlobalVariablesAndGlobalConstants) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry[2] = {{}, {}};
entry[0].s_name[0] = 'A';
entry[0].s_offset = 8;
entry[0].s_size = 16;
entry[0].s_type = vISA::GenSymType::S_GLOBAL_VAR;
entry[1].s_name[0] = 'B';
entry[1].s_offset = 24;
entry[1].s_size = 8;
entry[1].s_type = vISA::GenSymType::S_GLOBAL_VAR_CONST;
EXPECT_EQ(0U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
auto decodeResult = linkerInput.decodeGlobalVariablesSymbolTable(entry, 2);
EXPECT_TRUE(decodeResult);
EXPECT_EQ(2U, linkerInput.getSymbols().size());
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
auto symbolA = linkerInput.getSymbols().find("A");
ASSERT_NE(linkerInput.getSymbols().end(), symbolA);
EXPECT_EQ(entry[0].s_offset, symbolA->second.offset);
EXPECT_EQ(entry[0].s_size, symbolA->second.size);
EXPECT_EQ(NEO::SymbolInfo::GlobalVariable, symbolA->second.type);
auto symbolB = linkerInput.getSymbols().find("B");
ASSERT_NE(linkerInput.getSymbols().end(), symbolB);
EXPECT_EQ(entry[1].s_offset, symbolB->second.offset);
EXPECT_EQ(entry[1].s_size, symbolB->second.size);
EXPECT_EQ(NEO::SymbolInfo::GlobalConstant, symbolB->second.type);
auto symbolC = linkerInput.getSymbols().find("C");
EXPECT_EQ(linkerInput.getSymbols().end(), symbolC);
}
TEST(LinkerInputTests, givenFunctionsSymbolTableThenProperlyDecodesGlobalVariablesAndGlobalConstants) {
// Note : this is subject to change in IGC shotly.
// GLOBAL_VAR/CONST will be ultimately allowed only in globalVariables symbol table.
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry[2] = {{}, {}};
entry[0].s_name[0] = 'A';
entry[0].s_offset = 8;
entry[0].s_size = 16;
entry[0].s_type = vISA::GenSymType::S_GLOBAL_VAR;
entry[1].s_name[0] = 'B';
entry[1].s_offset = 24;
entry[1].s_size = 8;
entry[1].s_type = vISA::GenSymType::S_GLOBAL_VAR_CONST;
EXPECT_EQ(0U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
auto decodeResult = linkerInput.decodeExportedFunctionsSymbolTable(entry, 2, 3);
EXPECT_TRUE(decodeResult);
EXPECT_EQ(2U, linkerInput.getSymbols().size());
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_TRUE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
auto symbolA = linkerInput.getSymbols().find("A");
ASSERT_NE(linkerInput.getSymbols().end(), symbolA);
EXPECT_EQ(entry[0].s_offset, symbolA->second.offset);
EXPECT_EQ(entry[0].s_size, symbolA->second.size);
EXPECT_EQ(NEO::SymbolInfo::GlobalVariable, symbolA->second.type);
auto symbolB = linkerInput.getSymbols().find("B");
ASSERT_NE(linkerInput.getSymbols().end(), symbolB);
EXPECT_EQ(entry[1].s_offset, symbolB->second.offset);
EXPECT_EQ(entry[1].s_size, symbolB->second.size);
EXPECT_EQ(NEO::SymbolInfo::GlobalConstant, symbolB->second.type);
auto symbolC = linkerInput.getSymbols().find("C");
EXPECT_EQ(linkerInput.getSymbols().end(), symbolC);
}
TEST(LinkerInputTests, givenGlobalsSymbolTableThenFunctionExportsAreNotAllowed) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry = {};
entry.s_name[0] = 'A';
entry.s_offset = 8;
entry.s_size = 16;
entry.s_type = vISA::GenSymType::S_FUNC;
auto decodeResult = linkerInput.decodeGlobalVariablesSymbolTable(&entry, 1);
EXPECT_FALSE(decodeResult);
}
TEST(LinkerInputTests, givenFunctionsSymbolTableThenProperlyDecodesExportedFunctions) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry[2] = {{}, {}};
entry[0].s_name[0] = 'A';
entry[0].s_offset = 8;
entry[0].s_size = 16;
entry[0].s_type = vISA::GenSymType::S_FUNC;
entry[1].s_name[0] = 'B';
entry[1].s_offset = 24;
entry[1].s_size = 8;
entry[1].s_type = vISA::GenSymType::S_FUNC;
EXPECT_EQ(0U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
EXPECT_EQ(-1, linkerInput.getExportedFunctionsSegmentId());
auto decodeResult = linkerInput.decodeExportedFunctionsSymbolTable(entry, 2, 3);
EXPECT_TRUE(decodeResult);
EXPECT_EQ(2U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_TRUE(linkerInput.getTraits().exportsFunctions);
EXPECT_FALSE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
auto symbolA = linkerInput.getSymbols().find("A");
ASSERT_NE(linkerInput.getSymbols().end(), symbolA);
EXPECT_EQ(entry[0].s_offset, symbolA->second.offset);
EXPECT_EQ(entry[0].s_size, symbolA->second.size);
EXPECT_EQ(NEO::SymbolInfo::Function, symbolA->second.type);
auto symbolB = linkerInput.getSymbols().find("B");
ASSERT_NE(linkerInput.getSymbols().end(), symbolB);
EXPECT_EQ(entry[1].s_offset, symbolB->second.offset);
EXPECT_EQ(entry[1].s_size, symbolB->second.size);
EXPECT_EQ(NEO::SymbolInfo::Function, symbolB->second.type);
EXPECT_EQ(3, linkerInput.getExportedFunctionsSegmentId());
}
TEST(LinkerInputTests, givenFunctionsSymbolTableThenUndefIsNotAllowed) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry entry = {};
entry.s_name[0] = 'A';
entry.s_offset = 8;
entry.s_size = 16;
entry.s_type = vISA::GenSymType::S_UNDEF;
auto decodeResult = linkerInput.decodeExportedFunctionsSymbolTable(&entry, 1, 3);
EXPECT_FALSE(decodeResult);
}
TEST(LinkerInputTests, givenRelocationTableThenRelocationEntriesAreProperlyParsed) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry entry = {};
entry.r_symbol[0] = 'A';
entry.r_offset = 8;
entry.r_type = vISA::GenRelocType::R_SYM_ADDR;
auto decodeResult = linkerInput.decodeRelocationTable(&entry, 1, 3);
EXPECT_TRUE(decodeResult);
EXPECT_EQ(0U, linkerInput.getSymbols().size());
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalVariables);
EXPECT_FALSE(linkerInput.getTraits().exportsGlobalConstants);
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
EXPECT_TRUE(linkerInput.getTraits().requiresPatchingOfInstructionSegments);
decodeResult = linkerInput.decodeRelocationTable(&entry, 1, 1);
EXPECT_TRUE(decodeResult);
}
TEST(LinkerInputTests, givenRelocationTableThenNoneIsNotAllowed) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry entry = {};
entry.r_symbol[0] = 'A';
entry.r_offset = 8;
entry.r_type = vISA::GenRelocType::R_NONE;
auto decodeResult = linkerInput.decodeRelocationTable(&entry, 1, 3);
EXPECT_FALSE(decodeResult);
}
TEST(LinkerTests, givenEmptyLinkerInputThenLinkerOutputIsEmpty) {
NEO::LinkerInput linkerInput;
NEO::Linker linker(linkerInput);
NEO::Linker::Segment globalVar, globalConst, exportedFunc;
NEO::Linker::PatchableSegments patchableInstructionSegments;
NEO::Linker::UnresolvedExternals unresolvedExternals;
bool linkResult = linker.link(globalVar, globalConst, exportedFunc, patchableInstructionSegments, unresolvedExternals);
EXPECT_TRUE(linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
}
TEST(LinkerTests, givenUnresolvedExternalThenLinkerFails) {
NEO::LinkerInput linkerInput;
vISA::GenRelocEntry entry = {};
entry.r_symbol[0] = 'A';
entry.r_offset = 8;
entry.r_type = vISA::GenRelocType::R_SYM_ADDR;
auto decodeResult = linkerInput.decodeRelocationTable(&entry, 1, 0);
EXPECT_TRUE(decodeResult);
NEO::Linker linker(linkerInput);
NEO::Linker::Segment globalVar, globalConst, exportedFunc;
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<char> instructionSegment;
instructionSegment.resize(64);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
bool linkResult = linker.link(globalVar, globalConst, exportedFunc, patchableInstructionSegments, unresolvedExternals);
EXPECT_FALSE(linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
ASSERT_EQ(1U, unresolvedExternals.size());
EXPECT_EQ(0U, unresolvedExternals[0].instructionsSegmentId);
EXPECT_FALSE(unresolvedExternals[0].internalError);
EXPECT_EQ(entry.r_offset, unresolvedExternals[0].unresolvedRelocation.offset);
EXPECT_EQ(std::string(entry.r_symbol), std::string(unresolvedExternals[0].unresolvedRelocation.symbolName));
}
TEST(LinkerTests, givenValidSymbolsAndRelocationsThenInstructionSegmentsAreProperlyPatched) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry symGlobalVariable = {};
symGlobalVariable.s_name[0] = 'A';
symGlobalVariable.s_offset = 4;
symGlobalVariable.s_size = 16;
symGlobalVariable.s_type = vISA::GenSymType::S_GLOBAL_VAR;
bool decodeSymSuccess = linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalVariable, 1);
vISA::GenSymEntry symGlobalConstant = {};
symGlobalConstant.s_name[0] = 'B';
symGlobalConstant.s_offset = 20;
symGlobalConstant.s_size = 8;
symGlobalConstant.s_type = vISA::GenSymType::S_GLOBAL_VAR_CONST;
decodeSymSuccess = decodeSymSuccess && linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalConstant, 1);
vISA::GenSymEntry symExportedFunc = {};
symExportedFunc.s_name[0] = 'C';
symExportedFunc.s_offset = 16;
symExportedFunc.s_size = 32;
symExportedFunc.s_type = vISA::GenSymType::S_FUNC;
decodeSymSuccess = decodeSymSuccess && linkerInput.decodeExportedFunctionsSymbolTable(&symExportedFunc, 1, 0);
EXPECT_TRUE(decodeSymSuccess);
vISA::GenRelocEntry relocA = {};
relocA.r_symbol[0] = 'A';
relocA.r_offset = 0;
relocA.r_type = vISA::GenRelocType::R_SYM_ADDR;
vISA::GenRelocEntry relocB = {};
relocB.r_symbol[0] = 'B';
relocB.r_offset = 8;
relocB.r_type = vISA::GenRelocType::R_SYM_ADDR;
vISA::GenRelocEntry relocC = {};
relocC.r_symbol[0] = 'C';
relocC.r_offset = 16;
relocC.r_type = vISA::GenRelocType::R_SYM_ADDR;
vISA::GenRelocEntry relocs[] = {relocA, relocB, relocC};
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocs, 3, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::Segment globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
globalConstSegment.gpuAddress = 128;
globalConstSegment.segmentSize = 256;
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<char> instructionSegment;
instructionSegment.resize(64, 0);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
bool linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment,
patchableInstructionSegments, unresolvedExternals);
EXPECT_TRUE(linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(3U, relocatedSymbols.size());
ASSERT_EQ(1U, relocatedSymbols.count(symGlobalVariable.s_name));
ASSERT_EQ(1U, relocatedSymbols.count(symGlobalConstant.s_name));
ASSERT_EQ(1U, relocatedSymbols.count(symGlobalVariable.s_name));
EXPECT_EQ(relocatedSymbols[symGlobalVariable.s_name].gpuAddress, globalVarSegment.gpuAddress + symGlobalVariable.s_offset);
EXPECT_EQ(relocatedSymbols[symGlobalConstant.s_name].gpuAddress, globalConstSegment.gpuAddress + symGlobalConstant.s_offset);
EXPECT_EQ(relocatedSymbols[symExportedFunc.s_name].gpuAddress, exportedFuncSegment.gpuAddress + symExportedFunc.s_offset);
EXPECT_EQ(relocatedSymbols[symGlobalVariable.s_name].gpuAddress, *reinterpret_cast<const uintptr_t *>(instructionSegment.data() + relocA.r_offset));
EXPECT_EQ(relocatedSymbols[symGlobalConstant.s_name].gpuAddress, *reinterpret_cast<const uintptr_t *>(instructionSegment.data() + relocB.r_offset));
EXPECT_EQ(relocatedSymbols[symExportedFunc.s_name].gpuAddress, *reinterpret_cast<const uintptr_t *>(instructionSegment.data() + relocC.r_offset));
}
TEST(LinkerTests, givenInvalidSymbolOffsetThenRelocationFails) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry symGlobalVariable = {};
symGlobalVariable.s_name[0] = 'A';
symGlobalVariable.s_offset = 64;
symGlobalVariable.s_size = 16;
symGlobalVariable.s_type = vISA::GenSymType::S_GLOBAL_VAR;
bool decodeSymSuccess = linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalVariable, 1);
EXPECT_TRUE(decodeSymSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::Segment globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = symGlobalVariable.s_offset;
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<char> instructionSegment;
instructionSegment.resize(64, 0);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
bool linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment,
patchableInstructionSegments, unresolvedExternals);
EXPECT_FALSE(linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(0U, relocatedSymbols.size());
globalVarSegment.segmentSize = symGlobalVariable.s_offset + symGlobalVariable.s_size;
linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment,
patchableInstructionSegments, unresolvedExternals);
EXPECT_TRUE(linkResult);
}
TEST(LinkerTests, givenInvalidRelocationOffsetThenPatchingOfIsaFails) {
NEO::LinkerInput linkerInput;
vISA::GenSymEntry symGlobalVariable = {};
symGlobalVariable.s_name[0] = 'A';
symGlobalVariable.s_offset = 64;
symGlobalVariable.s_size = 16;
symGlobalVariable.s_type = vISA::GenSymType::S_GLOBAL_VAR;
bool decodeSymSuccess = linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalVariable, 1);
EXPECT_TRUE(decodeSymSuccess);
vISA::GenRelocEntry relocA = {};
relocA.r_symbol[0] = 'A';
relocA.r_offset = 32;
relocA.r_type = vISA::GenRelocType::R_SYM_ADDR;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocA, 1, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::Segment globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = symGlobalVariable.s_offset + symGlobalVariable.s_size;
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<char> instructionSegment;
instructionSegment.resize(relocA.r_offset + sizeof(uintptr_t), 0);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = relocA.r_offset;
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
bool linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment,
patchableInstructionSegments, unresolvedExternals);
EXPECT_FALSE(linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(1U, relocatedSymbols.size());
ASSERT_EQ(1U, unresolvedExternals.size());
EXPECT_TRUE(unresolvedExternals[0].internalError);
patchableInstructionSegments[0].segmentSize = relocA.r_offset + sizeof(uintptr_t);
linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment,
patchableInstructionSegments, unresolvedExternals);
EXPECT_TRUE(linkResult);
}
TEST(LinkerTests, givenUnknownSymbolTypeThenRelocationFails) {
WhiteBox<NEO::LinkerInput> linkerInput;
vISA::GenSymEntry symGlobalVariable = {};
symGlobalVariable.s_name[0] = 'A';
symGlobalVariable.s_offset = 0;
symGlobalVariable.s_size = 16;
symGlobalVariable.s_type = vISA::GenSymType::S_GLOBAL_VAR;
bool decodeSymSuccess = linkerInput.decodeGlobalVariablesSymbolTable(&symGlobalVariable, 1);
EXPECT_TRUE(decodeSymSuccess);
vISA::GenRelocEntry relocA = {};
relocA.r_symbol[0] = 'A';
relocA.r_offset = 0;
relocA.r_type = vISA::GenRelocType::R_SYM_ADDR;
bool decodeRelocSuccess = linkerInput.decodeRelocationTable(&relocA, 1, 0);
EXPECT_TRUE(decodeRelocSuccess);
NEO::Linker linker(linkerInput);
NEO::Linker::Segment globalVarSegment, globalConstSegment, exportedFuncSegment;
globalVarSegment.gpuAddress = 8;
globalVarSegment.segmentSize = 64;
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<char> instructionSegment;
instructionSegment.resize(64, 0);
NEO::Linker::PatchableSegment seg0;
seg0.hostPointer = instructionSegment.data();
seg0.segmentSize = instructionSegment.size();
NEO::Linker::PatchableSegments patchableInstructionSegments{seg0};
ASSERT_EQ(1U, linkerInput.symbols.count("A"));
linkerInput.symbols["A"].type = NEO::SymbolInfo::Unknown;
bool linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment,
patchableInstructionSegments, unresolvedExternals);
EXPECT_FALSE(linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
ASSERT_EQ(0U, unresolvedExternals.size());
linkerInput.symbols["A"].type = NEO::SymbolInfo::GlobalVariable;
linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment,
patchableInstructionSegments, unresolvedExternals);
EXPECT_TRUE(linkResult);
}
TEST(LinkerErrorMessageTests, whenNoUnresolvedExternalsThenInternalError) {
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<std::string> segmentsNames{"kernel1", "kernel2"};
auto err = NEO::constructLinkerErrorMessage(unresolvedExternals, segmentsNames);
EXPECT_EQ(std::string("Internal linker error"), err);
}
TEST(LinkerErrorMessageTests, whenUnresolvedExternalsThenSymbolNameInErrorMessage) {
NEO::Linker::UnresolvedExternals unresolvedExternals;
std::vector<std::string> segmentsNames{"kernel1", "kernel2"};
NEO::Linker::UnresolvedExternal unresolvedExternal = {};
unresolvedExternal.instructionsSegmentId = 1;
unresolvedExternal.internalError = false;
unresolvedExternal.unresolvedRelocation.offset = 64;
unresolvedExternal.unresolvedRelocation.symbolName = "arrayABC";
unresolvedExternals.push_back(unresolvedExternal);
auto err = NEO::constructLinkerErrorMessage(unresolvedExternals, segmentsNames);
EXPECT_THAT(err.c_str(), ::testing::HasSubstr(unresolvedExternal.unresolvedRelocation.symbolName.c_str()));
EXPECT_THAT(err.c_str(), ::testing::HasSubstr(segmentsNames[unresolvedExternal.instructionsSegmentId].c_str()));
EXPECT_THAT(err.c_str(), ::testing::HasSubstr(std::to_string(unresolvedExternal.unresolvedRelocation.offset).c_str()));
EXPECT_THAT(err.c_str(), ::testing::Not(::testing::HasSubstr("internal error")));
unresolvedExternals[0].internalError = true;
err = NEO::constructLinkerErrorMessage(unresolvedExternals, segmentsNames);
EXPECT_THAT(err.c_str(), ::testing::HasSubstr(unresolvedExternal.unresolvedRelocation.symbolName.c_str()));
EXPECT_THAT(err.c_str(), ::testing::HasSubstr(segmentsNames[unresolvedExternal.instructionsSegmentId].c_str()));
EXPECT_THAT(err.c_str(), ::testing::HasSubstr(std::to_string(unresolvedExternal.unresolvedRelocation.offset).c_str()));
EXPECT_THAT(err.c_str(), ::testing::HasSubstr("internal linker error"));
err = NEO::constructLinkerErrorMessage(unresolvedExternals, {});
EXPECT_THAT(err.c_str(), ::testing::HasSubstr(unresolvedExternal.unresolvedRelocation.symbolName.c_str()));
EXPECT_THAT(err.c_str(), ::testing::Not(::testing::HasSubstr(segmentsNames[unresolvedExternal.instructionsSegmentId].c_str())));
EXPECT_THAT(err.c_str(), ::testing::HasSubstr(std::to_string(unresolvedExternal.unresolvedRelocation.offset).c_str()));
EXPECT_THAT(err.c_str(), ::testing::HasSubstr("internal linker error"));
}

View File

@ -65,6 +65,7 @@ target_include_directories(${NEO_STATIC_LIB_NAME} PUBLIC
${GMM_INCLUDE_PATHS} ${GMM_INCLUDE_PATHS}
${HW_SRC_INCLUDE_PATH} ${HW_SRC_INCLUDE_PATH}
${IGC_OCL_ADAPTOR_DIR} ${IGC_OCL_ADAPTOR_DIR}
${VISA_DIR}
${IGDRCL__IGC_INCLUDE_DIR} ${IGDRCL__IGC_INCLUDE_DIR}
${THIRD_PARTY_DIR} ${THIRD_PARTY_DIR}
${UMKM_SHAREDDATA_INCLUDE_PATHS} ${UMKM_SHAREDDATA_INCLUDE_PATHS}

View File

@ -1,5 +1,5 @@
# #
# Copyright (C) 2018 Intel Corporation # Copyright (C) 2018-2019 Intel Corporation
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# #
@ -16,5 +16,8 @@ set(RUNTIME_SRCS_COMPILER_INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/create_main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/create_main.cpp
) )
get_property(NEO_COMPILER_INTERFACE GLOBAL PROPERTY NEO_COMPILER_INTERFACE)
list(APPEND RUNTIME_SRCS_COMPILER_INTERFACE ${NEO_COMPILER_INTERFACE})
target_sources(${NEO_STATIC_LIB_NAME} PRIVATE ${RUNTIME_SRCS_COMPILER_INTERFACE}) target_sources(${NEO_STATIC_LIB_NAME} PRIVATE ${RUNTIME_SRCS_COMPILER_INTERFACE})
set_property(GLOBAL PROPERTY RUNTIME_SRCS_COMPILER_INTERFACE ${RUNTIME_SRCS_COMPILER_INTERFACE}) set_property(GLOBAL PROPERTY RUNTIME_SRCS_COMPILER_INTERFACE ${RUNTIME_SRCS_COMPILER_INTERFACE})

View File

@ -969,7 +969,6 @@ void Kernel::clearUnifiedMemoryExecInfo() {
} }
inline void Kernel::makeArgsResident(CommandStreamReceiver &commandStreamReceiver) { inline void Kernel::makeArgsResident(CommandStreamReceiver &commandStreamReceiver) {
auto numArgs = kernelInfo.kernelArgInfo.size(); auto numArgs = kernelInfo.kernelArgInfo.size();
for (decltype(numArgs) argIndex = 0; argIndex < numArgs; argIndex++) { for (decltype(numArgs) argIndex = 0; argIndex < numArgs; argIndex++) {
if (kernelArguments[argIndex].object) { if (kernelArguments[argIndex].object) {
@ -1005,6 +1004,10 @@ void Kernel::makeResident(CommandStreamReceiver &commandStreamReceiver) {
commandStreamReceiver.makeResident(*(program->getGlobalSurface())); commandStreamReceiver.makeResident(*(program->getGlobalSurface()));
} }
if (program->getExportedFunctionsSurface()) {
commandStreamReceiver.makeResident(*(program->getExportedFunctionsSurface()));
}
for (auto gfxAlloc : kernelSvmGfxAllocations) { for (auto gfxAlloc : kernelSvmGfxAllocations) {
commandStreamReceiver.makeResident(*gfxAlloc); commandStreamReceiver.makeResident(*gfxAlloc);
} }
@ -1045,6 +1048,11 @@ void Kernel::getResidency(std::vector<Surface *> &dst) {
dst.push_back(surface); dst.push_back(surface);
} }
if (program->getExportedFunctionsSurface()) {
GeneralSurface *surface = new GeneralSurface(program->getExportedFunctionsSurface());
dst.push_back(surface);
}
for (auto gfxAlloc : kernelSvmGfxAllocations) { for (auto gfxAlloc : kernelSvmGfxAllocations) {
GeneralSurface *surface = new GeneralSurface(gfxAlloc); GeneralSurface *surface = new GeneralSurface(gfxAlloc);
dst.push_back(surface); dst.push_back(surface);

View File

@ -179,7 +179,7 @@ cl_int Program::build(
const char *pKernelData, const char *pKernelData,
size_t kernelDataSize) { size_t kernelDataSize) {
cl_int retVal = CL_SUCCESS; cl_int retVal = CL_SUCCESS;
processKernel(pKernelData, retVal); processKernel(pKernelData, 0U, retVal);
return retVal; return retVal;
} }

View File

@ -6,12 +6,14 @@
*/ */
#include "core/helpers/ptr_math.h" #include "core/helpers/ptr_math.h"
#include "runtime/context/context.h"
#include "runtime/gtpin/gtpin_notify.h" #include "runtime/gtpin/gtpin_notify.h"
#include "runtime/helpers/aligned_memory.h" #include "runtime/helpers/aligned_memory.h"
#include "runtime/helpers/debug_helpers.h" #include "runtime/helpers/debug_helpers.h"
#include "runtime/helpers/hash.h" #include "runtime/helpers/hash.h"
#include "runtime/helpers/string.h" #include "runtime/helpers/string.h"
#include "runtime/memory_manager/memory_manager.h" #include "runtime/memory_manager/memory_manager.h"
#include "runtime/memory_manager/svm_memory_manager.h"
#include "patch_list.h" #include "patch_list.h"
#include "patch_shared.h" #include "patch_shared.h"
@ -61,6 +63,7 @@ std::string Program::getKernelNamesString() const {
size_t Program::processKernel( size_t Program::processKernel(
const void *pKernelBlob, const void *pKernelBlob,
uint32_t kernelNum,
cl_int &retVal) { cl_int &retVal) {
size_t sizeProcessed = 0; size_t sizeProcessed = 0;
@ -95,7 +98,7 @@ size_t Program::processKernel(
pKernelInfo->heapInfo.pPatchList = pCurKernelPtr; pKernelInfo->heapInfo.pPatchList = pCurKernelPtr;
retVal = parsePatchList(*pKernelInfo); retVal = parsePatchList(*pKernelInfo, kernelNum);
if (retVal != CL_SUCCESS) { if (retVal != CL_SUCCESS) {
delete pKernelInfo; delete pKernelInfo;
sizeProcessed = ptrDiff(pCurKernelPtr, pKernelBlob); sizeProcessed = ptrDiff(pCurKernelPtr, pKernelBlob);
@ -139,7 +142,7 @@ size_t Program::processKernel(
return sizeProcessed; return sizeProcessed;
} }
cl_int Program::parsePatchList(KernelInfo &kernelInfo) { cl_int Program::parsePatchList(KernelInfo &kernelInfo, uint32_t kernelNum) {
cl_int retVal = CL_SUCCESS; cl_int retVal = CL_SUCCESS;
auto pPatchList = kernelInfo.heapInfo.pPatchList; auto pPatchList = kernelInfo.heapInfo.pPatchList;
@ -804,6 +807,19 @@ cl_int Program::parsePatchList(KernelInfo &kernelInfo) {
"\n .Size", pPatch->Size); "\n .Size", pPatch->Size);
break; break;
} }
case PATCH_TOKEN_PROGRAM_SYMBOL_TABLE: {
const auto patch = reinterpret_cast<const iOpenCL::SPatchFunctionTableInfo *>(pPatch);
prepareLinkerInputStorage();
linkerInput->decodeExportedFunctionsSymbolTable(patch + 1, patch->NumEntries, kernelNum);
} break;
case PATCH_TOKEN_PROGRAM_RELOCATION_TABLE: {
const auto patch = reinterpret_cast<const iOpenCL::SPatchFunctionTableInfo *>(pPatch);
prepareLinkerInputStorage();
linkerInput->decodeRelocationTable(patch + 1, patch->NumEntries, kernelNum);
} break;
default: default:
printDebugString(DebugManager.flags.PrintDebugMessages.get(), stderr, " Program::parsePatchList. Unknown Patch Token: %d\n", pPatch->Token); printDebugString(DebugManager.flags.PrintDebugMessages.get(), stderr, " Program::parsePatchList. Unknown Patch Token: %d\n", pPatch->Token);
if (false == isSafeToSkipUnhandledToken(pPatch->Token)) { if (false == isSafeToSkipUnhandledToken(pPatch->Token)) {
@ -853,15 +869,44 @@ cl_int Program::parsePatchList(KernelInfo &kernelInfo) {
return retVal; return retVal;
} }
inline uint64_t readMisalignedUint64(const uint64_t *address) {
const uint32_t *addressBits = reinterpret_cast<const uint32_t *>(address);
return static_cast<uint64_t>(static_cast<uint64_t>(addressBits[1]) << 32) | addressBits[0];
}
GraphicsAllocation *allocateGlobalsSurface(NEO::Context *ctx, NEO::Device *device, size_t size, bool constant, bool globalsAreExported, const void *initData) {
if (globalsAreExported && (ctx != nullptr) && (ctx->getSVMAllocsManager() != nullptr)) {
NEO::SVMAllocsManager::SvmAllocationProperties svmProps = {};
svmProps.coherent = false;
svmProps.readOnly = constant;
svmProps.hostPtrReadOnly = constant;
auto ptr = ctx->getSVMAllocsManager()->createSVMAlloc(size, svmProps);
UNRECOVERABLE_IF(device == nullptr);
auto gpuAlloc = ctx->getSVMAllocsManager()->getSVMAlloc(ptr)->gpuAllocation;
device->getMemoryManager()->copyMemoryToAllocation(gpuAlloc, initData, static_cast<uint32_t>(size));
return ctx->getSVMAllocsManager()->getSVMAlloc(ptr)->gpuAllocation;
} else {
UNRECOVERABLE_IF(device == nullptr);
auto allocationType = constant ? GraphicsAllocation::AllocationType::CONSTANT_SURFACE : GraphicsAllocation::AllocationType::GLOBAL_SURFACE;
auto gpuAlloc = device->getMemoryManager()->allocateGraphicsMemoryWithProperties({size, allocationType});
memcpy_s(gpuAlloc->getUnderlyingBuffer(), gpuAlloc->getUnderlyingBufferSize(), initData, size);
return gpuAlloc;
}
}
cl_int Program::parseProgramScopePatchList() { cl_int Program::parseProgramScopePatchList() {
cl_int retVal = CL_SUCCESS; cl_int retVal = CL_SUCCESS;
cl_uint surfaceSize = 0; size_t globalVariablesSurfaceSize = 0U, globalConstantsSurfaceSize = 0U;
const void *globalVariablesInitData = nullptr, *globalConstantsInitData = nullptr;
auto pPatchList = programScopePatchList; auto pPatchList = programScopePatchList;
auto patchListSize = programScopePatchListSize; auto patchListSize = programScopePatchListSize;
auto pCurPatchListPtr = pPatchList; auto pCurPatchListPtr = pPatchList;
cl_uint headerSize = 0; cl_uint headerSize = 0;
std::vector<uint64_t> globalVariablesSelfPatches;
std::vector<uint64_t> globalConstantsSelfPatches;
while (ptrDiff(pCurPatchListPtr, pPatchList) < patchListSize) { while (ptrDiff(pCurPatchListPtr, pPatchList) < patchListSize) {
auto pPatch = reinterpret_cast<const SPatchItemHeader *>(pCurPatchListPtr); auto pPatch = reinterpret_cast<const SPatchItemHeader *>(pCurPatchListPtr);
switch (pPatch->Token) { switch (pPatch->Token) {
@ -872,12 +917,11 @@ cl_int Program::parseProgramScopePatchList() {
pDevice->getMemoryManager()->freeGraphicsMemory(constantSurface); pDevice->getMemoryManager()->freeGraphicsMemory(constantSurface);
} }
surfaceSize = patch.InlineDataSize; globalConstantsSurfaceSize = patch.InlineDataSize;
headerSize = sizeof(SPatchAllocateConstantMemorySurfaceProgramBinaryInfo); headerSize = sizeof(SPatchAllocateConstantMemorySurfaceProgramBinaryInfo);
constantSurface = pDevice->getMemoryManager()->allocateGraphicsMemoryWithProperties({surfaceSize, GraphicsAllocation::AllocationType::CONSTANT_SURFACE});
memcpy_s(constantSurface->getUnderlyingBuffer(), surfaceSize, (cl_char *)pPatch + headerSize, surfaceSize); globalConstantsInitData = (cl_char *)pPatch + headerSize;
pCurPatchListPtr = ptrOffset(pCurPatchListPtr, surfaceSize); pCurPatchListPtr = ptrOffset(pCurPatchListPtr, globalConstantsSurfaceSize);
DBG_LOG(LogPatchTokens, DBG_LOG(LogPatchTokens,
"\n .ALLOCATE_CONSTANT_MEMORY_SURFACE_PROGRAM_BINARY_INFO", pPatch->Token, "\n .ALLOCATE_CONSTANT_MEMORY_SURFACE_PROGRAM_BINARY_INFO", pPatch->Token,
"\n .Size", pPatch->Size, "\n .Size", pPatch->Size,
@ -893,13 +937,12 @@ cl_int Program::parseProgramScopePatchList() {
pDevice->getMemoryManager()->freeGraphicsMemory(globalSurface); pDevice->getMemoryManager()->freeGraphicsMemory(globalSurface);
} }
surfaceSize = patch.InlineDataSize; globalVariablesSurfaceSize = patch.InlineDataSize;
globalVarTotalSize += (size_t)surfaceSize; globalVarTotalSize += (size_t)globalVariablesSurfaceSize;
headerSize = sizeof(SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo); headerSize = sizeof(SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo);
globalSurface = pDevice->getMemoryManager()->allocateGraphicsMemoryWithProperties({surfaceSize, GraphicsAllocation::AllocationType::GLOBAL_SURFACE});
memcpy_s(globalSurface->getUnderlyingBuffer(), surfaceSize, (cl_char *)pPatch + headerSize, surfaceSize); globalVariablesInitData = (cl_char *)pPatch + headerSize;
pCurPatchListPtr = ptrOffset(pCurPatchListPtr, surfaceSize); pCurPatchListPtr = ptrOffset(pCurPatchListPtr, globalVariablesSurfaceSize);
DBG_LOG(LogPatchTokens, DBG_LOG(LogPatchTokens,
"\n .ALLOCATE_GLOBAL_MEMORY_SURFACE_PROGRAM_BINARY_INFO", pPatch->Token, "\n .ALLOCATE_GLOBAL_MEMORY_SURFACE_PROGRAM_BINARY_INFO", pPatch->Token,
"\n .Size", pPatch->Size, "\n .Size", pPatch->Size,
@ -913,12 +956,7 @@ cl_int Program::parseProgramScopePatchList() {
if (globalSurface != nullptr) { if (globalSurface != nullptr) {
auto patch = *(SPatchGlobalPointerProgramBinaryInfo *)pPatch; auto patch = *(SPatchGlobalPointerProgramBinaryInfo *)pPatch;
if ((patch.GlobalBufferIndex == 0) && (patch.BufferIndex == 0) && (patch.BufferType == PROGRAM_SCOPE_GLOBAL_BUFFER)) { if ((patch.GlobalBufferIndex == 0) && (patch.BufferIndex == 0) && (patch.BufferType == PROGRAM_SCOPE_GLOBAL_BUFFER)) {
void *pPtr = (void *)((uintptr_t)globalSurface->getUnderlyingBuffer() + (uintptr_t)patch.GlobalPointerOffset); globalVariablesSelfPatches.push_back(readMisalignedUint64(&patch.GlobalPointerOffset));
if (globalSurface->is32BitAllocation()) {
*reinterpret_cast<uint32_t *>(pPtr) += static_cast<uint32_t>(globalSurface->getGpuAddressToPatch());
} else {
*reinterpret_cast<uintptr_t *>(pPtr) += static_cast<uintptr_t>(globalSurface->getGpuAddressToPatch());
}
} else { } else {
printDebugString(DebugManager.flags.PrintDebugMessages.get(), stderr, "Program::parseProgramScopePatchList. Unhandled Data parameter: %d\n", pPatch->Token); printDebugString(DebugManager.flags.PrintDebugMessages.get(), stderr, "Program::parseProgramScopePatchList. Unhandled Data parameter: %d\n", pPatch->Token);
} }
@ -936,13 +974,7 @@ cl_int Program::parseProgramScopePatchList() {
if (constantSurface != nullptr) { if (constantSurface != nullptr) {
auto patch = *(SPatchConstantPointerProgramBinaryInfo *)pPatch; auto patch = *(SPatchConstantPointerProgramBinaryInfo *)pPatch;
if ((patch.ConstantBufferIndex == 0) && (patch.BufferIndex == 0) && (patch.BufferType == PROGRAM_SCOPE_CONSTANT_BUFFER)) { if ((patch.ConstantBufferIndex == 0) && (patch.BufferIndex == 0) && (patch.BufferType == PROGRAM_SCOPE_CONSTANT_BUFFER)) {
void *pPtr = (uintptr_t *)((uintptr_t)constantSurface->getUnderlyingBuffer() + (uintptr_t)patch.ConstantPointerOffset); globalConstantsSelfPatches.push_back(readMisalignedUint64(&patch.ConstantPointerOffset));
if (constantSurface->is32BitAllocation()) {
*reinterpret_cast<uint32_t *>(pPtr) += static_cast<uint32_t>(constantSurface->getGpuAddressToPatch());
} else {
*reinterpret_cast<uintptr_t *>(pPtr) += static_cast<uintptr_t>(constantSurface->getGpuAddressToPatch());
}
} else { } else {
printDebugString(DebugManager.flags.PrintDebugMessages.get(), stderr, "Program::parseProgramScopePatchList. Unhandled Data parameter: %d\n", pPatch->Token); printDebugString(DebugManager.flags.PrintDebugMessages.get(), stderr, "Program::parseProgramScopePatchList. Unhandled Data parameter: %d\n", pPatch->Token);
} }
@ -956,6 +988,12 @@ cl_int Program::parseProgramScopePatchList() {
} }
break; break;
case PATCH_TOKEN_PROGRAM_SYMBOL_TABLE: {
const auto patch = reinterpret_cast<const iOpenCL::SPatchFunctionTableInfo *>(pPatch);
prepareLinkerInputStorage();
linkerInput->decodeGlobalVariablesSymbolTable(patch + 1, patch->NumEntries);
} break;
default: default:
if (false == isSafeToSkipUnhandledToken(pPatch->Token)) { if (false == isSafeToSkipUnhandledToken(pPatch->Token)) {
retVal = CL_INVALID_BINARY; retVal = CL_INVALID_BINARY;
@ -972,9 +1010,97 @@ cl_int Program::parseProgramScopePatchList() {
pCurPatchListPtr = ptrOffset(pCurPatchListPtr, pPatch->Size); pCurPatchListPtr = ptrOffset(pCurPatchListPtr, pPatch->Size);
} }
if (globalConstantsSurfaceSize != 0) {
auto exportsGlobals = (linkerInput && linkerInput->getTraits().exportsGlobalConstants);
constantSurface = allocateGlobalsSurface(context, pDevice, globalConstantsSurfaceSize, true, exportsGlobals, globalConstantsInitData);
}
if (globalVariablesSurfaceSize != 0) {
auto exportsGlobals = (linkerInput && linkerInput->getTraits().exportsGlobalVariables);
globalSurface = allocateGlobalsSurface(context, pDevice, globalVariablesSurfaceSize, false, exportsGlobals, globalVariablesInitData);
}
for (auto offset : globalVariablesSelfPatches) {
UNRECOVERABLE_IF(globalSurface == nullptr);
void *pPtr = ptrOffset(globalSurface->getUnderlyingBuffer(), static_cast<size_t>(offset));
if (globalSurface->is32BitAllocation()) {
*reinterpret_cast<uint32_t *>(pPtr) += static_cast<uint32_t>(globalSurface->getGpuAddressToPatch());
} else {
*reinterpret_cast<uintptr_t *>(pPtr) += static_cast<uintptr_t>(globalSurface->getGpuAddressToPatch());
}
}
for (auto offset : globalConstantsSelfPatches) {
UNRECOVERABLE_IF(constantSurface == nullptr);
void *pPtr = ptrOffset(constantSurface->getUnderlyingBuffer(), static_cast<size_t>(offset));
if (constantSurface->is32BitAllocation()) {
*reinterpret_cast<uint32_t *>(pPtr) += static_cast<uint32_t>(constantSurface->getGpuAddressToPatch());
} else {
*reinterpret_cast<uintptr_t *>(pPtr) += static_cast<uintptr_t>(constantSurface->getGpuAddressToPatch());
}
}
return retVal; return retVal;
} }
cl_int Program::linkBinary() {
if (linkerInput != nullptr) {
Linker linker(*linkerInput);
Linker::Segment globals;
Linker::Segment constants;
Linker::Segment exportedFunctions;
if (this->globalSurface != nullptr) {
globals.gpuAddress = static_cast<uintptr_t>(this->globalSurface->getGpuAddress());
globals.segmentSize = this->globalSurface->getUnderlyingBufferSize();
}
if (this->constantSurface != nullptr) {
constants.gpuAddress = static_cast<uintptr_t>(this->constantSurface->getGpuAddress());
constants.segmentSize = this->constantSurface->getUnderlyingBufferSize();
}
if (this->linkerInput->getExportedFunctionsSegmentId() >= 0) {
// Exported functions reside in instruction heap of one of kernels
auto exportedFunctionHeapId = this->linkerInput->getExportedFunctionsSegmentId();
this->exportedFunctionsSurface = this->kernelInfoArray[exportedFunctionHeapId]->getGraphicsAllocation();
exportedFunctions.gpuAddress = static_cast<uintptr_t>(exportedFunctionsSurface->getGpuAddressToPatch());
exportedFunctions.segmentSize = exportedFunctionsSurface->getUnderlyingBufferSize();
}
Linker::PatchableSegments isaSegmentsForPatching;
std::vector<std::vector<char>> patchedIsaTempStorage;
if (linkerInput->getTraits().requiresPatchingOfInstructionSegments) {
patchedIsaTempStorage.reserve(this->kernelInfoArray.size());
for (const auto &kernelInfo : this->kernelInfoArray) {
auto &kernHeapInfo = kernelInfo->heapInfo;
const char *originalIsa = reinterpret_cast<const char *>(kernHeapInfo.pKernelHeap);
patchedIsaTempStorage.push_back(std::vector<char>(originalIsa, originalIsa + kernHeapInfo.pKernelHeader->KernelHeapSize));
isaSegmentsForPatching.push_back(Linker::PatchableSegment{patchedIsaTempStorage.rbegin()->data(), kernHeapInfo.pKernelHeader->KernelHeapSize});
}
}
Linker::UnresolvedExternals unresolvedExternalsInfo;
bool linkSuccess = linker.link(globals, constants, exportedFunctions,
isaSegmentsForPatching, unresolvedExternalsInfo);
this->symbols = linker.extractRelocatedSymbols();
if (false == linkSuccess) {
std::vector<std::string> kernelNames;
for (const auto &kernelInfo : this->kernelInfoArray) {
kernelNames.push_back("kernel : " + kernelInfo->name);
}
auto error = constructLinkerErrorMessage(unresolvedExternalsInfo, kernelNames);
updateBuildLog(pDevice, error.c_str(), error.size());
return CL_INVALID_BINARY;
} else {
for (const auto &kernelInfo : this->kernelInfoArray) {
auto &kernHeapInfo = kernelInfo->heapInfo;
auto segmentId = &kernelInfo - &this->kernelInfoArray[0];
this->pDevice->getMemoryManager()->copyMemoryToAllocation(kernelInfo->getGraphicsAllocation(),
isaSegmentsForPatching[segmentId].hostPointer,
kernHeapInfo.pKernelHeader->KernelHeapSize);
}
}
}
return CL_SUCCESS;
}
cl_int Program::processGenBinary() { cl_int Program::processGenBinary() {
cl_int retVal = CL_SUCCESS; cl_int retVal = CL_SUCCESS;
@ -997,20 +1123,24 @@ cl_int Program::processGenBinary() {
programScopePatchList = pCurBinaryPtr; programScopePatchList = pCurBinaryPtr;
programScopePatchListSize = pGenBinaryHeader->PatchListSize; programScopePatchListSize = pGenBinaryHeader->PatchListSize;
if (programScopePatchListSize != 0u) {
retVal = parseProgramScopePatchList();
}
pCurBinaryPtr = ptrOffset(pCurBinaryPtr, pGenBinaryHeader->PatchListSize); pCurBinaryPtr = ptrOffset(pCurBinaryPtr, pGenBinaryHeader->PatchListSize);
auto numKernels = pGenBinaryHeader->NumberOfKernels; auto numKernels = pGenBinaryHeader->NumberOfKernels;
for (uint32_t i = 0; i < numKernels && retVal == CL_SUCCESS; i++) { for (uint32_t i = 0; i < numKernels && retVal == CL_SUCCESS; i++) {
size_t bytesProcessed = processKernel(pCurBinaryPtr, retVal); size_t bytesProcessed = processKernel(pCurBinaryPtr, i, retVal);
pCurBinaryPtr = ptrOffset(pCurBinaryPtr, bytesProcessed); pCurBinaryPtr = ptrOffset(pCurBinaryPtr, bytesProcessed);
} }
if (programScopePatchListSize != 0u) {
retVal = parseProgramScopePatchList();
}
} while (false); } while (false);
if (retVal == CL_SUCCESS) {
retVal = linkBinary();
}
return retVal; return retVal;
} }

View File

@ -14,6 +14,7 @@
#include "runtime/helpers/hw_helper.h" #include "runtime/helpers/hw_helper.h"
#include "runtime/helpers/string.h" #include "runtime/helpers/string.h"
#include "runtime/memory_manager/memory_manager.h" #include "runtime/memory_manager/memory_manager.h"
#include "runtime/memory_manager/svm_memory_manager.h"
#include <sstream> #include <sstream>
@ -108,14 +109,23 @@ Program::~Program() {
delete blockKernelManager; delete blockKernelManager;
if (constantSurface) { if (constantSurface) {
this->executionEnvironment.memoryManager->checkGpuUsageAndDestroyGraphicsAllocations(constantSurface); if ((nullptr != context) && (nullptr != context->getSVMAllocsManager()) && (context->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(constantSurface->getGpuAddress())))) {
context->getSVMAllocsManager()->freeSVMAlloc(reinterpret_cast<void *>(constantSurface->getGpuAddress()));
} else {
this->executionEnvironment.memoryManager->checkGpuUsageAndDestroyGraphicsAllocations(constantSurface);
}
constantSurface = nullptr; constantSurface = nullptr;
} }
if (globalSurface) { if (globalSurface) {
this->executionEnvironment.memoryManager->checkGpuUsageAndDestroyGraphicsAllocations(globalSurface); if ((nullptr != context) && (nullptr != context->getSVMAllocsManager()) && (context->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(globalSurface->getGpuAddress())))) {
context->getSVMAllocsManager()->freeSVMAlloc(reinterpret_cast<void *>(globalSurface->getGpuAddress()));
} else {
this->executionEnvironment.memoryManager->checkGpuUsageAndDestroyGraphicsAllocations(globalSurface);
}
globalSurface = nullptr; globalSurface = nullptr;
} }
if (context && !isBuiltIn) { if (context && !isBuiltIn) {
context->decRefInternal(); context->decRefInternal();
} }
@ -493,4 +503,10 @@ void Program::updateNonUniformFlag(const Program **inputPrograms, size_t numInpu
} }
this->allowNonUniform = allowNonUniform; this->allowNonUniform = allowNonUniform;
} }
void Program::prepareLinkerInputStorage() {
if (this->linkerInput == nullptr) {
this->linkerInput = std::make_unique<LinkerInput>();
}
}
} // namespace NEO } // namespace NEO

View File

@ -6,6 +6,7 @@
*/ */
#pragma once #pragma once
#include "core/compiler_interface/linker.h"
#include "elf/reader.h" #include "elf/reader.h"
#include "elf/writer.h" #include "elf/writer.h"
#include "runtime/api/cl_types.h" #include "runtime/api/cl_types.h"
@ -148,6 +149,7 @@ class Program : public BaseObject<_cl_program> {
} }
const Device &getDevice(cl_uint deviceOrdinal) const { const Device &getDevice(cl_uint deviceOrdinal) const {
UNRECOVERABLE_IF(pDevice == nullptr);
return *pDevice; return *pDevice;
} }
@ -206,6 +208,10 @@ class Program : public BaseObject<_cl_program> {
return globalSurface; return globalSurface;
} }
GraphicsAllocation *getExportedFunctionsSurface() const {
return exportedFunctionsSurface;
}
BlockKernelManager *getBlockKernelManager() const { BlockKernelManager *getBlockKernelManager() const {
return blockKernelManager; return blockKernelManager;
} }
@ -258,6 +264,14 @@ class Program : public BaseObject<_cl_program> {
return this->specConstantsSizes; return this->specConstantsSizes;
} }
const Linker::RelocatedSymbolsMap &getSymbols() const {
return this->symbols;
}
LinkerInput *getLinkerInput() const {
return this->linkerInput.get();
}
protected: protected:
Program(ExecutionEnvironment &executionEnvironment); Program(ExecutionEnvironment &executionEnvironment);
@ -274,13 +288,15 @@ class Program : public BaseObject<_cl_program> {
cl_int resolveProgramBinary(); cl_int resolveProgramBinary();
MOCKABLE_VIRTUAL cl_int linkBinary();
cl_int parseProgramScopePatchList(); cl_int parseProgramScopePatchList();
MOCKABLE_VIRTUAL cl_int rebuildProgramFromIr(); MOCKABLE_VIRTUAL cl_int rebuildProgramFromIr();
cl_int parsePatchList(KernelInfo &pKernelInfo); cl_int parsePatchList(KernelInfo &pKernelInfo, uint32_t kernelNum);
size_t processKernel(const void *pKernelBlob, cl_int &retVal); size_t processKernel(const void *pKernelBlob, uint32_t kernelNum, cl_int &retVal);
void storeBinary(char *&pDst, size_t &dstSize, const void *pSrc, const size_t srcSize); void storeBinary(char *&pDst, size_t &dstSize, const void *pSrc, const size_t srcSize);
@ -300,6 +316,8 @@ class Program : public BaseObject<_cl_program> {
MOCKABLE_VIRTUAL bool appendKernelDebugOptions(); MOCKABLE_VIRTUAL bool appendKernelDebugOptions();
void notifyDebuggerWithSourceCode(std::string &filename); void notifyDebuggerWithSourceCode(std::string &filename);
void prepareLinkerInputStorage();
static const std::string clOptNameClVer; static const std::string clOptNameClVer;
static const std::string clOptNameUniformWgs; static const std::string clOptNameUniformWgs;
@ -329,6 +347,7 @@ class Program : public BaseObject<_cl_program> {
GraphicsAllocation *constantSurface; GraphicsAllocation *constantSurface;
GraphicsAllocation *globalSurface; GraphicsAllocation *globalSurface;
GraphicsAllocation *exportedFunctionsSurface = nullptr;
size_t globalVarTotalSize; size_t globalVarTotalSize;
@ -346,6 +365,9 @@ class Program : public BaseObject<_cl_program> {
uint32_t programOptionVersion; uint32_t programOptionVersion;
bool allowNonUniform; bool allowNonUniform;
std::unique_ptr<LinkerInput> linkerInput;
Linker::RelocatedSymbolsMap symbols;
std::map<const Device *, std::string> buildLog; std::map<const Device *, std::string> buildLog;
bool areSpecializationConstantsInitialized = false; bool areSpecializationConstantsInitialized = false;

View File

@ -1,5 +1,5 @@
# #
# Copyright (C) 2017-2018 Intel Corporation # Copyright (C) 2017-2019 Intel Corporation
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# #
@ -9,4 +9,8 @@ set(IGDRCL_SRCS_tests_compiler_interface
${CMAKE_CURRENT_SOURCE_DIR}/binary_cache_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/binary_cache_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/compiler_interface_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/compiler_interface_tests.cpp
) )
get_property(NEO_CORE_COMPILER_INTERFACE_TESTS GLOBAL PROPERTY NEO_CORE_COMPILER_INTERFACE_TESTS)
list(APPEND IGDRCL_SRCS_tests_compiler_interface ${NEO_CORE_COMPILER_INTERFACE_TESTS})
target_sources(igdrcl_tests PRIVATE ${IGDRCL_SRCS_tests_compiler_interface}) target_sources(igdrcl_tests PRIVATE ${IGDRCL_SRCS_tests_compiler_interface})

View File

@ -94,6 +94,11 @@ TEST_P(KernelArgInfoTest, Create_Simple) {
// included in the setup of fixture // included in the setup of fixture
} }
TEST_P(KernelArgInfoTest, WhenQueryingWithNullptrKernelNameTheniReturnNullptr) {
auto kernelInfo = this->pProgram->getKernelInfo(nullptr);
EXPECT_EQ(nullptr, kernelInfo);
}
TEST_P(KernelArgInfoTest, getKernelArgAcessQualifier) { TEST_P(KernelArgInfoTest, getKernelArgAcessQualifier) {
cl_kernel_arg_access_qualifier param_value = 0; cl_kernel_arg_access_qualifier param_value = 0;
queryArgInfo<cl_kernel_arg_access_qualifier>(CL_KERNEL_ARG_ACCESS_QUALIFIER, param_value); queryArgInfo<cl_kernel_arg_access_qualifier>(CL_KERNEL_ARG_ACCESS_QUALIFIER, param_value);

View File

@ -436,6 +436,8 @@ class CommandStreamReceiverMock : public CommandStreamReceiver {
typedef CommandStreamReceiver BaseClass; typedef CommandStreamReceiver BaseClass;
public: public:
using CommandStreamReceiver::executionEnvironment;
using BaseClass::CommandStreamReceiver; using BaseClass::CommandStreamReceiver;
CommandStreamReceiverMock() : BaseClass(*(new ExecutionEnvironment)) { CommandStreamReceiverMock() : BaseClass(*(new ExecutionEnvironment)) {
this->mockExecutionEnvironment.reset(&this->executionEnvironment); this->mockExecutionEnvironment.reset(&this->executionEnvironment);
@ -443,12 +445,16 @@ class CommandStreamReceiverMock : public CommandStreamReceiver {
void makeResident(GraphicsAllocation &graphicsAllocation) override { void makeResident(GraphicsAllocation &graphicsAllocation) override {
residency[graphicsAllocation.getUnderlyingBuffer()] = graphicsAllocation.getUnderlyingBufferSize(); residency[graphicsAllocation.getUnderlyingBuffer()] = graphicsAllocation.getUnderlyingBufferSize();
CommandStreamReceiver::makeResident(graphicsAllocation); if (passResidencyCallToBaseClass) {
CommandStreamReceiver::makeResident(graphicsAllocation);
}
} }
void makeNonResident(GraphicsAllocation &graphicsAllocation) override { void makeNonResident(GraphicsAllocation &graphicsAllocation) override {
residency.erase(graphicsAllocation.getUnderlyingBuffer()); residency.erase(graphicsAllocation.getUnderlyingBuffer());
CommandStreamReceiver::makeNonResident(graphicsAllocation); if (passResidencyCallToBaseClass) {
CommandStreamReceiver::makeNonResident(graphicsAllocation);
}
} }
FlushStamp flush(BatchBuffer &batchBuffer, ResidencyContainer &allocationsForResidency) override { FlushStamp flush(BatchBuffer &batchBuffer, ResidencyContainer &allocationsForResidency) override {
@ -479,6 +485,7 @@ class CommandStreamReceiverMock : public CommandStreamReceiver {
} }
std::map<const void *, size_t> residency; std::map<const void *, size_t> residency;
bool passResidencyCallToBaseClass = true;
std::unique_ptr<ExecutionEnvironment> mockExecutionEnvironment; std::unique_ptr<ExecutionEnvironment> mockExecutionEnvironment;
}; };
@ -1616,6 +1623,76 @@ HWTEST_F(KernelResidencyTest, givenKernelWhenMakeResidentIsCalledThenKernelIsaIs
memoryManager->freeGraphicsMemory(pKernelInfo->kernelAllocation); memoryManager->freeGraphicsMemory(pKernelInfo->kernelAllocation);
} }
HWTEST_F(KernelResidencyTest, givenKernelWhenMakeResidentIsCalledThenExportedFunctionsIsaAllocationIsMadeResident) {
auto pKernelInfo = std::make_unique<KernelInfo>();
auto &commandStreamReceiver = pDevice->getUltCommandStreamReceiver<FamilyType>();
commandStreamReceiver.storeMakeResidentAllocations = true;
auto memoryManager = commandStreamReceiver.getMemoryManager();
pKernelInfo->kernelAllocation = memoryManager->allocateGraphicsMemoryWithProperties(MockAllocationProperties{MemoryConstants::pageSize});
MockProgram program(*pDevice->getExecutionEnvironment());
auto exportedFunctionsSurface = std::make_unique<MockGraphicsAllocation>();
program.exportedFunctionsSurface = exportedFunctionsSurface.get();
std::unique_ptr<MockKernel> pKernel(new MockKernel(&program, *pKernelInfo, *pDevice));
ASSERT_EQ(CL_SUCCESS, pKernel->initialize());
EXPECT_EQ(0u, commandStreamReceiver.makeResidentAllocations.size());
pKernel->makeResident(pDevice->getCommandStreamReceiver());
EXPECT_TRUE(commandStreamReceiver.isMadeResident(program.exportedFunctionsSurface));
// check getResidency as well
std::vector<NEO::Surface *> residencySurfaces;
pKernel->getResidency(residencySurfaces);
std::unique_ptr<NEO::ExecutionEnvironment> mockCsrExecEnv;
{
CommandStreamReceiverMock csrMock;
csrMock.passResidencyCallToBaseClass = false;
for (const auto &s : residencySurfaces) {
s->makeResident(csrMock);
delete s;
}
EXPECT_EQ(1U, csrMock.residency.count(exportedFunctionsSurface->getUnderlyingBuffer()));
mockCsrExecEnv = std::move(csrMock.mockExecutionEnvironment);
}
memoryManager->freeGraphicsMemory(pKernelInfo->kernelAllocation);
}
HWTEST_F(KernelResidencyTest, givenKernelWhenMakeResidentIsCalledThenGlobalBufferIsMadeResident) {
auto pKernelInfo = std::make_unique<KernelInfo>();
auto &commandStreamReceiver = pDevice->getUltCommandStreamReceiver<FamilyType>();
commandStreamReceiver.storeMakeResidentAllocations = true;
auto memoryManager = commandStreamReceiver.getMemoryManager();
pKernelInfo->kernelAllocation = memoryManager->allocateGraphicsMemoryWithProperties(MockAllocationProperties{MemoryConstants::pageSize});
MockProgram program(*pDevice->getExecutionEnvironment());
program.globalSurface = new MockGraphicsAllocation();
std::unique_ptr<MockKernel> pKernel(new MockKernel(&program, *pKernelInfo, *pDevice));
ASSERT_EQ(CL_SUCCESS, pKernel->initialize());
EXPECT_EQ(0u, commandStreamReceiver.makeResidentAllocations.size());
pKernel->makeResident(pDevice->getCommandStreamReceiver());
EXPECT_TRUE(commandStreamReceiver.isMadeResident(program.globalSurface));
std::vector<NEO::Surface *> residencySurfaces;
pKernel->getResidency(residencySurfaces);
std::unique_ptr<NEO::ExecutionEnvironment> mockCsrExecEnv;
{
CommandStreamReceiverMock csrMock;
csrMock.passResidencyCallToBaseClass = false;
for (const auto &s : residencySurfaces) {
s->makeResident(csrMock);
delete s;
}
EXPECT_EQ(1U, csrMock.residency.count(program.globalSurface->getUnderlyingBuffer()));
mockCsrExecEnv = std::move(csrMock.mockExecutionEnvironment);
}
memoryManager->freeGraphicsMemory(pKernelInfo->kernelAllocation);
}
HWTEST_F(KernelResidencyTest, givenKernelWhenItUsesIndirectUnifiedMemoryDeviceAllocationThenTheyAreMadeResident) { HWTEST_F(KernelResidencyTest, givenKernelWhenItUsesIndirectUnifiedMemoryDeviceAllocationThenTheyAreMadeResident) {
MockKernelWithInternals mockKernel(*this->pDevice); MockKernelWithInternals mockKernel(*this->pDevice);
auto &commandStreamReceiver = this->pDevice->getUltCommandStreamReceiver<FamilyType>(); auto &commandStreamReceiver = this->pDevice->getUltCommandStreamReceiver<FamilyType>();

View File

@ -25,25 +25,35 @@ class GraphicsAllocation;
class MockProgram : public Program { class MockProgram : public Program {
public: public:
using Program::createProgramFromBinary; using Program::createProgramFromBinary;
using Program::getKernelNamesString;
using Program::getProgramCompilerVersion; using Program::getProgramCompilerVersion;
using Program::isKernelDebugEnabled; using Program::isKernelDebugEnabled;
using Program::linkBinary;
using Program::prepareLinkerInputStorage;
using Program::rebuildProgramFromIr; using Program::rebuildProgramFromIr;
using Program::resolveProgramBinary; using Program::resolveProgramBinary;
using Program::updateNonUniformFlag; using Program::updateNonUniformFlag;
using Program::areSpecializationConstantsInitialized; using Program::areSpecializationConstantsInitialized;
using Program::constantSurface;
using Program::context;
using Program::elfBinary; using Program::elfBinary;
using Program::elfBinarySize; using Program::elfBinarySize;
using Program::exportedFunctionsSurface;
using Program::genBinary; using Program::genBinary;
using Program::genBinarySize; using Program::genBinarySize;
using Program::globalSurface;
using Program::irBinary; using Program::irBinary;
using Program::irBinarySize; using Program::irBinarySize;
using Program::isProgramBinaryResolved; using Program::isProgramBinaryResolved;
using Program::isSpirV; using Program::isSpirV;
using Program::linkerInput;
using Program::pDevice;
using Program::programBinaryType; using Program::programBinaryType;
using Program::specConstantsIds; using Program::specConstantsIds;
using Program::specConstantsSizes; using Program::specConstantsSizes;
using Program::specConstantsValues; using Program::specConstantsValues;
using Program::symbols;
using Program::sourceCode; using Program::sourceCode;

View File

@ -108,18 +108,24 @@ TEST_F(KernelDataTest, PrintfString) {
iOpenCL::SPatchString printfString; iOpenCL::SPatchString printfString;
printfString.Token = PATCH_TOKEN_STRING; printfString.Token = PATCH_TOKEN_STRING;
printfString.Size = static_cast<uint32_t>(sizeof(SPatchString) + strSize); printfString.Size = static_cast<uint32_t>(sizeof(SPatchString) + strSize);
printfString.Index = 0; printfString.Index = 0;
printfString.StringSize = static_cast<uint32_t>(strSize); printfString.StringSize = static_cast<uint32_t>(strSize);
cl_char *pPrintfString = new cl_char[printfString.Size]; iOpenCL::SPatchString emptyString;
emptyString.Token = PATCH_TOKEN_STRING;
emptyString.Size = static_cast<uint32_t>(sizeof(SPatchString));
emptyString.Index = 1;
emptyString.StringSize = 0;
cl_char *pPrintfString = new cl_char[printfString.Size + emptyString.Size];
memcpy_s(pPrintfString, sizeof(SPatchString), &printfString, sizeof(SPatchString)); memcpy_s(pPrintfString, sizeof(SPatchString), &printfString, sizeof(SPatchString));
memcpy_s((cl_char *)pPrintfString + sizeof(printfString), strSize, stringValue, strSize); memcpy_s((cl_char *)pPrintfString + sizeof(printfString), strSize, stringValue, strSize);
memcpy_s((cl_char *)pPrintfString + printfString.Size, emptyString.Size, &emptyString, emptyString.Size);
pPatchList = (void *)pPrintfString; pPatchList = (void *)pPrintfString;
patchListSize = printfString.Size; patchListSize = printfString.Size + emptyString.Size;
buildAndDecode(); buildAndDecode();
@ -1387,3 +1393,31 @@ TEST_F(KernelDataTest, PATCH_TOKEN_ALLOCATE_SIP_SURFACE) {
EXPECT_EQ(token.Token, pKernelInfo->patchInfo.pAllocateSystemThreadSurface->Token); EXPECT_EQ(token.Token, pKernelInfo->patchInfo.pAllocateSystemThreadSurface->Token);
EXPECT_EQ(token.PerThreadSystemThreadSurfaceSize, pKernelInfo->patchInfo.pAllocateSystemThreadSurface->PerThreadSystemThreadSurfaceSize); EXPECT_EQ(token.PerThreadSystemThreadSurfaceSize, pKernelInfo->patchInfo.pAllocateSystemThreadSurface->PerThreadSystemThreadSurfaceSize);
} }
TEST_F(KernelDataTest, givenSymbolTablePatchTokenThenLinkerInputIsCreated) {
SPatchFunctionTableInfo token;
token.Token = PATCH_TOKEN_PROGRAM_SYMBOL_TABLE;
token.Size = static_cast<uint32_t>(sizeof(SPatchFunctionTableInfo));
token.NumEntries = 0;
pPatchList = &token;
patchListSize = token.Size;
buildAndDecode();
EXPECT_NE(nullptr, program->linkerInput);
}
TEST_F(KernelDataTest, givenRelocationTablePatchTokenThenLinkerInputIsCreated) {
SPatchFunctionTableInfo token;
token.Token = PATCH_TOKEN_PROGRAM_RELOCATION_TABLE;
token.Size = static_cast<uint32_t>(sizeof(SPatchFunctionTableInfo));
token.NumEntries = 0;
pPatchList = &token;
patchListSize = token.Size;
buildAndDecode();
EXPECT_NE(nullptr, program->linkerInput);
}

View File

@ -5,9 +5,11 @@
* *
*/ */
#include "core/unit_tests/compiler_interface/linker_mock.h"
#include "runtime/helpers/string.h" #include "runtime/helpers/string.h"
#include "runtime/memory_manager/allocations_list.h" #include "runtime/memory_manager/allocations_list.h"
#include "runtime/memory_manager/graphics_allocation.h" #include "runtime/memory_manager/graphics_allocation.h"
#include "runtime/memory_manager/svm_memory_manager.h"
#include "runtime/platform/platform.h" #include "runtime/platform/platform.h"
#include "runtime/program/program.h" #include "runtime/program/program.h"
#include "test.h" #include "test.h"
@ -22,17 +24,17 @@ using namespace iOpenCL;
static const char constValue[] = "11223344"; static const char constValue[] = "11223344";
static const char globalValue[] = "55667788"; static const char globalValue[] = "55667788";
class ProgramDataTest : public testing::Test, template <typename ProgramType>
public ContextFixture, class ProgramDataTestBase : public testing::Test,
public PlatformFixture, public ContextFixture,
public ProgramFixture { public PlatformFixture,
public ProgramFixture {
using ContextFixture::SetUp; using ContextFixture::SetUp;
using PlatformFixture::SetUp; using PlatformFixture::SetUp;
public: public:
ProgramDataTest() { ProgramDataTestBase() {
memset(&programBinaryHeader, 0x00, sizeof(SProgramBinaryHeader)); memset(&programBinaryHeader, 0x00, sizeof(SProgramBinaryHeader));
pCurPtr = nullptr; pCurPtr = nullptr;
pProgramPatchList = nullptr; pProgramPatchList = nullptr;
@ -48,7 +50,7 @@ class ProgramDataTest : public testing::Test,
ContextFixture::SetUp(1, &device); ContextFixture::SetUp(1, &device);
ProgramFixture::SetUp(); ProgramFixture::SetUp();
CreateProgramWithSource( CreateProgramWithSource<ProgramType>(
pContext, pContext,
&device, &device,
"CopyBuffer_simd8.cl"); "CopyBuffer_simd8.cl");
@ -120,7 +122,8 @@ class ProgramDataTest : public testing::Test,
uint32_t programPatchListSize; uint32_t programPatchListSize;
}; };
void ProgramDataTest::buildAndDecodeProgramPatchList() { template <typename ProgramType>
void ProgramDataTestBase<ProgramType>::buildAndDecodeProgramPatchList() {
size_t headerSize = sizeof(SProgramBinaryHeader); size_t headerSize = sizeof(SProgramBinaryHeader);
cl_int error = CL_SUCCESS; cl_int error = CL_SUCCESS;
@ -154,6 +157,9 @@ void ProgramDataTest::buildAndDecodeProgramPatchList() {
delete[] pProgramData; delete[] pProgramData;
} }
using ProgramDataTest = ProgramDataTestBase<NEO::Program>;
using MockProgramDataTest = ProgramDataTestBase<MockProgram>;
TEST_F(ProgramDataTest, EmptyProgramBinaryHeader) { TEST_F(ProgramDataTest, EmptyProgramBinaryHeader) {
buildAndDecodeProgramPatchList(); buildAndDecodeProgramPatchList();
} }
@ -168,6 +174,113 @@ TEST_F(ProgramDataTest, AllocateConstantMemorySurfaceProgramBinaryInfo) {
EXPECT_EQ(0, memcmp(constValue, reinterpret_cast<char *>(pProgram->getConstantSurface()->getUnderlyingBuffer()), constSize)); EXPECT_EQ(0, memcmp(constValue, reinterpret_cast<char *>(pProgram->getConstantSurface()->getUnderlyingBuffer()), constSize));
} }
TEST_F(MockProgramDataTest, whenGlobalConstantsAreExportedThenAllocateSurfacesAsSvm) {
if (this->pContext->getSVMAllocsManager() == nullptr) {
return;
}
setupConstantAllocation();
std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
mockLinkerInput->traits.exportsGlobalConstants = true;
static_cast<MockProgram *>(pProgram)->linkerInput = std::move(mockLinkerInput);
buildAndDecodeProgramPatchList();
ASSERT_NE(nullptr, pProgram->getConstantSurface());
EXPECT_NE(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getConstantSurface()->getGpuAddress())));
}
TEST_F(MockProgramDataTest, whenGlobalConstantsAreNotExportedThenAllocateSurfacesAsNonSvm) {
if (this->pContext->getSVMAllocsManager() == nullptr) {
return;
}
setupConstantAllocation();
std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
mockLinkerInput->traits.exportsGlobalConstants = false;
static_cast<MockProgram *>(pProgram)->linkerInput = std::move(mockLinkerInput);
static_cast<MockProgram *>(pProgram)->context = nullptr;
buildAndDecodeProgramPatchList();
static_cast<MockProgram *>(pProgram)->context = pContext;
ASSERT_NE(nullptr, pProgram->getConstantSurface());
EXPECT_EQ(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getConstantSurface()->getGpuAddress())));
}
TEST_F(MockProgramDataTest, whenGlobalConstantsAreExportedButContextUnavailableThenAllocateSurfacesAsNonSvm) {
if (this->pContext->getSVMAllocsManager() == nullptr) {
return;
}
setupConstantAllocation();
std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
mockLinkerInput->traits.exportsGlobalConstants = true;
static_cast<MockProgram *>(pProgram)->linkerInput = std::move(mockLinkerInput);
static_cast<MockProgram *>(pProgram)->context = nullptr;
buildAndDecodeProgramPatchList();
static_cast<MockProgram *>(pProgram)->context = pContext;
ASSERT_NE(nullptr, pProgram->getConstantSurface());
EXPECT_EQ(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getConstantSurface()->getGpuAddress())));
}
TEST_F(MockProgramDataTest, whenGlobalVariablesAreExportedThenAllocateSurfacesAsSvm) {
if (this->pContext->getSVMAllocsManager() == nullptr) {
return;
}
setupGlobalAllocation();
std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
mockLinkerInput->traits.exportsGlobalVariables = true;
static_cast<MockProgram *>(pProgram)->linkerInput = std::move(mockLinkerInput);
buildAndDecodeProgramPatchList();
ASSERT_NE(nullptr, pProgram->getGlobalSurface());
EXPECT_NE(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getGlobalSurface()->getGpuAddress())));
}
TEST_F(MockProgramDataTest, whenGlobalVariablesAreExportedButContextUnavailableThenAllocateSurfacesAsNonSvm) {
if (this->pContext->getSVMAllocsManager() == nullptr) {
return;
}
setupGlobalAllocation();
std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
mockLinkerInput->traits.exportsGlobalVariables = true;
static_cast<MockProgram *>(pProgram)->linkerInput = std::move(mockLinkerInput);
static_cast<MockProgram *>(pProgram)->context = nullptr;
buildAndDecodeProgramPatchList();
static_cast<MockProgram *>(pProgram)->context = pContext;
ASSERT_NE(nullptr, pProgram->getGlobalSurface());
EXPECT_EQ(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getGlobalSurface()->getGpuAddress())));
}
TEST_F(MockProgramDataTest, whenGlobalVariablesAreNotExportedThenAllocateSurfacesAsNonSvm) {
if (this->pContext->getSVMAllocsManager() == nullptr) {
return;
}
setupGlobalAllocation();
std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
mockLinkerInput->traits.exportsGlobalVariables = false;
static_cast<MockProgram *>(pProgram)->linkerInput = std::move(mockLinkerInput);
static_cast<MockProgram *>(pProgram)->context = nullptr;
buildAndDecodeProgramPatchList();
static_cast<MockProgram *>(pProgram)->context = pContext;
ASSERT_NE(nullptr, pProgram->getGlobalSurface());
EXPECT_EQ(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getGlobalSurface()->getGpuAddress())));
}
TEST_F(ProgramDataTest, givenConstantAllocationThatIsInUseByGpuWhenProgramIsBeingDestroyedThenItIsAddedToTemporaryAllocationList) { TEST_F(ProgramDataTest, givenConstantAllocationThatIsInUseByGpuWhenProgramIsBeingDestroyedThenItIsAddedToTemporaryAllocationList) {
setupConstantAllocation(); setupConstantAllocation();
@ -676,3 +789,122 @@ TEST_F(ProgramDataTest, GivenProgramWith32bitPointerOptWhenProgramScopeGlobalPoi
globalSurface.mockGfxAllocation.set32BitAllocation(false); globalSurface.mockGfxAllocation.set32BitAllocation(false);
prog->setGlobalSurface(nullptr); prog->setGlobalSurface(nullptr);
} }
TEST_F(ProgramDataTest, givenSymbolTablePatchTokenThenLinkerInputIsCreated) {
SPatchFunctionTableInfo token;
token.Token = PATCH_TOKEN_PROGRAM_SYMBOL_TABLE;
token.Size = static_cast<uint32_t>(sizeof(SPatchFunctionTableInfo));
token.NumEntries = 0;
pProgramPatchList = &token;
programPatchListSize = token.Size;
buildAndDecodeProgramPatchList();
EXPECT_NE(nullptr, pProgram->getLinkerInput());
}
TEST(ProgramLinkBinaryTest, whenLinkerInputEmptyThenLinkSuccessful) {
auto linkerInput = std::make_unique<WhiteBox<LinkerInput>>();
NEO::ExecutionEnvironment env;
MockProgram program{env};
program.linkerInput = std::move(linkerInput);
auto ret = program.linkBinary();
EXPECT_EQ(CL_SUCCESS, ret);
}
TEST(ProgramLinkBinaryTest, whenLinkerUnresolvedExternalThenLinkFailedAndBuildLogAvailable) {
auto linkerInput = std::make_unique<WhiteBox<LinkerInput>>();
NEO::LinkerInput::RelocationInfo relocation = {};
relocation.symbolName = "A";
relocation.offset = 0;
linkerInput->relocations.push_back(NEO::LinkerInput::Relocations{relocation});
linkerInput->traits.requiresPatchingOfInstructionSegments = true;
NEO::ExecutionEnvironment env;
MockProgram program{env};
KernelInfo kernelInfo = {};
kernelInfo.name = "onlyKernel";
std::vector<char> kernelHeap;
kernelHeap.resize(32, 7);
kernelInfo.heapInfo.pKernelHeap = kernelHeap.data();
iOpenCL::SKernelBinaryHeaderCommon kernelHeader = {};
kernelHeader.KernelHeapSize = static_cast<uint32_t>(kernelHeap.size());
kernelInfo.heapInfo.pKernelHeader = &kernelHeader;
program.getKernelInfoArray().push_back(&kernelInfo);
program.linkerInput = std::move(linkerInput);
EXPECT_EQ(nullptr, program.getBuildLog(nullptr));
auto ret = program.linkBinary();
EXPECT_NE(CL_SUCCESS, ret);
program.getKernelInfoArray().clear();
auto buildLog = program.getBuildLog(nullptr);
ASSERT_NE(nullptr, buildLog);
Linker::UnresolvedExternals expectedUnresolvedExternals;
expectedUnresolvedExternals.push_back(Linker::UnresolvedExternal{relocation, 0, false});
auto expectedError = constructLinkerErrorMessage(expectedUnresolvedExternals, std::vector<std::string>{"kernel : " + kernelInfo.name});
EXPECT_THAT(buildLog, ::testing::HasSubstr(expectedError));
}
TEST(ProgramLinkBinaryTest, whenPrepareLinkerInputStorageGetsCalledTwiceThenLinkerInputStorageIsReused) {
ExecutionEnvironment execEnv;
MockProgram program{execEnv};
EXPECT_EQ(nullptr, program.linkerInput);
program.prepareLinkerInputStorage();
EXPECT_NE(nullptr, program.linkerInput);
auto prevLinkerInput = program.getLinkerInput();
program.prepareLinkerInputStorage();
EXPECT_EQ(prevLinkerInput, program.linkerInput.get());
}
TEST_F(ProgramDataTest, whenLinkerInputValidThenIsaIsProperlyPatched) {
auto linkerInput = std::make_unique<WhiteBox<LinkerInput>>();
linkerInput->symbols["A"] = NEO::SymbolInfo{4U, 4U, NEO::SymbolInfo::GlobalVariable};
linkerInput->symbols["B"] = NEO::SymbolInfo{8U, 4U, NEO::SymbolInfo::GlobalConstant};
linkerInput->symbols["C"] = NEO::SymbolInfo{16U, 4U, NEO::SymbolInfo::Function};
linkerInput->relocations.push_back({NEO::LinkerInput::RelocationInfo{"A", 8U}, NEO::LinkerInput::RelocationInfo{"B", 16U}, NEO::LinkerInput::RelocationInfo{"C", 24U}});
linkerInput->traits.requiresPatchingOfInstructionSegments = true;
linkerInput->exportedFunctionsSegmentId = 0;
NEO::ExecutionEnvironment env;
MockProgram program{env};
KernelInfo kernelInfo = {};
kernelInfo.name = "onlyKernel";
std::vector<char> kernelHeap;
kernelHeap.resize(32, 7);
kernelInfo.heapInfo.pKernelHeap = kernelHeap.data();
iOpenCL::SKernelBinaryHeaderCommon kernelHeader = {};
kernelHeader.KernelHeapSize = static_cast<uint32_t>(kernelHeap.size());
kernelInfo.heapInfo.pKernelHeader = &kernelHeader;
MockGraphicsAllocation kernelIsa(kernelHeap.data(), kernelHeap.size());
kernelInfo.kernelAllocation = &kernelIsa;
program.getKernelInfoArray().push_back(&kernelInfo);
program.linkerInput = std::move(linkerInput);
program.exportedFunctionsSurface = kernelInfo.kernelAllocation;
std::vector<char> globalVariablesBuffer;
globalVariablesBuffer.resize(32, 7);
std::vector<char> globalConstantsBuffer;
globalConstantsBuffer.resize(32, 7);
program.globalSurface = new MockGraphicsAllocation(globalVariablesBuffer.data(), globalVariablesBuffer.size());
program.constantSurface = new MockGraphicsAllocation(globalConstantsBuffer.data(), globalConstantsBuffer.size());
program.pDevice = this->pContext->getDevice(0);
auto ret = program.linkBinary();
EXPECT_EQ(CL_SUCCESS, ret);
linkerInput.reset(static_cast<WhiteBox<LinkerInput> *>(program.linkerInput.release()));
for (size_t i = 0; i < linkerInput->relocations.size(); ++i) {
auto expectedPatch = program.globalSurface->getGpuAddress() + linkerInput->symbols[linkerInput->relocations[0][0].symbolName].offset;
auto relocationAddress = kernelHeap.data() + linkerInput->relocations[0][0].offset;
EXPECT_EQ(static_cast<uintptr_t>(expectedPatch), *reinterpret_cast<uintptr_t *>(relocationAddress)) << i;
}
program.getKernelInfoArray().clear();
delete program.globalSurface;
program.globalSurface = nullptr;
delete program.constantSurface;
program.constantSurface = nullptr;
}

View File

@ -2923,6 +2923,12 @@ TEST_F(ProgramTests, givenProgramWhenUnknownInternalOptionsArePassedThenTheyAreN
EXPECT_TRUE(buildOptions == internalOption); EXPECT_TRUE(buildOptions == internalOption);
} }
TEST_F(ProgramTests, givenProgramWhenGetSymbolsIsCalledThenMapWithExportedSymbolsIsReturned) {
ExecutionEnvironment executionEnvironment;
MockProgram program(executionEnvironment);
EXPECT_EQ(&program.symbols, &program.getSymbols());
}
class AdditionalOptionsMockProgram : public MockProgram { class AdditionalOptionsMockProgram : public MockProgram {
public: public:
AdditionalOptionsMockProgram() : MockProgram(executionEnvironment) {} AdditionalOptionsMockProgram() : MockProgram(executionEnvironment) {}
@ -3011,6 +3017,19 @@ TEST(RebuildProgramFromIrTests, givenBinaryProgramWhenKernelRebulildIsNotForcedT
EXPECT_FALSE(pProgram->rebuildProgramFromIrCalled); EXPECT_FALSE(pProgram->rebuildProgramFromIrCalled);
} }
TEST(Program, whenGetKernelNamesStringIsCalledThenNamesAreProperlyConcatenated) {
ExecutionEnvironment execEnv;
MockProgram program{execEnv};
KernelInfo kernel1 = {};
kernel1.name = "kern1";
KernelInfo kernel2 = {};
kernel2.name = "kern2";
program.getKernelInfoArray().push_back(&kernel1);
program.getKernelInfoArray().push_back(&kernel2);
EXPECT_EQ("kern1;kern2", program.getKernelNamesString());
program.getKernelInfoArray().clear();
}
struct SpecializationConstantProgramMock : public MockProgram { struct SpecializationConstantProgramMock : public MockProgram {
using MockProgram::MockProgram; using MockProgram::MockProgram;
cl_int updateSpecializationConstant(cl_uint specId, size_t specSize, const void *specValue) override { cl_int updateSpecializationConstant(cl_uint specId, size_t specSize, const void *specValue) override {