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:
Neil R Spruit
2022-05-12 02:22:31 +00:00
committed by Compute-Runtime-Automation
parent 9b2ad0c5df
commit a688c23166
4 changed files with 119 additions and 59 deletions

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;