diff --git a/level_zero/core/source/module/module_imp.cpp b/level_zero/core/source/module/module_imp.cpp index faff39d92a..950af42989 100644 --- a/level_zero/core/source/module/module_imp.cpp +++ b/level_zero/core/source/module/module_imp.cpp @@ -208,6 +208,7 @@ bool ModuleTranslationUnit::processUnpackedBinary() { NEO::DecodeError decodeError; NEO::DeviceBinaryFormat singleDeviceBinaryFormat; + programInfo.levelZeroDynamicLinkProgram = true; std::tie(decodeError, singleDeviceBinaryFormat) = NEO::decodeSingleDeviceBinary(programInfo, binary, decodeErrors, decodeWarnings); if (decodeWarnings.empty() == false) { PRINT_DEBUG_STRING(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "%s\n", decodeWarnings.c_str()); @@ -547,6 +548,7 @@ bool ModuleImp::linkBinary() { auto error = constructLinkerErrorMessage(unresolvedExternalsInfo, kernelNames); moduleBuildLog->appendString(error.c_str(), error.size()); } + isFullyLinked = false; return LinkingStatus::LinkedPartially == linkStatus; } else { copyPatchedSegments(isaSegmentsForPatching); @@ -706,6 +708,18 @@ ze_result_t ModuleImp::performDynamicLink(uint32_t numModules, NEO::Linker::patchAddress(relocAddress, symbolIt->second, unresolvedExternal.unresolvedRelocation); numPatchedSymbols++; moduleId->importedSymbolAllocations.insert(moduleHandle->exportedFunctionsSurface); + // 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; } } diff --git a/level_zero/core/test/unit_tests/sources/module/test_module.cpp b/level_zero/core/test/unit_tests/sources/module/test_module.cpp index 9ae2e3ea62..e75dff2908 100644 --- a/level_zero/core/test/unit_tests/sources/module/test_module.cpp +++ b/level_zero/core/test/unit_tests/sources/module/test_module.cpp @@ -845,6 +845,52 @@ TEST_F(ModuleDynamicLinkTests, givenModuleWithUnresolvedSymbolWhenTheOtherModule EXPECT_EQ(gpuAddress, *reinterpret_cast(ptrOffset(isaPtr, offset))); } +TEST_F(ModuleDynamicLinkTests, givenModuleWithUnresolvedSymbolWhenTheOtherModuleDefinesTheSymbolThenTheExportedFunctionSurfaceIntheExportModuleIsAddedToTheImportModuleResidencyContainer) { + + uint64_t gpuAddress = 0x12345; + 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::SymbolInfo symbolInfo{}; + NEO::Linker::RelocatedSymbol relocatedSymbol{symbolInfo, gpuAddress}; + + char kernelHeap[MemoryConstants::pageSize] = {}; + + auto kernelInfo = std::make_unique(); + kernelInfo->heapInfo.pKernelHeap = kernelHeap; + kernelInfo->heapInfo.KernelHeapSize = MemoryConstants::pageSize; + module0->getTranslationUnit()->programInfo.kernelInfos.push_back(kernelInfo.release()); + + auto linkerInput = std::make_unique<::WhiteBox>(); + 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>(device); + kernelImmData->isaGraphicsAllocation.reset(neoDevice->getMemoryManager()->allocateGraphicsMemoryWithProperties( + {device->getRootDeviceIndex(), MemoryConstants::pageSize, NEO::GraphicsAllocation::AllocationType::KERNEL_ISA, neoDevice->getDeviceBitfield()})); + + module0->kernelImmDatas.push_back(std::move(kernelImmData)); + + module1->symbols[unresolvedRelocation.symbolName] = relocatedSymbol; + MockGraphicsAllocation alloc; + module1->exportedFunctionsSurface = &alloc; + + std::vector 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(module0->kernelImmDatas[0]->getResidencyContainer().back(), &alloc); +} + class DeviceModuleSetArgBufferTest : public ModuleFixture, public ::testing::Test { public: void SetUp() override { diff --git a/shared/source/compiler_interface/linker.cpp b/shared/source/compiler_interface/linker.cpp index 3989d81d8e..685aa36aa8 100644 --- a/shared/source/compiler_interface/linker.cpp +++ b/shared/source/compiler_interface/linker.cpp @@ -75,6 +75,15 @@ bool LinkerInput::decodeExportedFunctionsSymbolTable(const void *data, uint32_t DEBUG_BREAK_IF(true); this->valid = false; return false; + case vISA::S_UNDEF: + if (this->undefinedSymbolsAllowed) { + symbols.erase(symbolEntryIt->s_name); + break; + } else { + DEBUG_BREAK_IF(true); + this->valid = false; + return false; + } case vISA::S_GLOBAL_VAR: symbolInfo.segment = SegmentType::GlobalVariables; traits.exportsGlobalVariables = true; diff --git a/shared/source/compiler_interface/linker.h b/shared/source/compiler_interface/linker.h index 62fee32c3e..94c5619d5b 100644 --- a/shared/source/compiler_interface/linker.h +++ b/shared/source/compiler_interface/linker.h @@ -135,6 +135,8 @@ struct LinkerInput { return valid; } + bool undefinedSymbolsAllowed = false; + protected: Traits traits; SymbolMap symbols; diff --git a/shared/source/program/program_info.h b/shared/source/program/program_info.h index 577ae3a9de..b0b6f9bcca 100644 --- a/shared/source/program/program_info.h +++ b/shared/source/program/program_info.h @@ -42,6 +42,7 @@ struct ProgramInfo { std::vector kernelInfos; Elf::Elf decodedElf; uint32_t grfSize = 32U; + bool levelZeroDynamicLinkProgram = false; }; size_t getMaxInlineSlmNeeded(const ProgramInfo &programInfo); diff --git a/shared/source/program/program_info_from_patchtokens.cpp b/shared/source/program/program_info_from_patchtokens.cpp index ac77f0142a..739c9e6e0f 100644 --- a/shared/source/program/program_info_from_patchtokens.cpp +++ b/shared/source/program/program_info_from_patchtokens.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2020-2021 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -34,6 +34,7 @@ void populateSingleKernelInfo(ProgramInfo &dst, const PatchTokenBinary::ProgramF if (decodedKernel.tokens.programSymbolTable) { dst.prepareLinkerInputStorage(); + dst.linkerInput->undefinedSymbolsAllowed = dst.levelZeroDynamicLinkProgram; dst.linkerInput->decodeExportedFunctionsSymbolTable(decodedKernel.tokens.programSymbolTable + 1, decodedKernel.tokens.programSymbolTable->NumEntries, kernelNum); } diff --git a/shared/test/unit_test/compiler_interface/linker_tests.cpp b/shared/test/unit_test/compiler_interface/linker_tests.cpp index 8431ee0272..2c3cd2483b 100644 --- a/shared/test/unit_test/compiler_interface/linker_tests.cpp +++ b/shared/test/unit_test/compiler_interface/linker_tests.cpp @@ -199,6 +199,33 @@ TEST(LinkerInputTests, givenFunctionsSymbolTableThenUndefIsNotAllowed) { EXPECT_FALSE(linkerInput.isValid()); } +TEST(LinkerInputTests, givenFunctionsSymbolTableWithAllowUndefinedSymbolsThenUndefIsAllowed) { + 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; + linkerInput.undefinedSymbolsAllowed = true; + + auto decodeResult = linkerInput.decodeExportedFunctionsSymbolTable(&entry, 1, 3); + EXPECT_TRUE(decodeResult); + EXPECT_TRUE(linkerInput.isValid()); +} + +TEST(LinkerInputTests, givenFunctionsSymbolTableThenNoTypeIsNotAllowed) { + 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_NOTYPE; + + auto decodeResult = linkerInput.decodeExportedFunctionsSymbolTable(&entry, 1, 3); + EXPECT_FALSE(decodeResult); + EXPECT_FALSE(linkerInput.isValid()); +} + TEST(LinkerInputTests, givenRelocationTableThenRelocationEntriesAreProperlyParsed) { NEO::LinkerInput linkerInput; vISA::GenRelocEntry entry = {};