mirror of
https://github.com/intel/compute-runtime.git
synced 2025-12-24 21:18:24 +08:00
All Exported Symbols between Modules in L0 Dynamic Link are accessible
- Allow for all exported symbols between L0 Dynamically linked Modules to be accessible by adding the exported function allocations to all linked modules unconditionally. - This enables for L0 Function Pointers to be used to call functions between the modules without unresolved symbols, which were a condition to allow for exported allocations to be accessed between modules. Signed-off-by: Neil R Spruit <neil.r.spruit@intel.com>
This commit is contained in:
committed by
Compute-Runtime-Automation
parent
9b2ad0c5df
commit
a688c23166
@@ -937,19 +937,6 @@ ze_result_t ModuleImp::getProperties(ze_module_properties_t *pModuleProperties)
|
||||
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_module_handle_t *phModules,
|
||||
ze_module_build_log_handle_t *phLinkLog) {
|
||||
@@ -961,10 +948,26 @@ ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
|
||||
}
|
||||
for (auto i = 0u; i < numModules; i++) {
|
||||
auto moduleId = static_cast<ModuleImp *>(Module::fromHandle(phModules[i]));
|
||||
// Add all provided Module's Exported Functions Surface to each Module to allow for all symbols
|
||||
// to be accessed from any module either directly thru Unresolved symbol resolution below or indirectly
|
||||
// thru function pointers or callbacks between the Modules.
|
||||
for (auto i = 0u; i < numModules; i++) {
|
||||
auto moduleHandle = static_cast<ModuleImp *>(Module::fromHandle(phModules[i]));
|
||||
if (nullptr != moduleHandle->exportedFunctionsSurface) {
|
||||
moduleId->importedSymbolAllocations.insert(moduleHandle->exportedFunctionsSurface);
|
||||
}
|
||||
}
|
||||
for (auto &kernImmData : moduleId->kernelImmDatas) {
|
||||
kernImmData->getResidencyContainer().insert(kernImmData->getResidencyContainer().end(), moduleId->importedSymbolAllocations.begin(),
|
||||
moduleId->importedSymbolAllocations.end());
|
||||
}
|
||||
|
||||
// If the Module is fully linked, this means no Unresolved Symbols Exist that require patching.
|
||||
if (moduleId->isFullyLinked) {
|
||||
continue;
|
||||
}
|
||||
std::map<void *, void *> moduleDeps;
|
||||
|
||||
// Resolve Unresolved Symbols in the Relocation Table between the Modules if Required.
|
||||
NEO::Linker::PatchableSegments isaSegmentsForPatching;
|
||||
std::vector<std::vector<char>> patchedIsaTempStorage;
|
||||
uint32_t numPatchedSymbols = 0u;
|
||||
@@ -993,12 +996,6 @@ ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
|
||||
|
||||
NEO::Linker::patchAddress(relocAddress, symbolIt->second.gpuAddress, unresolvedExternal.unresolvedRelocation);
|
||||
numPatchedSymbols++;
|
||||
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) {
|
||||
std::stringstream logMessage;
|
||||
@@ -1019,39 +1016,10 @@ ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
|
||||
if (numPatchedSymbols != moduleId->unresolvedExternalsInfo.size()) {
|
||||
return ZE_RESULT_ERROR_MODULE_LINK_FAILURE;
|
||||
}
|
||||
dependencies.insert(std::pair<void *, std::map<void *, void *>>(moduleId, moduleDeps));
|
||||
moduleId->copyPatchedSegments(isaSegmentsForPatching);
|
||||
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::FunctionDependenciesT extFuncDependencies;
|
||||
|
||||
@@ -140,8 +140,6 @@ struct ModuleImp : public Module {
|
||||
return this->translationUnit.get();
|
||||
}
|
||||
|
||||
void moduleDependencyWalker(std::map<void *, std::map<void *, void *>> inDeps, void *moduleHandle, std::list<ModuleImp *> *outDeps);
|
||||
|
||||
protected:
|
||||
void copyPatchedSegments(const NEO::Linker::PatchableSegments &isaSegmentsForPatching);
|
||||
void verifyDebugCapabilities();
|
||||
|
||||
@@ -25,6 +25,7 @@ struct WhiteBox<::L0::Module> : public ::L0::ModuleImp {
|
||||
using BaseClass::copyPatchedSegments;
|
||||
using BaseClass::device;
|
||||
using BaseClass::exportedFunctionsSurface;
|
||||
using BaseClass::importedSymbolAllocations;
|
||||
using BaseClass::isFullyLinked;
|
||||
using BaseClass::kernelImmDatas;
|
||||
using BaseClass::symbols;
|
||||
|
||||
@@ -1141,13 +1141,13 @@ TEST_F(ModulePropertyTest, givenCallToGetPropertiesWithUnresolvedSymbolsThenFlag
|
||||
struct ModuleDynamicLinkTests : public Test<ModuleFixture> {
|
||||
void SetUp() override {
|
||||
Test<ModuleFixture>::SetUp();
|
||||
module0 = 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);
|
||||
module0 = std::make_unique<WhiteBox<::L0::Module>>(device, nullptr, ModuleType::User);
|
||||
module1 = std::make_unique<WhiteBox<::L0::Module>>(device, nullptr, ModuleType::User);
|
||||
module2 = std::make_unique<WhiteBox<::L0::Module>>(device, nullptr, ModuleType::User);
|
||||
}
|
||||
std::unique_ptr<Module> module0;
|
||||
std::unique_ptr<Module> module1;
|
||||
std::unique_ptr<Module> module2;
|
||||
std::unique_ptr<WhiteBox<::L0::Module>> module0;
|
||||
std::unique_ptr<WhiteBox<::L0::Module>> module1;
|
||||
std::unique_ptr<WhiteBox<::L0::Module>> module2;
|
||||
};
|
||||
|
||||
TEST_F(ModuleDynamicLinkTests, givenCallToDynamicLinkOnModulesWithoutUnresolvedSymbolsThenSuccessIsReturned) {
|
||||
@@ -1269,7 +1269,7 @@ TEST_F(ModuleDynamicLinkTests, givenModuleWithUnresolvedSymbolWhenTheOtherModule
|
||||
std::vector<ze_module_handle_t> hModules = {module0->toHandle(), module1->toHandle()};
|
||||
ze_result_t res = module0->performDynamicLink(2, hModules.data(), nullptr);
|
||||
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
|
||||
EXPECT_EQ((int)module0->kernelImmDatas[0]->getResidencyContainer().size(), 2);
|
||||
EXPECT_EQ((int)module0->kernelImmDatas[0]->getResidencyContainer().size(), 1);
|
||||
EXPECT_EQ(module0->kernelImmDatas[0]->getResidencyContainer().back(), &alloc);
|
||||
}
|
||||
|
||||
@@ -1361,12 +1361,105 @@ TEST_F(ModuleDynamicLinkTests, givenMultipleModulesWithUnresolvedSymbolWhenTheEa
|
||||
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_EQ((int)module0->kernelImmDatas[0]->getResidencyContainer().size(), 3);
|
||||
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, givenMultipleModulesWithUnresolvedSymbolWhenTheEachModuleDefinesTheSymbolThenTheExportedFunctionSurfaceInBothModulesIsAddedToTheImportedSymbolAllocations) {
|
||||
|
||||
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->importedSymbolAllocations.size(), 3);
|
||||
EXPECT_EQ((int)module1->importedSymbolAllocations.size(), 3);
|
||||
EXPECT_EQ((int)module2->importedSymbolAllocations.size(), 3);
|
||||
}
|
||||
|
||||
TEST_F(ModuleDynamicLinkTests, givenModuleWithUnresolvedSymbolWhenTheOtherModuleDefinesTheSymbolThenTheBuildLogContainsTheSuccessfulLinkage) {
|
||||
|
||||
uint64_t gpuAddress = 0x12345;
|
||||
|
||||
Reference in New Issue
Block a user