L0 Support for Dynamic Module Linking for undefined symbols

- Support for L0 apps to handle undefined symbols reported by IGC
- Removed duplication of symbol in exported symbol list and relocation
for properly resolving the symbol during dynamic link
- Add export module's exported function surface state to the import
module's residency container during dynamic linkage to allow import
kernels to execute exported functions.

Signed-off-by: Spruit, Neil R <neil.r.spruit@intel.com>
This commit is contained in:
Spruit, Neil R 2021-07-09 20:00:18 +00:00 committed by Compute-Runtime-Automation
parent ad19eda689
commit d119494854
7 changed files with 100 additions and 0 deletions

View File

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

View File

@ -845,6 +845,52 @@ TEST_F(ModuleDynamicLinkTests, givenModuleWithUnresolvedSymbolWhenTheOtherModule
EXPECT_EQ(gpuAddress, *reinterpret_cast<uint64_t *>(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<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::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<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(module0->kernelImmDatas[0]->getResidencyContainer().back(), &alloc);
}
class DeviceModuleSetArgBufferTest : public ModuleFixture, public ::testing::Test {
public:
void SetUp() override {

View File

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

View File

@ -135,6 +135,8 @@ struct LinkerInput {
return valid;
}
bool undefinedSymbolsAllowed = false;
protected:
Traits traits;
SymbolMap symbols;

View File

@ -42,6 +42,7 @@ struct ProgramInfo {
std::vector<KernelInfo *> kernelInfos;
Elf::Elf<Elf::EI_CLASS_64> decodedElf;
uint32_t grfSize = 32U;
bool levelZeroDynamicLinkProgram = false;
};
size_t getMaxInlineSlmNeeded(const ProgramInfo &programInfo);

View File

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

View File

@ -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 = {};