mirror of
https://github.com/intel/compute-runtime.git
synced 2025-09-15 13:01:45 +08:00
Support for symbol/relocation tables
Change-Id: I87890f6dc36a3454ffdcab1fb9d070fdaf91e689
This commit is contained in:

committed by
sys_ocldev

parent
b6792ef049
commit
ce061a48ef
13
core/compiler_interface/CMakeLists.txt
Normal file
13
core/compiler_interface/CMakeLists.txt
Normal 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})
|
213
core/compiler_interface/linker.cpp
Normal file
213
core/compiler_interface/linker.cpp
Normal 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
|
134
core/compiler_interface/linker.h
Normal file
134
core/compiler_interface/linker.h
Normal 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
|
13
core/unit_tests/compiler_interface/CMakeLists.txt
Normal file
13
core/unit_tests/compiler_interface/CMakeLists.txt
Normal 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})
|
23
core/unit_tests/compiler_interface/linker_mock.h
Normal file
23
core/unit_tests/compiler_interface/linker_mock.h
Normal 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;
|
||||||
|
};
|
522
core/unit_tests/compiler_interface/linker_tests.cpp
Normal file
522
core/unit_tests/compiler_interface/linker_tests.cpp
Normal 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"));
|
||||||
|
}
|
@ -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}
|
||||||
|
@ -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})
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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})
|
||||||
|
@ -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);
|
||||||
|
@ -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>();
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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 {
|
||||||
|
Reference in New Issue
Block a user