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:
parent
ad19eda689
commit
d119494854
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -135,6 +135,8 @@ struct LinkerInput {
|
|||
return valid;
|
||||
}
|
||||
|
||||
bool undefinedSymbolsAllowed = false;
|
||||
|
||||
protected:
|
||||
Traits traits;
|
||||
SymbolMap symbols;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
Loading…
Reference in New Issue