Support Circular and chained deps in L0 Dynamic Link
- Added support for Dynamic Module link to allow usage of symbols that exist thru a circular dependency from the export->import module & chained dependencies with importModule -> ExportModule -> ExportModule2. - Adds a dependency walker which collects all dependent modules whose export surface state must be made resident for a given kernel execution to successfully access each function resolved on different modules. - Expanded the Dynamic Link Black Box test to allow for testing either simple linking and circular + chained linkage. Signed-off-by: Spruit, Neil R <neil.r.spruit@intel.com>
This commit is contained in:
parent
0e9e0ff5e9
commit
c8e60ce3ba
|
@ -35,6 +35,7 @@
|
||||||
#include "compiler_options.h"
|
#include "compiler_options.h"
|
||||||
#include "program_debug_data.h"
|
#include "program_debug_data.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
@ -928,9 +929,23 @@ ze_result_t ModuleImp::getProperties(ze_module_properties_t *pModuleProperties)
|
||||||
return ZE_RESULT_SUCCESS;
|
return ZE_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModuleImp::moduleDependencyWalker(std::map<void *, std::map<void *, void *>> inDeps, void *moduleHandle, std::list<ModuleImp *> *outDeps) {
|
||||||
|
std::map<void *, std::map<void *, void *>>::iterator it;
|
||||||
|
it = inDeps.find(moduleHandle);
|
||||||
|
if (it != inDeps.end()) {
|
||||||
|
std::map<void *, void *> dependencies = it->second;
|
||||||
|
inDeps.erase(it);
|
||||||
|
for (auto const &dependency : dependencies) {
|
||||||
|
moduleDependencyWalker(inDeps, dependency.first, outDeps);
|
||||||
|
outDeps->push_back(static_cast<ModuleImp *>(dependency.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
|
ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
|
||||||
ze_module_handle_t *phModules,
|
ze_module_handle_t *phModules,
|
||||||
ze_module_build_log_handle_t *phLinkLog) {
|
ze_module_build_log_handle_t *phLinkLog) {
|
||||||
|
std::map<void *, std::map<void *, void *>> dependencies;
|
||||||
ModuleBuildLog *moduleLinkLog = nullptr;
|
ModuleBuildLog *moduleLinkLog = nullptr;
|
||||||
if (phLinkLog) {
|
if (phLinkLog) {
|
||||||
moduleLinkLog = ModuleBuildLog::create();
|
moduleLinkLog = ModuleBuildLog::create();
|
||||||
|
@ -941,6 +956,7 @@ ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
|
||||||
if (moduleId->isFullyLinked) {
|
if (moduleId->isFullyLinked) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
std::map<void *, void *> moduleDeps;
|
||||||
NEO::Linker::PatchableSegments isaSegmentsForPatching;
|
NEO::Linker::PatchableSegments isaSegmentsForPatching;
|
||||||
std::vector<std::vector<char>> patchedIsaTempStorage;
|
std::vector<std::vector<char>> patchedIsaTempStorage;
|
||||||
uint32_t numPatchedSymbols = 0u;
|
uint32_t numPatchedSymbols = 0u;
|
||||||
|
@ -970,6 +986,11 @@ ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
|
||||||
NEO::Linker::patchAddress(relocAddress, symbolIt->second, unresolvedExternal.unresolvedRelocation);
|
NEO::Linker::patchAddress(relocAddress, symbolIt->second, unresolvedExternal.unresolvedRelocation);
|
||||||
numPatchedSymbols++;
|
numPatchedSymbols++;
|
||||||
moduleId->importedSymbolAllocations.insert(moduleHandle->exportedFunctionsSurface);
|
moduleId->importedSymbolAllocations.insert(moduleHandle->exportedFunctionsSurface);
|
||||||
|
std::map<void *, void *>::iterator it;
|
||||||
|
it = moduleDeps.find(moduleHandle);
|
||||||
|
if ((it == moduleDeps.end()) && (nullptr != moduleHandle->exportedFunctionsSurface)) {
|
||||||
|
moduleDeps.insert(std::pair<void *, void *>(moduleHandle, moduleHandle));
|
||||||
|
}
|
||||||
|
|
||||||
if (moduleLinkLog) {
|
if (moduleLinkLog) {
|
||||||
std::stringstream logMessage;
|
std::stringstream logMessage;
|
||||||
|
@ -977,18 +998,6 @@ ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
|
||||||
unresolvedSymbolLogMessages.back().append(logMessage.str());
|
unresolvedSymbolLogMessages.back().append(logMessage.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the exported functions surface state from the export module to the import module if it exists.
|
|
||||||
// Enables import modules to access the exported functions during kernel execution.
|
|
||||||
for (auto &kernImmData : moduleId->kernelImmDatas) {
|
|
||||||
kernImmData->getResidencyContainer().reserve(kernImmData->getResidencyContainer().size() +
|
|
||||||
((moduleHandle->exportedFunctionsSurface != nullptr) ? 1 : 0) + moduleId->importedSymbolAllocations.size());
|
|
||||||
|
|
||||||
if (nullptr != moduleHandle->exportedFunctionsSurface) {
|
|
||||||
kernImmData->getResidencyContainer().push_back(moduleHandle->exportedFunctionsSurface);
|
|
||||||
}
|
|
||||||
kernImmData->getResidencyContainer().insert(kernImmData->getResidencyContainer().end(), moduleId->importedSymbolAllocations.begin(),
|
|
||||||
moduleId->importedSymbolAllocations.end());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1002,10 +1011,39 @@ ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
|
||||||
if (numPatchedSymbols != moduleId->unresolvedExternalsInfo.size()) {
|
if (numPatchedSymbols != moduleId->unresolvedExternalsInfo.size()) {
|
||||||
return ZE_RESULT_ERROR_MODULE_LINK_FAILURE;
|
return ZE_RESULT_ERROR_MODULE_LINK_FAILURE;
|
||||||
}
|
}
|
||||||
|
dependencies.insert(std::pair<void *, std::map<void *, void *>>(moduleId, moduleDeps));
|
||||||
moduleId->copyPatchedSegments(isaSegmentsForPatching);
|
moduleId->copyPatchedSegments(isaSegmentsForPatching);
|
||||||
moduleId->isFullyLinked = true;
|
moduleId->isFullyLinked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto i = 0u; i < numModules; i++) {
|
||||||
|
static std::mutex depWalkMutex;
|
||||||
|
std::lock_guard<std::mutex> autolock(depWalkMutex);
|
||||||
|
|
||||||
|
auto moduleId = static_cast<ModuleImp *>(Module::fromHandle(phModules[i]));
|
||||||
|
std::map<void *, std::map<void *, void *>>::iterator it;
|
||||||
|
std::list<ModuleImp *> dependentModules;
|
||||||
|
|
||||||
|
// Walk the dependencies for each Module and dependent Module to determine
|
||||||
|
// the dependency exportedFunctionsSurfaces that must be resident for a given Module's kernels
|
||||||
|
// to execute on the device using Dynamic Module Linking.
|
||||||
|
it = dependencies.find(moduleId);
|
||||||
|
if (it != dependencies.end()) {
|
||||||
|
moduleDependencyWalker(dependencies, moduleId, &dependentModules);
|
||||||
|
// Apply the exported functions surface state from the export module(s) to the import module if it exists.
|
||||||
|
// Enables import modules to access the exported function(s) during kernel execution.
|
||||||
|
for (auto &kernImmData : moduleId->kernelImmDatas) {
|
||||||
|
for (auto const &dependency : dependentModules) {
|
||||||
|
kernImmData->getResidencyContainer().reserve(kernImmData->getResidencyContainer().size() +
|
||||||
|
1 + moduleId->importedSymbolAllocations.size());
|
||||||
|
kernImmData->getResidencyContainer().push_back(dependency->exportedFunctionsSurface);
|
||||||
|
}
|
||||||
|
kernImmData->getResidencyContainer().insert(kernImmData->getResidencyContainer().end(), moduleId->importedSymbolAllocations.begin(),
|
||||||
|
moduleId->importedSymbolAllocations.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
NEO::ExternalFunctionInfosT externalFunctionInfos;
|
NEO::ExternalFunctionInfosT externalFunctionInfos;
|
||||||
NEO::FunctionDependenciesT extFuncDependencies;
|
NEO::FunctionDependenciesT extFuncDependencies;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "igfxfmid.h"
|
#include "igfxfmid.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -139,6 +140,8 @@ struct ModuleImp : public Module {
|
||||||
return this->translationUnit.get();
|
return this->translationUnit.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void moduleDependencyWalker(std::map<void *, std::map<void *, void *>> inDeps, void *moduleHandle, std::list<ModuleImp *> *outDeps);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void copyPatchedSegments(const NEO::Linker::PatchableSegments &isaSegmentsForPatching);
|
void copyPatchedSegments(const NEO::Linker::PatchableSegments &isaSegmentsForPatching);
|
||||||
void verifyDebugCapabilities();
|
void verifyDebugCapabilities();
|
||||||
|
|
|
@ -68,6 +68,17 @@ inline int getParamValue(int argc, char *argv[], const char *shortName, const ch
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isCircularDepTest(int argc, char *argv[]) {
|
||||||
|
bool enabled = isParamEnabled(argc, argv, "-c", "--circular");
|
||||||
|
if (enabled == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Circular Dependency Test mode detected" << std::endl;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool isVerbose(int argc, char *argv[]) {
|
inline bool isVerbose(int argc, char *argv[]) {
|
||||||
bool enabled = isParamEnabled(argc, argv, "-v", "--verbose");
|
bool enabled = isParamEnabled(argc, argv, "-v", "--verbose");
|
||||||
if (enabled == false) {
|
if (enabled == false) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2021 Intel Corporation
|
* Copyright (C) 2021-2022 Intel Corporation
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
|
@ -40,13 +40,62 @@ int lib_func_sub(int x, int y) {
|
||||||
}
|
}
|
||||||
)===";
|
)===";
|
||||||
|
|
||||||
|
const char *importModuleSrcCircDep = R"===(
|
||||||
|
int lib_func_add(int x, int y);
|
||||||
|
int lib_func_mult(int x, int y);
|
||||||
|
int lib_func_sub(int x, int y);
|
||||||
|
|
||||||
|
kernel void call_library_funcs(__global int* result) {
|
||||||
|
int add_result = lib_func_add(1,2);
|
||||||
|
int mult_result = lib_func_mult(add_result,2);
|
||||||
|
result[0] = lib_func_sub(mult_result, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lib_func_add2(int x) {
|
||||||
|
return x+2;
|
||||||
|
}
|
||||||
|
)===";
|
||||||
|
|
||||||
|
const char *exportModuleSrcCircDep = R"===(
|
||||||
|
int lib_func_add2(int x);
|
||||||
|
int lib_func_add5(int x);
|
||||||
|
|
||||||
|
int lib_func_add(int x, int y) {
|
||||||
|
return lib_func_add5(lib_func_add2(x + y));
|
||||||
|
}
|
||||||
|
|
||||||
|
int lib_func_mult(int x, int y) {
|
||||||
|
return x*y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lib_func_sub(int x, int y) {
|
||||||
|
return x-y;
|
||||||
|
}
|
||||||
|
)===";
|
||||||
|
|
||||||
|
const char *exportModuleSrc2CircDep = R"===(
|
||||||
|
int lib_func_add5(int x) {
|
||||||
|
return x+5;
|
||||||
|
}
|
||||||
|
)===";
|
||||||
|
|
||||||
extern bool verbose;
|
extern bool verbose;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
bool outputValidationSuccessful = true;
|
bool outputValidationSuccessful = true;
|
||||||
verbose = isVerbose(argc, argv);
|
verbose = isVerbose(argc, argv);
|
||||||
|
bool circularDep = isCircularDepTest(argc, argv);
|
||||||
|
int numModules = 2;
|
||||||
|
|
||||||
|
char *exportModuleSrcValue = const_cast<char *>(exportModuleSrc);
|
||||||
|
char *importModuleSrcValue = const_cast<char *>(importModuleSrc);
|
||||||
|
ze_module_handle_t exportModule2 = {};
|
||||||
|
if (circularDep) {
|
||||||
|
exportModuleSrcValue = const_cast<char *>(exportModuleSrcCircDep);
|
||||||
|
importModuleSrcValue = const_cast<char *>(importModuleSrcCircDep);
|
||||||
|
numModules = 3;
|
||||||
|
}
|
||||||
// Setup
|
// Setup
|
||||||
SUCCESS_OR_TERMINATE(zeInit(ZE_INIT_FLAG_GPU_ONLY));
|
SUCCESS_OR_TERMINATE(zeInit(ZE_INIT_FLAG_GPU_ONLY));
|
||||||
|
|
||||||
|
@ -93,7 +142,7 @@ int main(int argc, char *argv[]) {
|
||||||
std::cout << "reading export module for spirv\n";
|
std::cout << "reading export module for spirv\n";
|
||||||
}
|
}
|
||||||
std::string buildLog;
|
std::string buildLog;
|
||||||
auto exportBinaryModule = compileToSpirV(exportModuleSrc, "", buildLog);
|
auto exportBinaryModule = compileToSpirV(const_cast<const char *>(exportModuleSrcValue), "", buildLog);
|
||||||
if (buildLog.size() > 0) {
|
if (buildLog.size() > 0) {
|
||||||
std::cout << "Build log " << buildLog;
|
std::cout << "Build log " << buildLog;
|
||||||
}
|
}
|
||||||
|
@ -114,10 +163,35 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
SUCCESS_OR_TERMINATE(zeModuleCreate(context, device, &exportModuleDesc, &exportModule, nullptr));
|
SUCCESS_OR_TERMINATE(zeModuleCreate(context, device, &exportModuleDesc, &exportModule, nullptr));
|
||||||
|
|
||||||
|
if (circularDep) {
|
||||||
|
if (verbose) {
|
||||||
|
std::cout << "reading export module2 for spirv\n";
|
||||||
|
}
|
||||||
|
auto exportBinaryModule2 = compileToSpirV(exportModuleSrc2CircDep, "", buildLog);
|
||||||
|
if (buildLog.size() > 0) {
|
||||||
|
std::cout << "Build log " << buildLog;
|
||||||
|
}
|
||||||
|
SUCCESS_OR_TERMINATE((0 == exportBinaryModule2.size()));
|
||||||
|
|
||||||
|
ze_module_desc_t exportModuleDesc2 = {ZE_STRUCTURE_TYPE_MODULE_DESC};
|
||||||
|
exportModuleDesc2.format = ZE_MODULE_FORMAT_IL_SPIRV;
|
||||||
|
exportModuleDesc2.pInputModule = reinterpret_cast<const uint8_t *>(exportBinaryModule2.data());
|
||||||
|
exportModuleDesc2.inputSize = exportBinaryModule2.size();
|
||||||
|
|
||||||
|
// -library-compliation is required for the non-kernel functions to be listed as exported by the Intel Graphics Compiler
|
||||||
|
exportModuleDesc2.pBuildFlags = "-library-compilation";
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
std::cout << "building export module\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
SUCCESS_OR_TERMINATE(zeModuleCreate(context, device, &exportModuleDesc2, &exportModule2, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << "reading import module for spirv\n";
|
std::cout << "reading import module for spirv\n";
|
||||||
}
|
}
|
||||||
auto importBinaryModule = compileToSpirV(importModuleSrc, "", buildLog);
|
auto importBinaryModule = compileToSpirV(const_cast<const char *>(importModuleSrcValue), "", buildLog);
|
||||||
if (buildLog.size() > 0) {
|
if (buildLog.size() > 0) {
|
||||||
std::cout << "Build log " << buildLog;
|
std::cout << "Build log " << buildLog;
|
||||||
}
|
}
|
||||||
|
@ -128,14 +202,14 @@ int main(int argc, char *argv[]) {
|
||||||
importModuleDesc.format = ZE_MODULE_FORMAT_IL_SPIRV;
|
importModuleDesc.format = ZE_MODULE_FORMAT_IL_SPIRV;
|
||||||
importModuleDesc.pInputModule = reinterpret_cast<const uint8_t *>(importBinaryModule.data());
|
importModuleDesc.pInputModule = reinterpret_cast<const uint8_t *>(importBinaryModule.data());
|
||||||
importModuleDesc.inputSize = importBinaryModule.size();
|
importModuleDesc.inputSize = importBinaryModule.size();
|
||||||
|
if (circularDep) {
|
||||||
|
importModuleDesc.pBuildFlags = "-library-compilation";
|
||||||
|
}
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
std::cout << "building import module\n";
|
std::cout << "building import module\n";
|
||||||
}
|
}
|
||||||
SUCCESS_OR_TERMINATE(zeModuleCreate(context, device, &importModuleDesc, &importModule, nullptr));
|
SUCCESS_OR_TERMINATE(zeModuleCreate(context, device, &importModuleDesc, &importModule, nullptr));
|
||||||
|
|
||||||
ze_module_handle_t modulesToLink[] = {importModule, exportModule};
|
|
||||||
|
|
||||||
// Dynamically linking the two Modules to resolve the symbols
|
// Dynamically linking the two Modules to resolve the symbols
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
@ -144,7 +218,13 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
ze_module_build_log_handle_t dynLinkLog;
|
ze_module_build_log_handle_t dynLinkLog;
|
||||||
|
|
||||||
SUCCESS_OR_TERMINATE(zeModuleDynamicLink(2, modulesToLink, &dynLinkLog));
|
if (circularDep) {
|
||||||
|
ze_module_handle_t modulesToLink[] = {importModule, exportModule, exportModule2};
|
||||||
|
SUCCESS_OR_TERMINATE(zeModuleDynamicLink(numModules, modulesToLink, &dynLinkLog));
|
||||||
|
} else {
|
||||||
|
ze_module_handle_t modulesToLink[] = {importModule, exportModule};
|
||||||
|
SUCCESS_OR_TERMINATE(zeModuleDynamicLink(numModules, modulesToLink, &dynLinkLog));
|
||||||
|
}
|
||||||
|
|
||||||
size_t buildLogSize;
|
size_t buildLogSize;
|
||||||
SUCCESS_OR_TERMINATE(zeModuleBuildLogGetString(dynLinkLog, &buildLogSize, nullptr));
|
SUCCESS_OR_TERMINATE(zeModuleBuildLogGetString(dynLinkLog, &buildLogSize, nullptr));
|
||||||
|
@ -202,8 +282,10 @@ int main(int argc, char *argv[]) {
|
||||||
SUCCESS_OR_TERMINATE(zeCommandQueueSynchronize(cmdQueue, std::numeric_limits<uint32_t>::max()));
|
SUCCESS_OR_TERMINATE(zeCommandQueueSynchronize(cmdQueue, std::numeric_limits<uint32_t>::max()));
|
||||||
|
|
||||||
// Validate results
|
// Validate results
|
||||||
|
|
||||||
int expectedResult = (((1 + 2) * 2) - 1);
|
int expectedResult = (((1 + 2) * 2) - 1);
|
||||||
|
if (circularDep) {
|
||||||
|
expectedResult = (((((1 + 2) + 2) + 5) * 2) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (expectedResult != *(int *)resultBuffer) {
|
if (expectedResult != *(int *)resultBuffer) {
|
||||||
std::cout << "Result:" << *(int *)resultBuffer << " invalid\n";
|
std::cout << "Result:" << *(int *)resultBuffer << " invalid\n";
|
||||||
|
@ -223,6 +305,9 @@ int main(int argc, char *argv[]) {
|
||||||
SUCCESS_OR_TERMINATE(zeKernelDestroy(importKernel));
|
SUCCESS_OR_TERMINATE(zeKernelDestroy(importKernel));
|
||||||
SUCCESS_OR_TERMINATE(zeModuleDestroy(importModule));
|
SUCCESS_OR_TERMINATE(zeModuleDestroy(importModule));
|
||||||
SUCCESS_OR_TERMINATE(zeModuleDestroy(exportModule));
|
SUCCESS_OR_TERMINATE(zeModuleDestroy(exportModule));
|
||||||
|
if (circularDep) {
|
||||||
|
SUCCESS_OR_TERMINATE(zeModuleDestroy(exportModule2));
|
||||||
|
}
|
||||||
SUCCESS_OR_TERMINATE(zeContextDestroy(context));
|
SUCCESS_OR_TERMINATE(zeContextDestroy(context));
|
||||||
std::cout << "\nZello Dynamic Link Results validation " << (outputValidationSuccessful ? "PASSED" : "FAILED") << "\n";
|
std::cout << "\nZello Dynamic Link Results validation " << (outputValidationSuccessful ? "PASSED" : "FAILED") << "\n";
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1087,9 +1087,11 @@ struct ModuleDynamicLinkTests : public Test<ModuleFixture> {
|
||||||
Test<ModuleFixture>::SetUp();
|
Test<ModuleFixture>::SetUp();
|
||||||
module0 = std::make_unique<Module>(device, nullptr, ModuleType::User);
|
module0 = std::make_unique<Module>(device, nullptr, ModuleType::User);
|
||||||
module1 = std::make_unique<Module>(device, nullptr, ModuleType::User);
|
module1 = std::make_unique<Module>(device, nullptr, ModuleType::User);
|
||||||
|
module2 = std::make_unique<Module>(device, nullptr, ModuleType::User);
|
||||||
}
|
}
|
||||||
std::unique_ptr<Module> module0;
|
std::unique_ptr<Module> module0;
|
||||||
std::unique_ptr<Module> module1;
|
std::unique_ptr<Module> module1;
|
||||||
|
std::unique_ptr<Module> module2;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ModuleDynamicLinkTests, givenCallToDynamicLinkOnModulesWithoutUnresolvedSymbolsThenSuccessIsReturned) {
|
TEST_F(ModuleDynamicLinkTests, givenCallToDynamicLinkOnModulesWithoutUnresolvedSymbolsThenSuccessIsReturned) {
|
||||||
|
@ -1215,6 +1217,100 @@ TEST_F(ModuleDynamicLinkTests, givenModuleWithUnresolvedSymbolWhenTheOtherModule
|
||||||
EXPECT_EQ(module0->kernelImmDatas[0]->getResidencyContainer().back(), &alloc);
|
EXPECT_EQ(module0->kernelImmDatas[0]->getResidencyContainer().back(), &alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ModuleDynamicLinkTests, givenMultipleModulesWithUnresolvedSymbolWhenTheEachModuleDefinesTheSymbolThenTheExportedFunctionSurfaceInBothModulesIsAddedToTheResidencyContainer) {
|
||||||
|
|
||||||
|
uint64_t gpuAddress0 = 0x12345;
|
||||||
|
uint64_t gpuAddress1 = 0x6789;
|
||||||
|
uint64_t gpuAddress2 = 0x1479;
|
||||||
|
uint32_t offset = 0x20;
|
||||||
|
|
||||||
|
NEO::Linker::RelocationInfo unresolvedRelocation;
|
||||||
|
unresolvedRelocation.symbolName = "unresolved";
|
||||||
|
unresolvedRelocation.offset = offset;
|
||||||
|
unresolvedRelocation.type = NEO::Linker::RelocationInfo::Type::Address;
|
||||||
|
NEO::Linker::UnresolvedExternal unresolvedExternal;
|
||||||
|
unresolvedExternal.unresolvedRelocation = unresolvedRelocation;
|
||||||
|
|
||||||
|
NEO::Linker::RelocationInfo unresolvedRelocationCircular;
|
||||||
|
unresolvedRelocationCircular.symbolName = "unresolvedCircular";
|
||||||
|
unresolvedRelocationCircular.offset = offset;
|
||||||
|
unresolvedRelocationCircular.type = NEO::Linker::RelocationInfo::Type::Address;
|
||||||
|
NEO::Linker::UnresolvedExternal unresolvedExternalCircular;
|
||||||
|
unresolvedExternalCircular.unresolvedRelocation = unresolvedRelocationCircular;
|
||||||
|
|
||||||
|
NEO::Linker::RelocationInfo unresolvedRelocationChained;
|
||||||
|
unresolvedRelocationChained.symbolName = "unresolvedChained";
|
||||||
|
unresolvedRelocationChained.offset = offset;
|
||||||
|
unresolvedRelocationChained.type = NEO::Linker::RelocationInfo::Type::Address;
|
||||||
|
NEO::Linker::UnresolvedExternal unresolvedExternalChained;
|
||||||
|
unresolvedExternalChained.unresolvedRelocation = unresolvedRelocationChained;
|
||||||
|
|
||||||
|
NEO::SymbolInfo module0SymbolInfo{};
|
||||||
|
NEO::Linker::RelocatedSymbol module0RelocatedSymbol{module0SymbolInfo, gpuAddress0};
|
||||||
|
|
||||||
|
NEO::SymbolInfo module1SymbolInfo{};
|
||||||
|
NEO::Linker::RelocatedSymbol module1RelocatedSymbol{module1SymbolInfo, gpuAddress1};
|
||||||
|
|
||||||
|
NEO::SymbolInfo module2SymbolInfo{};
|
||||||
|
NEO::Linker::RelocatedSymbol module2RelocatedSymbol{module2SymbolInfo, gpuAddress2};
|
||||||
|
|
||||||
|
char kernelHeap[MemoryConstants::pageSize] = {};
|
||||||
|
|
||||||
|
auto kernelInfo = std::make_unique<NEO::KernelInfo>();
|
||||||
|
kernelInfo->heapInfo.pKernelHeap = kernelHeap;
|
||||||
|
kernelInfo->heapInfo.KernelHeapSize = MemoryConstants::pageSize;
|
||||||
|
module0->getTranslationUnit()->programInfo.kernelInfos.push_back(kernelInfo.release());
|
||||||
|
|
||||||
|
auto linkerInput = std::make_unique<::WhiteBox<NEO::LinkerInput>>();
|
||||||
|
linkerInput->traits.requiresPatchingOfInstructionSegments = true;
|
||||||
|
|
||||||
|
module0->getTranslationUnit()->programInfo.linkerInput = std::move(linkerInput);
|
||||||
|
module0->unresolvedExternalsInfo.push_back({unresolvedRelocation});
|
||||||
|
module0->unresolvedExternalsInfo[0].instructionsSegmentId = 0u;
|
||||||
|
|
||||||
|
auto kernelImmData = std::make_unique<WhiteBox<::L0::KernelImmutableData>>(device);
|
||||||
|
kernelImmData->isaGraphicsAllocation.reset(neoDevice->getMemoryManager()->allocateGraphicsMemoryWithProperties(
|
||||||
|
{device->getRootDeviceIndex(), MemoryConstants::pageSize, NEO::AllocationType::KERNEL_ISA, neoDevice->getDeviceBitfield()}));
|
||||||
|
|
||||||
|
module0->kernelImmDatas.push_back(std::move(kernelImmData));
|
||||||
|
|
||||||
|
module0->symbols[unresolvedRelocationCircular.symbolName] = module0RelocatedSymbol;
|
||||||
|
MockGraphicsAllocation alloc0;
|
||||||
|
module0->exportedFunctionsSurface = &alloc0;
|
||||||
|
|
||||||
|
char kernelHeap2[MemoryConstants::pageSize] = {};
|
||||||
|
|
||||||
|
auto kernelInfo2 = std::make_unique<NEO::KernelInfo>();
|
||||||
|
kernelInfo2->heapInfo.pKernelHeap = kernelHeap2;
|
||||||
|
kernelInfo2->heapInfo.KernelHeapSize = MemoryConstants::pageSize;
|
||||||
|
module1->getTranslationUnit()->programInfo.kernelInfos.push_back(kernelInfo2.release());
|
||||||
|
|
||||||
|
auto linkerInput1 = std::make_unique<::WhiteBox<NEO::LinkerInput>>();
|
||||||
|
linkerInput1->traits.requiresPatchingOfInstructionSegments = true;
|
||||||
|
|
||||||
|
module1->getTranslationUnit()->programInfo.linkerInput = std::move(linkerInput1);
|
||||||
|
module1->unresolvedExternalsInfo.push_back({unresolvedRelocationCircular});
|
||||||
|
module1->unresolvedExternalsInfo[0].instructionsSegmentId = 0u;
|
||||||
|
module1->unresolvedExternalsInfo.push_back({unresolvedRelocationChained});
|
||||||
|
module1->unresolvedExternalsInfo[1].instructionsSegmentId = 0u;
|
||||||
|
|
||||||
|
module1->symbols[unresolvedRelocation.symbolName] = module1RelocatedSymbol;
|
||||||
|
MockGraphicsAllocation alloc1;
|
||||||
|
module1->exportedFunctionsSurface = &alloc1;
|
||||||
|
|
||||||
|
module2->symbols[unresolvedRelocationChained.symbolName] = module2RelocatedSymbol;
|
||||||
|
MockGraphicsAllocation alloc2;
|
||||||
|
module2->exportedFunctionsSurface = &alloc2;
|
||||||
|
|
||||||
|
std::vector<ze_module_handle_t> hModules = {module0->toHandle(), module1->toHandle(), module2->toHandle()};
|
||||||
|
ze_result_t res = module0->performDynamicLink(3, hModules.data(), nullptr);
|
||||||
|
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
|
||||||
|
EXPECT_EQ((int)module0->kernelImmDatas[0]->getResidencyContainer().size(), 4);
|
||||||
|
EXPECT_TRUE(std::find(module0->kernelImmDatas[0]->getResidencyContainer().begin(), module0->kernelImmDatas[0]->getResidencyContainer().end(), &alloc0) != module0->kernelImmDatas[0]->getResidencyContainer().end());
|
||||||
|
EXPECT_TRUE(std::find(module0->kernelImmDatas[0]->getResidencyContainer().begin(), module0->kernelImmDatas[0]->getResidencyContainer().end(), &alloc1) != module0->kernelImmDatas[0]->getResidencyContainer().end());
|
||||||
|
EXPECT_TRUE(std::find(module0->kernelImmDatas[0]->getResidencyContainer().begin(), module0->kernelImmDatas[0]->getResidencyContainer().end(), &alloc2) != module0->kernelImmDatas[0]->getResidencyContainer().end());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ModuleDynamicLinkTests, givenModuleWithUnresolvedSymbolWhenTheOtherModuleDefinesTheSymbolThenTheBuildLogContainsTheSuccessfulLinkage) {
|
TEST_F(ModuleDynamicLinkTests, givenModuleWithUnresolvedSymbolWhenTheOtherModuleDefinesTheSymbolThenTheBuildLogContainsTheSuccessfulLinkage) {
|
||||||
|
|
||||||
uint64_t gpuAddress = 0x12345;
|
uint64_t gpuAddress = 0x12345;
|
||||||
|
|
Loading…
Reference in New Issue