mirror of
https://github.com/intel/compute-runtime.git
synced 2025-12-24 12:23:05 +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}
|
||||
${HW_SRC_INCLUDE_PATH}
|
||||
${IGC_OCL_ADAPTOR_DIR}
|
||||
${VISA_DIR}
|
||||
${IGDRCL__IGC_INCLUDE_DIR}
|
||||
${THIRD_PARTY_DIR}
|
||||
${UMKM_SHAREDDATA_INCLUDE_PATHS}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2018 Intel Corporation
|
||||
# Copyright (C) 2018-2019 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
@@ -16,5 +16,8 @@ set(RUNTIME_SRCS_COMPILER_INTERFACE
|
||||
${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})
|
||||
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) {
|
||||
|
||||
auto numArgs = kernelInfo.kernelArgInfo.size();
|
||||
for (decltype(numArgs) argIndex = 0; argIndex < numArgs; argIndex++) {
|
||||
if (kernelArguments[argIndex].object) {
|
||||
@@ -1005,6 +1004,10 @@ void Kernel::makeResident(CommandStreamReceiver &commandStreamReceiver) {
|
||||
commandStreamReceiver.makeResident(*(program->getGlobalSurface()));
|
||||
}
|
||||
|
||||
if (program->getExportedFunctionsSurface()) {
|
||||
commandStreamReceiver.makeResident(*(program->getExportedFunctionsSurface()));
|
||||
}
|
||||
|
||||
for (auto gfxAlloc : kernelSvmGfxAllocations) {
|
||||
commandStreamReceiver.makeResident(*gfxAlloc);
|
||||
}
|
||||
@@ -1045,6 +1048,11 @@ void Kernel::getResidency(std::vector<Surface *> &dst) {
|
||||
dst.push_back(surface);
|
||||
}
|
||||
|
||||
if (program->getExportedFunctionsSurface()) {
|
||||
GeneralSurface *surface = new GeneralSurface(program->getExportedFunctionsSurface());
|
||||
dst.push_back(surface);
|
||||
}
|
||||
|
||||
for (auto gfxAlloc : kernelSvmGfxAllocations) {
|
||||
GeneralSurface *surface = new GeneralSurface(gfxAlloc);
|
||||
dst.push_back(surface);
|
||||
|
||||
@@ -179,7 +179,7 @@ cl_int Program::build(
|
||||
const char *pKernelData,
|
||||
size_t kernelDataSize) {
|
||||
cl_int retVal = CL_SUCCESS;
|
||||
processKernel(pKernelData, retVal);
|
||||
processKernel(pKernelData, 0U, retVal);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
*/
|
||||
|
||||
#include "core/helpers/ptr_math.h"
|
||||
#include "runtime/context/context.h"
|
||||
#include "runtime/gtpin/gtpin_notify.h"
|
||||
#include "runtime/helpers/aligned_memory.h"
|
||||
#include "runtime/helpers/debug_helpers.h"
|
||||
#include "runtime/helpers/hash.h"
|
||||
#include "runtime/helpers/string.h"
|
||||
#include "runtime/memory_manager/memory_manager.h"
|
||||
#include "runtime/memory_manager/svm_memory_manager.h"
|
||||
|
||||
#include "patch_list.h"
|
||||
#include "patch_shared.h"
|
||||
@@ -61,6 +63,7 @@ std::string Program::getKernelNamesString() const {
|
||||
|
||||
size_t Program::processKernel(
|
||||
const void *pKernelBlob,
|
||||
uint32_t kernelNum,
|
||||
cl_int &retVal) {
|
||||
size_t sizeProcessed = 0;
|
||||
|
||||
@@ -95,7 +98,7 @@ size_t Program::processKernel(
|
||||
|
||||
pKernelInfo->heapInfo.pPatchList = pCurKernelPtr;
|
||||
|
||||
retVal = parsePatchList(*pKernelInfo);
|
||||
retVal = parsePatchList(*pKernelInfo, kernelNum);
|
||||
if (retVal != CL_SUCCESS) {
|
||||
delete pKernelInfo;
|
||||
sizeProcessed = ptrDiff(pCurKernelPtr, pKernelBlob);
|
||||
@@ -139,7 +142,7 @@ size_t Program::processKernel(
|
||||
return sizeProcessed;
|
||||
}
|
||||
|
||||
cl_int Program::parsePatchList(KernelInfo &kernelInfo) {
|
||||
cl_int Program::parsePatchList(KernelInfo &kernelInfo, uint32_t kernelNum) {
|
||||
cl_int retVal = CL_SUCCESS;
|
||||
|
||||
auto pPatchList = kernelInfo.heapInfo.pPatchList;
|
||||
@@ -804,6 +807,19 @@ cl_int Program::parsePatchList(KernelInfo &kernelInfo) {
|
||||
"\n .Size", pPatch->Size);
|
||||
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:
|
||||
printDebugString(DebugManager.flags.PrintDebugMessages.get(), stderr, " Program::parsePatchList. Unknown Patch Token: %d\n", pPatch->Token);
|
||||
if (false == isSafeToSkipUnhandledToken(pPatch->Token)) {
|
||||
@@ -853,15 +869,44 @@ cl_int Program::parsePatchList(KernelInfo &kernelInfo) {
|
||||
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 retVal = CL_SUCCESS;
|
||||
cl_uint surfaceSize = 0;
|
||||
size_t globalVariablesSurfaceSize = 0U, globalConstantsSurfaceSize = 0U;
|
||||
const void *globalVariablesInitData = nullptr, *globalConstantsInitData = nullptr;
|
||||
|
||||
auto pPatchList = programScopePatchList;
|
||||
auto patchListSize = programScopePatchListSize;
|
||||
auto pCurPatchListPtr = pPatchList;
|
||||
cl_uint headerSize = 0;
|
||||
|
||||
std::vector<uint64_t> globalVariablesSelfPatches;
|
||||
std::vector<uint64_t> globalConstantsSelfPatches;
|
||||
|
||||
while (ptrDiff(pCurPatchListPtr, pPatchList) < patchListSize) {
|
||||
auto pPatch = reinterpret_cast<const SPatchItemHeader *>(pCurPatchListPtr);
|
||||
switch (pPatch->Token) {
|
||||
@@ -872,12 +917,11 @@ cl_int Program::parseProgramScopePatchList() {
|
||||
pDevice->getMemoryManager()->freeGraphicsMemory(constantSurface);
|
||||
}
|
||||
|
||||
surfaceSize = patch.InlineDataSize;
|
||||
globalConstantsSurfaceSize = patch.InlineDataSize;
|
||||
headerSize = sizeof(SPatchAllocateConstantMemorySurfaceProgramBinaryInfo);
|
||||
constantSurface = pDevice->getMemoryManager()->allocateGraphicsMemoryWithProperties({surfaceSize, GraphicsAllocation::AllocationType::CONSTANT_SURFACE});
|
||||
|
||||
memcpy_s(constantSurface->getUnderlyingBuffer(), surfaceSize, (cl_char *)pPatch + headerSize, surfaceSize);
|
||||
pCurPatchListPtr = ptrOffset(pCurPatchListPtr, surfaceSize);
|
||||
globalConstantsInitData = (cl_char *)pPatch + headerSize;
|
||||
pCurPatchListPtr = ptrOffset(pCurPatchListPtr, globalConstantsSurfaceSize);
|
||||
DBG_LOG(LogPatchTokens,
|
||||
"\n .ALLOCATE_CONSTANT_MEMORY_SURFACE_PROGRAM_BINARY_INFO", pPatch->Token,
|
||||
"\n .Size", pPatch->Size,
|
||||
@@ -893,13 +937,12 @@ cl_int Program::parseProgramScopePatchList() {
|
||||
pDevice->getMemoryManager()->freeGraphicsMemory(globalSurface);
|
||||
}
|
||||
|
||||
surfaceSize = patch.InlineDataSize;
|
||||
globalVarTotalSize += (size_t)surfaceSize;
|
||||
globalVariablesSurfaceSize = patch.InlineDataSize;
|
||||
globalVarTotalSize += (size_t)globalVariablesSurfaceSize;
|
||||
headerSize = sizeof(SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo);
|
||||
globalSurface = pDevice->getMemoryManager()->allocateGraphicsMemoryWithProperties({surfaceSize, GraphicsAllocation::AllocationType::GLOBAL_SURFACE});
|
||||
|
||||
memcpy_s(globalSurface->getUnderlyingBuffer(), surfaceSize, (cl_char *)pPatch + headerSize, surfaceSize);
|
||||
pCurPatchListPtr = ptrOffset(pCurPatchListPtr, surfaceSize);
|
||||
globalVariablesInitData = (cl_char *)pPatch + headerSize;
|
||||
pCurPatchListPtr = ptrOffset(pCurPatchListPtr, globalVariablesSurfaceSize);
|
||||
DBG_LOG(LogPatchTokens,
|
||||
"\n .ALLOCATE_GLOBAL_MEMORY_SURFACE_PROGRAM_BINARY_INFO", pPatch->Token,
|
||||
"\n .Size", pPatch->Size,
|
||||
@@ -913,12 +956,7 @@ cl_int Program::parseProgramScopePatchList() {
|
||||
if (globalSurface != nullptr) {
|
||||
auto patch = *(SPatchGlobalPointerProgramBinaryInfo *)pPatch;
|
||||
if ((patch.GlobalBufferIndex == 0) && (patch.BufferIndex == 0) && (patch.BufferType == PROGRAM_SCOPE_GLOBAL_BUFFER)) {
|
||||
void *pPtr = (void *)((uintptr_t)globalSurface->getUnderlyingBuffer() + (uintptr_t)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());
|
||||
}
|
||||
globalVariablesSelfPatches.push_back(readMisalignedUint64(&patch.GlobalPointerOffset));
|
||||
} else {
|
||||
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) {
|
||||
auto patch = *(SPatchConstantPointerProgramBinaryInfo *)pPatch;
|
||||
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);
|
||||
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());
|
||||
}
|
||||
|
||||
globalConstantsSelfPatches.push_back(readMisalignedUint64(&patch.ConstantPointerOffset));
|
||||
} else {
|
||||
printDebugString(DebugManager.flags.PrintDebugMessages.get(), stderr, "Program::parseProgramScopePatchList. Unhandled Data parameter: %d\n", pPatch->Token);
|
||||
}
|
||||
@@ -956,6 +988,12 @@ cl_int Program::parseProgramScopePatchList() {
|
||||
}
|
||||
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:
|
||||
if (false == isSafeToSkipUnhandledToken(pPatch->Token)) {
|
||||
retVal = CL_INVALID_BINARY;
|
||||
@@ -972,9 +1010,97 @@ cl_int Program::parseProgramScopePatchList() {
|
||||
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;
|
||||
}
|
||||
|
||||
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 retVal = CL_SUCCESS;
|
||||
|
||||
@@ -997,20 +1123,24 @@ cl_int Program::processGenBinary() {
|
||||
programScopePatchList = pCurBinaryPtr;
|
||||
programScopePatchListSize = pGenBinaryHeader->PatchListSize;
|
||||
|
||||
if (programScopePatchListSize != 0u) {
|
||||
retVal = parseProgramScopePatchList();
|
||||
}
|
||||
|
||||
pCurBinaryPtr = ptrOffset(pCurBinaryPtr, pGenBinaryHeader->PatchListSize);
|
||||
|
||||
auto numKernels = pGenBinaryHeader->NumberOfKernels;
|
||||
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);
|
||||
}
|
||||
|
||||
if (programScopePatchListSize != 0u) {
|
||||
retVal = parseProgramScopePatchList();
|
||||
}
|
||||
} while (false);
|
||||
|
||||
if (retVal == CL_SUCCESS) {
|
||||
retVal = linkBinary();
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "runtime/helpers/hw_helper.h"
|
||||
#include "runtime/helpers/string.h"
|
||||
#include "runtime/memory_manager/memory_manager.h"
|
||||
#include "runtime/memory_manager/svm_memory_manager.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -108,14 +109,23 @@ Program::~Program() {
|
||||
delete blockKernelManager;
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
if (context && !isBuiltIn) {
|
||||
context->decRefInternal();
|
||||
}
|
||||
@@ -493,4 +503,10 @@ void Program::updateNonUniformFlag(const Program **inputPrograms, size_t numInpu
|
||||
}
|
||||
this->allowNonUniform = allowNonUniform;
|
||||
}
|
||||
|
||||
void Program::prepareLinkerInputStorage() {
|
||||
if (this->linkerInput == nullptr) {
|
||||
this->linkerInput = std::make_unique<LinkerInput>();
|
||||
}
|
||||
}
|
||||
} // namespace NEO
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "core/compiler_interface/linker.h"
|
||||
#include "elf/reader.h"
|
||||
#include "elf/writer.h"
|
||||
#include "runtime/api/cl_types.h"
|
||||
@@ -148,6 +149,7 @@ class Program : public BaseObject<_cl_program> {
|
||||
}
|
||||
|
||||
const Device &getDevice(cl_uint deviceOrdinal) const {
|
||||
UNRECOVERABLE_IF(pDevice == nullptr);
|
||||
return *pDevice;
|
||||
}
|
||||
|
||||
@@ -206,6 +208,10 @@ class Program : public BaseObject<_cl_program> {
|
||||
return globalSurface;
|
||||
}
|
||||
|
||||
GraphicsAllocation *getExportedFunctionsSurface() const {
|
||||
return exportedFunctionsSurface;
|
||||
}
|
||||
|
||||
BlockKernelManager *getBlockKernelManager() const {
|
||||
return blockKernelManager;
|
||||
}
|
||||
@@ -258,6 +264,14 @@ class Program : public BaseObject<_cl_program> {
|
||||
return this->specConstantsSizes;
|
||||
}
|
||||
|
||||
const Linker::RelocatedSymbolsMap &getSymbols() const {
|
||||
return this->symbols;
|
||||
}
|
||||
|
||||
LinkerInput *getLinkerInput() const {
|
||||
return this->linkerInput.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
Program(ExecutionEnvironment &executionEnvironment);
|
||||
|
||||
@@ -274,13 +288,15 @@ class Program : public BaseObject<_cl_program> {
|
||||
|
||||
cl_int resolveProgramBinary();
|
||||
|
||||
MOCKABLE_VIRTUAL cl_int linkBinary();
|
||||
|
||||
cl_int parseProgramScopePatchList();
|
||||
|
||||
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);
|
||||
|
||||
@@ -300,6 +316,8 @@ class Program : public BaseObject<_cl_program> {
|
||||
MOCKABLE_VIRTUAL bool appendKernelDebugOptions();
|
||||
void notifyDebuggerWithSourceCode(std::string &filename);
|
||||
|
||||
void prepareLinkerInputStorage();
|
||||
|
||||
static const std::string clOptNameClVer;
|
||||
static const std::string clOptNameUniformWgs;
|
||||
|
||||
@@ -329,6 +347,7 @@ class Program : public BaseObject<_cl_program> {
|
||||
|
||||
GraphicsAllocation *constantSurface;
|
||||
GraphicsAllocation *globalSurface;
|
||||
GraphicsAllocation *exportedFunctionsSurface = nullptr;
|
||||
|
||||
size_t globalVarTotalSize;
|
||||
|
||||
@@ -346,6 +365,9 @@ class Program : public BaseObject<_cl_program> {
|
||||
uint32_t programOptionVersion;
|
||||
bool allowNonUniform;
|
||||
|
||||
std::unique_ptr<LinkerInput> linkerInput;
|
||||
Linker::RelocatedSymbolsMap symbols;
|
||||
|
||||
std::map<const Device *, std::string> buildLog;
|
||||
|
||||
bool areSpecializationConstantsInitialized = false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2017-2018 Intel Corporation
|
||||
# Copyright (C) 2017-2019 Intel Corporation
|
||||
#
|
||||
# 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}/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})
|
||||
|
||||
@@ -94,6 +94,11 @@ TEST_P(KernelArgInfoTest, Create_Simple) {
|
||||
// included in the setup of fixture
|
||||
}
|
||||
|
||||
TEST_P(KernelArgInfoTest, WhenQueryingWithNullptrKernelNameTheniReturnNullptr) {
|
||||
auto kernelInfo = this->pProgram->getKernelInfo(nullptr);
|
||||
EXPECT_EQ(nullptr, kernelInfo);
|
||||
}
|
||||
|
||||
TEST_P(KernelArgInfoTest, getKernelArgAcessQualifier) {
|
||||
cl_kernel_arg_access_qualifier param_value = 0;
|
||||
queryArgInfo<cl_kernel_arg_access_qualifier>(CL_KERNEL_ARG_ACCESS_QUALIFIER, param_value);
|
||||
|
||||
@@ -436,6 +436,8 @@ class CommandStreamReceiverMock : public CommandStreamReceiver {
|
||||
typedef CommandStreamReceiver BaseClass;
|
||||
|
||||
public:
|
||||
using CommandStreamReceiver::executionEnvironment;
|
||||
|
||||
using BaseClass::CommandStreamReceiver;
|
||||
CommandStreamReceiverMock() : BaseClass(*(new ExecutionEnvironment)) {
|
||||
this->mockExecutionEnvironment.reset(&this->executionEnvironment);
|
||||
@@ -443,13 +445,17 @@ class CommandStreamReceiverMock : public CommandStreamReceiver {
|
||||
|
||||
void makeResident(GraphicsAllocation &graphicsAllocation) override {
|
||||
residency[graphicsAllocation.getUnderlyingBuffer()] = graphicsAllocation.getUnderlyingBufferSize();
|
||||
if (passResidencyCallToBaseClass) {
|
||||
CommandStreamReceiver::makeResident(graphicsAllocation);
|
||||
}
|
||||
}
|
||||
|
||||
void makeNonResident(GraphicsAllocation &graphicsAllocation) override {
|
||||
residency.erase(graphicsAllocation.getUnderlyingBuffer());
|
||||
if (passResidencyCallToBaseClass) {
|
||||
CommandStreamReceiver::makeNonResident(graphicsAllocation);
|
||||
}
|
||||
}
|
||||
|
||||
FlushStamp flush(BatchBuffer &batchBuffer, ResidencyContainer &allocationsForResidency) override {
|
||||
return flushStamp->peekStamp();
|
||||
@@ -479,6 +485,7 @@ class CommandStreamReceiverMock : public CommandStreamReceiver {
|
||||
}
|
||||
|
||||
std::map<const void *, size_t> residency;
|
||||
bool passResidencyCallToBaseClass = true;
|
||||
std::unique_ptr<ExecutionEnvironment> mockExecutionEnvironment;
|
||||
};
|
||||
|
||||
@@ -1616,6 +1623,76 @@ HWTEST_F(KernelResidencyTest, givenKernelWhenMakeResidentIsCalledThenKernelIsaIs
|
||||
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) {
|
||||
MockKernelWithInternals mockKernel(*this->pDevice);
|
||||
auto &commandStreamReceiver = this->pDevice->getUltCommandStreamReceiver<FamilyType>();
|
||||
|
||||
@@ -25,25 +25,35 @@ class GraphicsAllocation;
|
||||
class MockProgram : public Program {
|
||||
public:
|
||||
using Program::createProgramFromBinary;
|
||||
using Program::getKernelNamesString;
|
||||
using Program::getProgramCompilerVersion;
|
||||
using Program::isKernelDebugEnabled;
|
||||
using Program::linkBinary;
|
||||
using Program::prepareLinkerInputStorage;
|
||||
using Program::rebuildProgramFromIr;
|
||||
using Program::resolveProgramBinary;
|
||||
using Program::updateNonUniformFlag;
|
||||
|
||||
using Program::areSpecializationConstantsInitialized;
|
||||
using Program::constantSurface;
|
||||
using Program::context;
|
||||
using Program::elfBinary;
|
||||
using Program::elfBinarySize;
|
||||
using Program::exportedFunctionsSurface;
|
||||
using Program::genBinary;
|
||||
using Program::genBinarySize;
|
||||
using Program::globalSurface;
|
||||
using Program::irBinary;
|
||||
using Program::irBinarySize;
|
||||
using Program::isProgramBinaryResolved;
|
||||
using Program::isSpirV;
|
||||
using Program::linkerInput;
|
||||
using Program::pDevice;
|
||||
using Program::programBinaryType;
|
||||
using Program::specConstantsIds;
|
||||
using Program::specConstantsSizes;
|
||||
using Program::specConstantsValues;
|
||||
using Program::symbols;
|
||||
|
||||
using Program::sourceCode;
|
||||
|
||||
|
||||
@@ -108,18 +108,24 @@ TEST_F(KernelDataTest, PrintfString) {
|
||||
iOpenCL::SPatchString printfString;
|
||||
printfString.Token = PATCH_TOKEN_STRING;
|
||||
printfString.Size = static_cast<uint32_t>(sizeof(SPatchString) + strSize);
|
||||
|
||||
printfString.Index = 0;
|
||||
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((cl_char *)pPrintfString + sizeof(printfString), strSize, stringValue, strSize);
|
||||
|
||||
memcpy_s((cl_char *)pPrintfString + printfString.Size, emptyString.Size, &emptyString, emptyString.Size);
|
||||
|
||||
pPatchList = (void *)pPrintfString;
|
||||
patchListSize = printfString.Size;
|
||||
patchListSize = printfString.Size + emptyString.Size;
|
||||
|
||||
buildAndDecode();
|
||||
|
||||
@@ -1387,3 +1393,31 @@ TEST_F(KernelDataTest, PATCH_TOKEN_ALLOCATE_SIP_SURFACE) {
|
||||
EXPECT_EQ(token.Token, pKernelInfo->patchInfo.pAllocateSystemThreadSurface->Token);
|
||||
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/memory_manager/allocations_list.h"
|
||||
#include "runtime/memory_manager/graphics_allocation.h"
|
||||
#include "runtime/memory_manager/svm_memory_manager.h"
|
||||
#include "runtime/platform/platform.h"
|
||||
#include "runtime/program/program.h"
|
||||
#include "test.h"
|
||||
@@ -22,7 +24,8 @@ using namespace iOpenCL;
|
||||
static const char constValue[] = "11223344";
|
||||
static const char globalValue[] = "55667788";
|
||||
|
||||
class ProgramDataTest : public testing::Test,
|
||||
template <typename ProgramType>
|
||||
class ProgramDataTestBase : public testing::Test,
|
||||
public ContextFixture,
|
||||
public PlatformFixture,
|
||||
public ProgramFixture {
|
||||
@@ -31,8 +34,7 @@ class ProgramDataTest : public testing::Test,
|
||||
using PlatformFixture::SetUp;
|
||||
|
||||
public:
|
||||
ProgramDataTest() {
|
||||
|
||||
ProgramDataTestBase() {
|
||||
memset(&programBinaryHeader, 0x00, sizeof(SProgramBinaryHeader));
|
||||
pCurPtr = nullptr;
|
||||
pProgramPatchList = nullptr;
|
||||
@@ -48,7 +50,7 @@ class ProgramDataTest : public testing::Test,
|
||||
ContextFixture::SetUp(1, &device);
|
||||
ProgramFixture::SetUp();
|
||||
|
||||
CreateProgramWithSource(
|
||||
CreateProgramWithSource<ProgramType>(
|
||||
pContext,
|
||||
&device,
|
||||
"CopyBuffer_simd8.cl");
|
||||
@@ -120,7 +122,8 @@ class ProgramDataTest : public testing::Test,
|
||||
uint32_t programPatchListSize;
|
||||
};
|
||||
|
||||
void ProgramDataTest::buildAndDecodeProgramPatchList() {
|
||||
template <typename ProgramType>
|
||||
void ProgramDataTestBase<ProgramType>::buildAndDecodeProgramPatchList() {
|
||||
size_t headerSize = sizeof(SProgramBinaryHeader);
|
||||
|
||||
cl_int error = CL_SUCCESS;
|
||||
@@ -154,6 +157,9 @@ void ProgramDataTest::buildAndDecodeProgramPatchList() {
|
||||
delete[] pProgramData;
|
||||
}
|
||||
|
||||
using ProgramDataTest = ProgramDataTestBase<NEO::Program>;
|
||||
using MockProgramDataTest = ProgramDataTestBase<MockProgram>;
|
||||
|
||||
TEST_F(ProgramDataTest, EmptyProgramBinaryHeader) {
|
||||
buildAndDecodeProgramPatchList();
|
||||
}
|
||||
@@ -168,6 +174,113 @@ TEST_F(ProgramDataTest, AllocateConstantMemorySurfaceProgramBinaryInfo) {
|
||||
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) {
|
||||
|
||||
setupConstantAllocation();
|
||||
@@ -676,3 +789,122 @@ TEST_F(ProgramDataTest, GivenProgramWith32bitPointerOptWhenProgramScopeGlobalPoi
|
||||
globalSurface.mockGfxAllocation.set32BitAllocation(false);
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(ProgramTests, givenProgramWhenGetSymbolsIsCalledThenMapWithExportedSymbolsIsReturned) {
|
||||
ExecutionEnvironment executionEnvironment;
|
||||
MockProgram program(executionEnvironment);
|
||||
EXPECT_EQ(&program.symbols, &program.getSymbols());
|
||||
}
|
||||
|
||||
class AdditionalOptionsMockProgram : public MockProgram {
|
||||
public:
|
||||
AdditionalOptionsMockProgram() : MockProgram(executionEnvironment) {}
|
||||
@@ -3011,6 +3017,19 @@ TEST(RebuildProgramFromIrTests, givenBinaryProgramWhenKernelRebulildIsNotForcedT
|
||||
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 {
|
||||
using MockProgram::MockProgram;
|
||||
cl_int updateSpecializationConstant(cl_uint specId, size_t specSize, const void *specValue) override {
|
||||
|
||||
Reference in New Issue
Block a user