Zebin: set kernel barriers based on ext funcs

This change allows for modifying kernel's barrier count
based on called external functions metadata passed
via zeInfo section in zebin.

Added parsing external functions metadata.
Added resolving external functions call graph.
Added updating kernel barriers based on called external functions.
Added support for L0 dynamic link.

Signed-off-by: Krystian Chmielewski <krystian.chmielewski@intel.com>
This commit is contained in:
Krystian Chmielewski
2022-02-28 17:44:06 +00:00
committed by Compute-Runtime-Automation
parent bae9e6f5b5
commit 0ccce5a6d7
24 changed files with 1143 additions and 98 deletions

View File

@@ -480,7 +480,7 @@ bool ModuleImp::initialize(const ze_module_desc_t *desc, NEO::Device *neoDevice)
internalBuildOptions = internalBuildOptions + tmpInternalBuildOptions;
}
}
//If the user passed in only 1 SPIRV, then fallback to standard build
// If the user passed in only 1 SPIRV, then fallback to standard build
if (inputSpirVs.size() > 1) {
success = this->translationUnit->staticLinkSpirV(inputSpirVs,
inputModuleSizes,
@@ -774,7 +774,8 @@ bool ModuleImp::linkBinary() {
globalsForPatching, constantsForPatching,
isaSegmentsForPatching, unresolvedExternalsInfo, this->device->getNEODevice(),
translationUnit->programInfo.globalConstants.initData,
translationUnit->programInfo.globalVariables.initData, kernelDescriptors);
translationUnit->programInfo.globalVariables.initData,
kernelDescriptors, translationUnit->programInfo.externalFunctions);
this->symbols = linker.extractRelocatedSymbols();
if (LinkingStatus::LinkedFully != linkStatus) {
if (moduleBuildLog) {
@@ -885,7 +886,7 @@ void ModuleImp::verifyDebugCapabilities() {
bool debugCapabilities = device->getNEODevice()->getDebugger() != nullptr;
if (debugCapabilities) {
//verify all kernels are debuggable
// verify all kernels are debuggable
for (auto kernelInfo : this->translationUnit->programInfo.kernelInfos) {
bool systemThreadSurfaceAvailable = NEO::isValidOffset(kernelInfo->kernelDescriptor.payloadMappings.implicitArgs.systemThreadSurfaceAddress.bindful) ||
NEO::isValidOffset(kernelInfo->kernelDescriptor.payloadMappings.implicitArgs.systemThreadSurfaceAddress.bindless);
@@ -1002,6 +1003,39 @@ ze_result_t ModuleImp::performDynamicLink(uint32_t numModules,
moduleId->copyPatchedSegments(isaSegmentsForPatching);
moduleId->isFullyLinked = true;
}
{
NEO::ExternalFunctionInfosT externalFunctionInfos;
NEO::FunctionDependenciesT extFuncDependencies;
NEO::KernelDependenciesT kernelDependencies;
NEO::KernelDescriptorMapT nameToKernelDescriptor;
for (auto i = 0u; i < numModules; i++) {
auto moduleId = static_cast<ModuleImp *>(Module::fromHandle(phModules[i]));
auto &programInfo = moduleId->translationUnit->programInfo;
auto toPtrVec = [](auto &inVec, auto &outPtrVec) {
auto pos = outPtrVec.size();
outPtrVec.resize(pos + inVec.size());
for (size_t i = 0; i < inVec.size(); i++) {
outPtrVec[pos + i] = &inVec[i];
}
};
toPtrVec(programInfo.externalFunctions, externalFunctionInfos);
if (programInfo.linkerInput) {
toPtrVec(programInfo.linkerInput->getFunctionDependencies(), extFuncDependencies);
toPtrVec(programInfo.linkerInput->getKernelDependencies(), kernelDependencies);
}
for (auto &kernelInfo : programInfo.kernelInfos) {
auto &kd = kernelInfo->kernelDescriptor;
nameToKernelDescriptor[kd.kernelMetadata.kernelName] = &kd;
}
}
auto error = NEO::resolveBarrierCount(externalFunctionInfos, kernelDependencies, extFuncDependencies, nameToKernelDescriptor);
if (error != NEO::RESOLVE_SUCCESS) {
return ZE_RESULT_ERROR_MODULE_LINK_FAILURE;
}
}
return ZE_RESULT_SUCCESS;
}

View File

@@ -1380,6 +1380,44 @@ TEST_F(ModuleDynamicLinkTests, givenUnresolvedSymbolsWhenModuleIsCreatedThenIsaA
EXPECT_FALSE(module->isFullyLinked);
}
TEST_F(ModuleDynamicLinkTests, givenModuleWithFunctionDependenciesWhenOtherModuleDefinesThisFunctionThenBarrierCountIsProperlyResolved) {
std::vector<ze_module_handle_t> hModules = {module0->toHandle(), module1->toHandle()};
auto linkerInput = new ::WhiteBox<NEO::LinkerInput>();
linkerInput->extFunDependencies.push_back({"funMod1", "funMod0"});
linkerInput->kernelDependencies.push_back({"funMod1", "kernel"});
module0->translationUnit->programInfo.linkerInput.reset(linkerInput);
module0->translationUnit->programInfo.externalFunctions.push_back({"funMod0", 1U, 128U, 8U});
KernelInfo *ki = new KernelInfo();
ki->kernelDescriptor.kernelMetadata.kernelName = "kernel";
module0->translationUnit->programInfo.kernelInfos.push_back(ki);
module1->translationUnit->programInfo.externalFunctions.push_back({"funMod1", 3U, 128U, 8U});
ze_result_t res = module0->performDynamicLink(2, hModules.data(), nullptr);
EXPECT_EQ(ZE_RESULT_SUCCESS, res);
EXPECT_EQ(3U, module0->translationUnit->programInfo.externalFunctions[0].barrierCount);
EXPECT_EQ(3U, ki->kernelDescriptor.kernelAttributes.barrierCount);
}
TEST_F(ModuleDynamicLinkTests, givenModuleWithFunctionDependenciesWhenOtherModuleDoesNotDefineThisFunctionThenLinkFailureIsReturned) {
std::vector<ze_module_handle_t> hModules = {module0->toHandle(), module1->toHandle()};
auto linkerInput = new ::WhiteBox<NEO::LinkerInput>();
linkerInput->extFunDependencies.push_back({"funMod1", "funMod0"});
linkerInput->kernelDependencies.push_back({"funMod1", "kernel"});
module0->translationUnit->programInfo.linkerInput.reset(linkerInput);
module0->translationUnit->programInfo.externalFunctions.push_back({"funMod0", 1U, 128U, 8U});
KernelInfo *ki = new KernelInfo();
ki->kernelDescriptor.kernelMetadata.kernelName = "kernel";
module0->translationUnit->programInfo.kernelInfos.push_back(ki);
ze_result_t res = module0->performDynamicLink(2, hModules.data(), nullptr);
EXPECT_EQ(ZE_RESULT_ERROR_MODULE_LINK_FAILURE, res);
}
class DeviceModuleSetArgBufferTest : public ModuleFixture, public ::testing::Test {
public:
void SetUp() override {
@@ -2407,5 +2445,25 @@ TEST_F(ModuleWithZebinTest, givenNonZebinaryFormatWhenGettingDebugInfoThenDebugZ
EXPECT_EQ(retCode, ZE_RESULT_SUCCESS);
}
TEST_F(ModuleWithZebinTest, givenZebinWithKernelCallingExternalFunctionThenUpdateKernelsBarrierCount) {
ZebinTestData::ZebinWithExternalFunctionsInfo zebin;
zebin.setProductFamily(static_cast<uint16_t>(device->getHwInfo().platform.eProductFamily));
ze_module_desc_t moduleDesc = {};
moduleDesc.format = ZE_MODULE_FORMAT_NATIVE;
moduleDesc.pInputModule = zebin.storage.data();
moduleDesc.inputSize = zebin.storage.size();
ModuleBuildLog *moduleBuildLog = nullptr;
auto module = std::unique_ptr<L0::ModuleImp>(new L0::ModuleImp(device, moduleBuildLog, ModuleType::User));
ASSERT_NE(nullptr, module.get());
auto moduleInitSuccess = module->initialize(&moduleDesc, device->getNEODevice());
EXPECT_TRUE(moduleInitSuccess);
const auto &kernImmData = module->getKernelImmutableData("kernel");
ASSERT_NE(nullptr, kernImmData);
EXPECT_EQ(zebin.barrierCount, kernImmData->getDescriptor().kernelAttributes.barrierCount);
}
} // namespace ult
} // namespace L0

View File

@@ -55,7 +55,8 @@ const KernelInfo *Program::getKernelInfo(size_t ordinal, uint32_t rootDeviceInde
return kernelInfoArray[ordinal];
}
cl_int Program::linkBinary(Device *pDevice, const void *constantsInitData, const void *variablesInitData, const ProgramInfo::GlobalSurfaceInfo &stringsInfo) {
cl_int Program::linkBinary(Device *pDevice, const void *constantsInitData, const void *variablesInitData,
const ProgramInfo::GlobalSurfaceInfo &stringsInfo, std::vector<NEO::ExternalFunctionInfo> &extFuncInfos) {
auto linkerInput = getLinkerInput(pDevice->getRootDeviceIndex());
if (linkerInput == nullptr) {
return CL_SUCCESS;
@@ -108,7 +109,8 @@ cl_int Program::linkBinary(Device *pDevice, const void *constantsInitData, const
bool linkSuccess = LinkingStatus::LinkedFully == linker.link(globals, constants, exportedFunctions, strings,
globalsForPatching, constantsForPatching,
isaSegmentsForPatching, unresolvedExternalsInfo,
pDevice, constantsInitData, variablesInitData, kernelDescriptors);
pDevice, constantsInitData, variablesInitData,
kernelDescriptors, extFuncInfos);
setSymbols(rootDeviceIndex, linker.extractRelocatedSymbols());
if (false == linkSuccess) {
std::vector<std::string> kernelNames;
@@ -238,7 +240,7 @@ cl_int Program::processProgramInfo(ProgramInfo &src, const ClDevice &clDevice) {
kernelInfo->apply(deviceInfoConstants);
}
return linkBinary(&clDevice.getDevice(), src.globalConstants.initData, src.globalVariables.initData, src.globalStrings);
return linkBinary(&clDevice.getDevice(), src.globalConstants.initData, src.globalVariables.initData, src.globalStrings, src.externalFunctions);
}
void Program::processDebugData(uint32_t rootDeviceIndex) {

View File

@@ -285,7 +285,8 @@ class Program : public BaseObject<_cl_program> {
cl_int packDeviceBinary(ClDevice &clDevice);
MOCKABLE_VIRTUAL cl_int linkBinary(Device *pDevice, const void *constantsInitData, const void *variablesInitData, const ProgramInfo::GlobalSurfaceInfo &stringInfo);
MOCKABLE_VIRTUAL cl_int linkBinary(Device *pDevice, const void *constantsInitData, const void *variablesInitData,
const ProgramInfo::GlobalSurfaceInfo &stringInfo, std::vector<NEO::ExternalFunctionInfo> &extFuncInfos);
void updateNonUniformFlag();
void updateNonUniformFlag(const Program **inputProgram, size_t numInputPrograms);

View File

@@ -2074,13 +2074,14 @@ HWTEST_TEMPLATED_F(BlitCopyTests, givenKernelAllocationInLocalMemoryWithoutCpuAc
ASSERT_NE(nullptr, kernelInfo.kernelAllocation);
EXPECT_TRUE(kernelInfo.kernelAllocation->isAllocatedInLocalMemoryPool());
std::vector<NEO::ExternalFunctionInfo> externalFunctions;
MockProgram program{nullptr, false, toClDeviceVector(*device)};
program.getKernelInfoArray(device->getRootDeviceIndex()).push_back(&kernelInfo);
program.setLinkerInput(device->getRootDeviceIndex(), std::move(linkerInput));
auto initialTaskCount = bcsMockContext->bcsCsr->peekTaskCount();
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {});
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {}, externalFunctions);
EXPECT_EQ(CL_SUCCESS, ret);
EXPECT_EQ(initialTaskCount + 1, bcsMockContext->bcsCsr->peekTaskCount());

View File

@@ -189,6 +189,7 @@ class MockProgram : public Program {
wasCreateDebugZebinCalled = true;
}
std::vector<NEO::ExternalFunctionInfo> externalFunctions;
std::map<uint32_t, int> processGenBinaryCalledPerRootDevice;
std::map<uint32_t, int> replaceDeviceBinaryCalledPerRootDevice;
static int getInternalOptionsCalled;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2021 Intel Corporation
* Copyright (C) 2018-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -488,7 +488,7 @@ TEST_F(ProgramDataTest, GivenProgramWith32bitPointerOptWhenProgramScopeConstantB
programInfo.globalConstants.initData = constantSurface.mockGfxAllocation.getUnderlyingBuffer();
pProgram->setLinkerInput(pClDevice->getRootDeviceIndex(), std::move(programInfo.linkerInput));
pProgram->linkBinary(&pClDevice->getDevice(), programInfo.globalConstants.initData, programInfo.globalVariables.initData, {});
pProgram->linkBinary(&pClDevice->getDevice(), programInfo.globalConstants.initData, programInfo.globalVariables.initData, {}, prog->externalFunctions);
uint32_t expectedAddr = static_cast<uint32_t>(constantSurface.getGraphicsAllocation(pClDevice->getRootDeviceIndex())->getGpuAddressToPatch());
EXPECT_EQ(expectedAddr, constantSurfaceStorage[0]);
EXPECT_EQ(sentinel, constantSurfaceStorage[1]);
@@ -534,7 +534,7 @@ TEST_F(ProgramDataTest, GivenProgramWith32bitPointerOptWhenProgramScopeGlobalPoi
programInfo.globalVariables.initData = globalSurface.mockGfxAllocation.getUnderlyingBuffer();
pProgram->setLinkerInput(pClDevice->getRootDeviceIndex(), std::move(programInfo.linkerInput));
pProgram->linkBinary(&pClDevice->getDevice(), programInfo.globalConstants.initData, programInfo.globalVariables.initData, {});
pProgram->linkBinary(&pClDevice->getDevice(), programInfo.globalConstants.initData, programInfo.globalVariables.initData, {}, prog->externalFunctions);
uint32_t expectedAddr = static_cast<uint32_t>(globalSurface.getGraphicsAllocation(pClDevice->getRootDeviceIndex())->getGpuAddressToPatch());
EXPECT_EQ(expectedAddr, globalSurfaceStorage[0]);
EXPECT_EQ(sentinel, globalSurfaceStorage[1]);
@@ -561,7 +561,7 @@ TEST(ProgramLinkBinaryTest, whenLinkerInputEmptyThenLinkSuccessful) {
auto device = std::make_unique<MockClDevice>(MockDevice::createWithNewExecutionEnvironment<MockDevice>(defaultHwInfo.get()));
MockProgram program{nullptr, false, toClDeviceVector(*device)};
program.setLinkerInput(device->getRootDeviceIndex(), std::move(linkerInput));
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {});
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {}, program.externalFunctions);
EXPECT_EQ(CL_SUCCESS, ret);
}
@@ -586,7 +586,7 @@ TEST(ProgramLinkBinaryTest, whenLinkerUnresolvedExternalThenLinkFailedAndBuildLo
std::string buildLog = program.getBuildLog(device->getRootDeviceIndex());
EXPECT_TRUE(buildLog.empty());
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {});
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {}, program.externalFunctions);
EXPECT_NE(CL_SUCCESS, ret);
program.getKernelInfoArray(rootDeviceIndex).clear();
buildLog = program.getBuildLog(rootDeviceIndex);
@@ -633,7 +633,7 @@ TEST_F(ProgramDataTest, whenLinkerInputValidThenIsaIsProperlyPatched) {
buildInfo.globalSurface = new MockGraphicsAllocation(globalVariablesBuffer.data(), globalVariablesBuffer.size());
buildInfo.constantSurface = new MockGraphicsAllocation(globalConstantsBuffer.data(), globalConstantsBuffer.size());
auto ret = program.linkBinary(&pClDevice->getDevice(), globalConstantsInitData.data(), globalVariablesInitData.data(), {});
auto ret = program.linkBinary(&pClDevice->getDevice(), globalConstantsInitData.data(), globalVariablesInitData.data(), {}, program.externalFunctions);
EXPECT_EQ(CL_SUCCESS, ret);
linkerInput.reset(static_cast<WhiteBox<LinkerInput> *>(buildInfo.linkerInput.release()));
@@ -681,7 +681,7 @@ TEST_F(ProgramDataTest, whenRelocationsAreNotNeededThenIsaIsPreserved) {
buildInfo.globalSurface = new MockGraphicsAllocation(globalVariablesBuffer.data(), globalVariablesBuffer.size());
buildInfo.constantSurface = new MockGraphicsAllocation(globalConstantsBuffer.data(), globalConstantsBuffer.size());
auto ret = program.linkBinary(&pClDevice->getDevice(), globalConstantsInitData.data(), globalVariablesInitData.data(), {});
auto ret = program.linkBinary(&pClDevice->getDevice(), globalConstantsInitData.data(), globalVariablesInitData.data(), {}, program.externalFunctions);
EXPECT_EQ(CL_SUCCESS, ret);
EXPECT_EQ(kernelHeapData, kernelHeap);
@@ -722,7 +722,7 @@ TEST(ProgramStringSectionTest, WhenConstStringBufferIsPresentThenUseItForLinking
const char constStringData[] = "Hello World!\n";
auto stringsAddr = reinterpret_cast<uintptr_t>(constStringData);
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {constStringData, sizeof(constStringData)});
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {constStringData, sizeof(constStringData)}, program.externalFunctions);
EXPECT_EQ(CL_SUCCESS, ret);
EXPECT_EQ(static_cast<size_t>(stringsAddr), *reinterpret_cast<size_t *>(patchAddr));
@@ -747,7 +747,7 @@ TEST(ProgramImplicitArgsTest, givenImplicitRelocationAndStackCallsThenKernelRequ
linkerInput->relocations.push_back({{implicitArgsRelocationSymbolName, 0x8, LinkerInput::RelocationInfo::Type::AddressLow, SegmentType::Instructions}});
linkerInput->traits.requiresPatchingOfInstructionSegments = true;
program.setLinkerInput(rootDeviceIndex, std::move(linkerInput));
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {});
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {}, program.externalFunctions);
EXPECT_EQ(CL_SUCCESS, ret);
EXPECT_TRUE(kernelInfo.kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
@@ -779,7 +779,7 @@ TEST(ProgramImplicitArgsTest, givenImplicitRelocationAndEnabledDebuggerThenKerne
linkerInput->relocations.push_back({{implicitArgsRelocationSymbolName, 0x8, LinkerInput::RelocationInfo::Type::AddressLow, SegmentType::Instructions}});
linkerInput->traits.requiresPatchingOfInstructionSegments = true;
program.setLinkerInput(rootDeviceIndex, std::move(linkerInput));
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {});
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {}, program.externalFunctions);
EXPECT_EQ(CL_SUCCESS, ret);
EXPECT_TRUE(kernelInfo.kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
@@ -805,7 +805,7 @@ TEST(ProgramImplicitArgsTest, givenImplicitRelocationAndNoStackCallsAndDisabledD
linkerInput->relocations.push_back({{implicitArgsRelocationSymbolName, 0x8, LinkerInput::RelocationInfo::Type::AddressLow, SegmentType::Instructions}});
linkerInput->traits.requiresPatchingOfInstructionSegments = true;
program.setLinkerInput(rootDeviceIndex, std::move(linkerInput));
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {});
auto ret = program.linkBinary(&device->getDevice(), nullptr, nullptr, {}, program.externalFunctions);
EXPECT_EQ(CL_SUCCESS, ret);
EXPECT_FALSE(kernelInfo.kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);

View File

@@ -1201,13 +1201,13 @@ TEST_F(ProgramFromSourceTest, GivenAdvancedOptionsWhenCreatingProgramThenSuccess
const char *sources[1] = {pSourceBuffer.get()};
EXPECT_NE(nullptr, pSourceBuffer);
//According to spec: If lengths is NULL, all strings in the strings argument are considered null-terminated.
// According to spec: If lengths is NULL, all strings in the strings argument are considered null-terminated.
p = Program::create(pContext, 1, sources, nullptr, retVal);
EXPECT_EQ(CL_SUCCESS, retVal);
EXPECT_NE(nullptr, p);
delete p;
//According to spec: If an element in lengths is zero, its accompanying string is null-terminated.
// According to spec: If an element in lengths is zero, its accompanying string is null-terminated.
p = Program::create(pContext, 1, sources, &sourceSize, retVal);
EXPECT_EQ(CL_SUCCESS, retVal);
EXPECT_NE(nullptr, p);
@@ -1406,7 +1406,7 @@ HWTEST_F(PatchTokenTests, givenKernelRequiringConstantAllocationWhenMakeResident
auto &residencyVector = pCommandStreamReceiver->getResidencyAllocations();
//we expect kernel ISA here and constant allocation
// we expect kernel ISA here and constant allocation
auto kernelIsa = pKernel->getKernelInfo().getGraphicsAllocation();
auto constantAllocation = pProgram->getConstantSurface(pDevice->getRootDeviceIndex());
@@ -3216,3 +3216,17 @@ TEST(ProgramTest, givenLockedProgramWhenReleasingForKernelIsCalledForEachRetainT
program.releaseForKernel();
EXPECT_FALSE(program.isLocked());
}
TEST_F(ProgramTests, givenValidZebinWithKernelCallingExternalFunctionThenUpdateKernelsBarrierCount) {
ZebinTestData::ZebinWithExternalFunctionsInfo zebin;
auto program = std::make_unique<MockProgram>(nullptr, false, toClDeviceVector(*pClDevice));
program->buildInfos[rootDeviceIndex].unpackedDeviceBinary = makeCopy(zebin.storage.data(), zebin.storage.size());
program->buildInfos[rootDeviceIndex].unpackedDeviceBinarySize = zebin.storage.size();
auto retVal = program->processGenBinary(*pClDevice);
EXPECT_EQ(CL_SUCCESS, retVal);
ASSERT_EQ(2U, program->buildInfos[rootDeviceIndex].kernelInfoArray.size());
auto &kernelInfo = program->buildInfos[rootDeviceIndex].kernelInfoArray[0];
EXPECT_EQ(zebin.barrierCount, kernelInfo->kernelDescriptor.kernelAttributes.barrierCount);
}

View File

@@ -1,5 +1,5 @@
#
# Copyright (C) 2019-2021 Intel Corporation
# Copyright (C) 2019-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
#
@@ -13,6 +13,8 @@ set(NEO_CORE_COMPILER_INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/compiler_interface.inl
${CMAKE_CURRENT_SOURCE_DIR}/create_main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/default_cache_config.h
${CMAKE_CURRENT_SOURCE_DIR}/external_functions.cpp
${CMAKE_CURRENT_SOURCE_DIR}/external_functions.h
${CMAKE_CURRENT_SOURCE_DIR}/intermediate_representations.h
${CMAKE_CURRENT_SOURCE_DIR}/linker.h
${CMAKE_CURRENT_SOURCE_DIR}/linker.inl

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/source/compiler_interface/external_functions.h"
#include "shared/source/helpers/debug_helpers.h"
#include "shared/source/kernel/kernel_descriptor.h"
#include <algorithm>
namespace NEO {
uint32_t resolveBarrierCount(ExternalFunctionInfosT externalFunctionInfos, KernelDependenciesT kernelDependencies,
FunctionDependenciesT funcDependencies, KernelDescriptorMapT &nameToKernelDescriptor) {
FuncNameToIdMapT funcNameToId;
for (size_t i = 0U; i < externalFunctionInfos.size(); i++) {
auto &extFuncInfo = externalFunctionInfos[i];
funcNameToId[extFuncInfo->functionName] = i;
}
auto error = resolveExtFuncDependencies(externalFunctionInfos, funcNameToId, funcDependencies);
if (error != RESOLVE_SUCCESS) {
return error;
}
error = resolveKernelDependencies(externalFunctionInfos, funcNameToId, kernelDependencies, nameToKernelDescriptor);
return error;
}
uint32_t getExtFuncDependencies(FuncNameToIdMapT &funcNameToId, FunctionDependenciesT funcDependencies, size_t numExternalFuncs,
DependenciesT &outDependencies, CalledByT &outCalledBy) {
outDependencies.resize(numExternalFuncs);
outCalledBy.resize(numExternalFuncs);
for (size_t i = 0; i < funcDependencies.size(); i++) {
auto funcDep = funcDependencies[i];
if (funcNameToId.count(funcDep->callerFuncName) == 0 ||
funcNameToId.count(funcDep->usedFuncName) == 0) {
return ERROR_EXTERNAL_FUNCTION_INFO_MISSING;
}
size_t callerId = funcNameToId[funcDep->callerFuncName];
size_t calleeId = funcNameToId[funcDep->usedFuncName];
outDependencies[callerId].push_back(calleeId);
outCalledBy[calleeId].push_back(callerId);
}
return RESOLVE_SUCCESS;
}
uint32_t resolveExtFuncDependencies(ExternalFunctionInfosT externalFunctionInfos, FuncNameToIdMapT &funcNameToId, FunctionDependenciesT funcDependencies) {
DependenciesT dependencies;
CalledByT calledBy;
auto error = getExtFuncDependencies(funcNameToId, funcDependencies, externalFunctionInfos.size(), dependencies, calledBy);
if (error != RESOLVE_SUCCESS) {
return error;
}
DependencyResolver depResolver(dependencies);
auto resolved = depResolver.resolveDependencies();
if (depResolver.hasLoop()) {
return ERROR_LOOP_DETECKTED;
}
for (auto calleeId : resolved) {
const auto callee = externalFunctionInfos[calleeId];
for (auto callerId : calledBy[calleeId]) {
auto caller = externalFunctionInfos[callerId];
caller->barrierCount = std::max(caller->barrierCount, callee->barrierCount);
}
}
return RESOLVE_SUCCESS;
}
uint32_t resolveKernelDependencies(ExternalFunctionInfosT externalFunctionInfos, FuncNameToIdMapT &funcNameToId, KernelDependenciesT kernelDependencies, KernelDescriptorMapT &nameToKernelDescriptor) {
for (size_t i = 0; i < kernelDependencies.size(); i++) {
auto kernelDep = kernelDependencies[i];
if (funcNameToId.count(kernelDep->usedFuncName) == 0) {
return ERROR_EXTERNAL_FUNCTION_INFO_MISSING;
} else if (nameToKernelDescriptor.count(kernelDep->kernelName) == 0) {
return ERROR_KERNEL_DESCRIPTOR_MISSING;
}
const auto functionBarrierCount = externalFunctionInfos[funcNameToId[kernelDep->usedFuncName]]->barrierCount;
auto &kernelBarrierCount = nameToKernelDescriptor[kernelDep->kernelName]->kernelAttributes.barrierCount;
kernelBarrierCount = std::max(kernelBarrierCount, functionBarrierCount);
}
return RESOLVE_SUCCESS;
}
std::vector<size_t> DependencyResolver::resolveDependencies() {
for (size_t i = 0; i < graph.size(); i++) {
if (std::find(seen.begin(), seen.end(), i) == seen.end() && graph[i].empty() == false) {
resolveDependency(i, graph[i]);
}
}
if (loopDeteckted) {
return {};
}
return resolved;
}
void DependencyResolver::resolveDependency(size_t nodeId, const std::vector<size_t> &edges) {
seen.push_back(nodeId);
for (auto &edgeId : edges) {
if (std::find(resolved.begin(), resolved.end(), edgeId) == resolved.end()) {
if (std::find(seen.begin(), seen.end(), edgeId) != seen.end()) {
loopDeteckted = true;
} else {
resolveDependency(edgeId, graph[edgeId]);
}
}
}
resolved.push_back(nodeId);
}
} // namespace NEO

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/source/utilities/arrayref.h"
#include <string>
#include <unordered_map>
#include <vector>
#pragma once
namespace NEO {
struct KernelDescriptor;
enum ExternalFunctionResolveError : uint32_t {
RESOLVE_SUCCESS = 0,
ERROR_EXTERNAL_FUNCTION_INFO_MISSING,
ERROR_KERNEL_DESCRIPTOR_MISSING,
ERROR_LOOP_DETECKTED
};
struct ExternalFunctionInfo {
std::string functionName;
uint8_t barrierCount;
uint16_t numGrfRequired;
uint8_t simdSize;
};
struct ExternalFunctionUsageKernel {
std::string usedFuncName;
std::string kernelName;
};
struct ExternalFunctionUsageExtFunc {
std::string usedFuncName;
std::string callerFuncName;
};
using ExternalFunctionInfosT = std::vector<ExternalFunctionInfo *>;
using KernelDependenciesT = std::vector<const ExternalFunctionUsageKernel *>;
using FunctionDependenciesT = std::vector<const ExternalFunctionUsageExtFunc *>;
using KernelDescriptorMapT = std::unordered_map<std::string, KernelDescriptor *>;
using FuncNameToIdMapT = std::unordered_map<std::string, size_t>;
using DependenciesT = std::vector<std::vector<size_t>>;
using CalledByT = std::vector<std::vector<size_t>>;
class DependencyResolver {
public:
DependencyResolver(const std::vector<std::vector<size_t>> &graph) : graph(graph) {}
std::vector<size_t> resolveDependencies();
inline bool hasLoop() { return loopDeteckted; }
protected:
void resolveDependency(size_t nodeId, const std::vector<size_t> &edges);
std::vector<size_t> seen;
std::vector<size_t> resolved;
const std::vector<std::vector<size_t>> &graph;
bool loopDeteckted = false;
};
uint32_t resolveBarrierCount(ExternalFunctionInfosT externalFunctionInfos, KernelDependenciesT kernelDependencies,
FunctionDependenciesT funcDependencies, KernelDescriptorMapT &nameToKernelDescriptor);
uint32_t getExtFuncDependencies(FuncNameToIdMapT &funcNameToId, FunctionDependenciesT funcDependencies, size_t numExternalFuncs,
DependenciesT &outDependencies, CalledByT &outCalledBy);
uint32_t resolveExtFuncDependencies(ExternalFunctionInfosT externalFunctionInfos, FuncNameToIdMapT &funcNameToId, FunctionDependenciesT funcDependencies);
uint32_t resolveKernelDependencies(ExternalFunctionInfosT externalFunctionInfos, FuncNameToIdMapT &funcNameToId, KernelDependenciesT kernelDependencies, KernelDescriptorMapT &nameToKernelDescriptor);
} // namespace NEO

View File

@@ -19,10 +19,12 @@
#include "shared/source/kernel/kernel_descriptor.h"
#include "shared/source/memory_manager/graphics_allocation.h"
#include "shared/source/memory_manager/memory_manager.h"
#include "shared/source/program/program_info.h"
#include "RelocationInfo.h"
#include <sstream>
#include <unordered_map>
namespace NEO {
@@ -169,55 +171,24 @@ void LinkerInput::addElfTextSegmentRelocation(RelocationInfo relocationInfo, uin
}
void LinkerInput::decodeElfSymbolTableAndRelocations(Elf::Elf<Elf::EI_CLASS_64> &elf, const SectionNameToSegmentIdMap &nameToSegmentId) {
for (auto &reloc : elf.getRelocations()) {
NEO::LinkerInput::RelocationInfo relocationInfo;
relocationInfo.offset = reloc.offset;
relocationInfo.symbolName = reloc.symbolName;
switch (reloc.relocType) {
case uint32_t(Elf::RELOCATION_X8664_TYPE::R_X8664_64):
relocationInfo.type = NEO::LinkerInput::RelocationInfo::Type::Address;
break;
case uint32_t(Elf::RELOCATION_X8664_TYPE::R_X8664_32):
relocationInfo.type = NEO::LinkerInput::RelocationInfo::Type::AddressLow;
break;
default: // Zebin relocation type
relocationInfo.type = reloc.relocType < uint32_t(NEO::LinkerInput::RelocationInfo::Type::RelocTypeMax)
? static_cast<NEO::LinkerInput::RelocationInfo::Type>(reloc.relocType)
: NEO::LinkerInput::RelocationInfo::Type::Unknown;
break;
}
auto name = elf.getSectionName(reloc.targetSectionIndex);
ConstStringRef nameRef(name);
if (nameRef.startsWith(NEO::Elf::SectionsNamesZebin::textPrefix.data())) {
auto kernelName = name.substr(static_cast<int>(NEO::Elf::SectionsNamesZebin::textPrefix.length()));
auto segmentIdIter = nameToSegmentId.find(kernelName);
if (segmentIdIter != nameToSegmentId.end()) {
this->addElfTextSegmentRelocation(relocationInfo, segmentIdIter->second);
}
} else if (nameRef.startsWith(NEO::Elf::SpecialSectionNames::data.data())) {
auto symbolSectionName = elf.getSectionName(reloc.symbolSectionIndex);
auto symbolSegment = getSegmentForSection(symbolSectionName);
auto relocationSegment = getSegmentForSection(nameRef);
if (symbolSegment != NEO::SegmentType::Unknown &&
(relocationSegment == NEO::SegmentType::GlobalConstants || relocationSegment == NEO::SegmentType::GlobalVariables)) {
relocationInfo.relocationSegment = relocationSegment;
this->addDataRelocationInfo(relocationInfo);
}
}
}
symbols.reserve(elf.getSymbols().size());
for (auto &symbol : elf.getSymbols()) {
auto bind = elf.extractSymbolBind(symbol);
auto type = elf.extractSymbolType(symbol);
if (type == Elf::SYMBOL_TABLE_TYPE::STT_FUNC) {
SymbolInfo symbolInfo;
symbolInfo.offset = static_cast<uint32_t>(symbol.value);
symbolInfo.size = static_cast<uint32_t>(symbol.size);
symbolInfo.bind = static_cast<SymbolBind>(bind);
extFuncSymbols.push_back({elf.getSymbolName(symbol.name), symbolInfo});
}
if (bind == Elf::SYMBOL_TABLE_BIND::STB_GLOBAL) {
SymbolInfo symbolInfo;
symbolInfo.offset = static_cast<uint32_t>(symbol.value);
symbolInfo.size = static_cast<uint32_t>(symbol.size);
auto type = elf.extractSymbolType(symbol);
auto symbolSectionName = elf.getSectionName(symbol.shndx);
auto symbolSegment = getSegmentForSection(symbolSectionName);
@@ -247,6 +218,67 @@ void LinkerInput::decodeElfSymbolTableAndRelocations(Elf::Elf<Elf::EI_CLASS_64>
symbols.insert({elf.getSymbolName(symbol.name), symbolInfo});
}
}
for (auto &reloc : elf.getRelocations()) {
NEO::LinkerInput::RelocationInfo relocationInfo;
relocationInfo.offset = reloc.offset;
relocationInfo.symbolName = reloc.symbolName;
switch (reloc.relocType) {
case uint32_t(Elf::RELOCATION_X8664_TYPE::R_X8664_64):
relocationInfo.type = NEO::LinkerInput::RelocationInfo::Type::Address;
break;
case uint32_t(Elf::RELOCATION_X8664_TYPE::R_X8664_32):
relocationInfo.type = NEO::LinkerInput::RelocationInfo::Type::AddressLow;
break;
default: // Zebin relocation type
relocationInfo.type = reloc.relocType < uint32_t(NEO::LinkerInput::RelocationInfo::Type::RelocTypeMax)
? static_cast<NEO::LinkerInput::RelocationInfo::Type>(reloc.relocType)
: NEO::LinkerInput::RelocationInfo::Type::Unknown;
break;
}
auto name = elf.getSectionName(reloc.targetSectionIndex);
ConstStringRef nameRef(name);
if (nameRef.startsWith(NEO::Elf::SectionsNamesZebin::textPrefix.data())) {
auto kernelName = name.substr(static_cast<int>(NEO::Elf::SectionsNamesZebin::textPrefix.length()));
auto segmentIdIter = nameToSegmentId.find(kernelName);
if (segmentIdIter != nameToSegmentId.end()) {
this->addElfTextSegmentRelocation(relocationInfo, segmentIdIter->second);
parseRelocationForExtFuncUsage(relocationInfo, kernelName);
}
} else if (nameRef.startsWith(NEO::Elf::SpecialSectionNames::data.data())) {
auto symbolSectionName = elf.getSectionName(reloc.symbolSectionIndex);
auto symbolSegment = getSegmentForSection(symbolSectionName);
auto relocationSegment = getSegmentForSection(nameRef);
if (symbolSegment != NEO::SegmentType::Unknown &&
(relocationSegment == NEO::SegmentType::GlobalConstants || relocationSegment == NEO::SegmentType::GlobalVariables)) {
relocationInfo.relocationSegment = relocationSegment;
this->addDataRelocationInfo(relocationInfo);
}
}
}
}
void LinkerInput::parseRelocationForExtFuncUsage(RelocationInfo relocInfo, std::string kernelName) {
auto extFuncSymIt = std::find_if(extFuncSymbols.begin(), extFuncSymbols.end(), [relocInfo](auto &pair) {
return pair.first == relocInfo.symbolName;
});
if (extFuncSymIt != extFuncSymbols.end()) {
if (kernelName == Elf::SectionsNamesZebin::externalFunctions.str()) {
auto callerIt = std::find_if(extFuncSymbols.begin(), extFuncSymbols.end(), [relocInfo](auto &pair) {
auto &symbol = pair.second;
return relocInfo.offset >= symbol.offset && relocInfo.offset < symbol.offset + symbol.size;
});
if (callerIt == extFuncSymbols.end()) {
this->valid = false;
return;
}
extFunDependencies.push_back({relocInfo.symbolName, callerIt->first});
} else {
kernelDependencies.push_back({relocInfo.symbolName, kernelName});
}
}
}
bool Linker::processRelocations(const SegmentInfo &globalVariables, const SegmentInfo &globalConstants, const SegmentInfo &exportedFunctions, const SegmentInfo &globalStrings) {
@@ -471,6 +503,29 @@ void Linker::applyDebugDataRelocations(const NEO::Elf::Elf<NEO::Elf::EI_CLASS_64
}
}
bool Linker::resolveExternalFunctions(const KernelDescriptorsT &kernelDescriptors, std::vector<ExternalFunctionInfo> &externalFunctions) {
ExternalFunctionInfosT externalFunctionsPtrs;
FunctionDependenciesT functionDependenciesPtrs;
KernelDependenciesT kernelDependenciesPtrs;
KernelDescriptorMapT nameToKernelDescriptor;
auto toPtrVec = [](auto &inVec, auto &outPtrVec) {
outPtrVec.resize(inVec.size());
for (size_t i = 0; i < inVec.size(); i++) {
outPtrVec[i] = &inVec[i];
}
};
toPtrVec(externalFunctions, externalFunctionsPtrs);
toPtrVec(data.getFunctionDependencies(), functionDependenciesPtrs);
toPtrVec(data.getKernelDependencies(), kernelDependenciesPtrs);
for (auto &kd : kernelDescriptors) {
nameToKernelDescriptor[kd->kernelMetadata.kernelName] = kd;
}
auto error = NEO::resolveBarrierCount(externalFunctionsPtrs, kernelDependenciesPtrs, functionDependenciesPtrs, nameToKernelDescriptor);
return (error == RESOLVE_SUCCESS) ? true : false;
}
void Linker::resolveImplicitArgs(const KernelDescriptorsT &kernelDescriptors, Device *pDevice) {
for (auto i = 0u; i < kernelDescriptors.size(); i++) {
UNRECOVERABLE_IF(!kernelDescriptors[i]);

View File

@@ -6,6 +6,7 @@
*/
#pragma once
#include "shared/source/compiler_interface/external_functions.h"
#include "shared/source/device_binary_format/elf/elf_decoder.h"
#include <cstdint>
@@ -34,6 +35,10 @@ enum class LinkingStatus : uint32_t {
LinkedFully,
LinkedPartially
};
enum class SymbolBind : uint8_t {
Local,
Global
};
inline const char *asString(SegmentType segment) {
switch (segment) {
@@ -52,6 +57,7 @@ struct SymbolInfo {
uint32_t offset = std::numeric_limits<uint32_t>::max();
uint32_t size = std::numeric_limits<uint32_t>::max();
SegmentType segment = SegmentType::Unknown;
SymbolBind bind = SymbolBind::Local;
};
struct LinkerInput {
@@ -91,6 +97,7 @@ struct LinkerInput {
Type type = Type::Unknown;
SegmentType relocationSegment = SegmentType::Unknown;
};
using SectionNameToSegmentIdMap = std::unordered_map<std::string, uint32_t>;
using Relocations = std::vector<RelocationInfo>;
using SymbolMap = std::unordered_map<std::string, SymbolInfo>;
@@ -142,12 +149,25 @@ struct LinkerInput {
bool undefinedSymbolsAllowed = false;
const std::vector<ExternalFunctionUsageKernel> &getKernelDependencies() const {
return kernelDependencies;
}
const std::vector<ExternalFunctionUsageExtFunc> &getFunctionDependencies() const {
return extFunDependencies;
}
protected:
void parseRelocationForExtFuncUsage(RelocationInfo relocInfo, std::string kernelName);
Traits traits;
SymbolMap symbols;
RelocationsPerInstSegment relocations;
Relocations dataRelocations;
std::vector<std::pair<std::string, SymbolInfo>> extFuncSymbols;
int32_t exportedFunctionsSegmentId = -1;
std::vector<ExternalFunctionUsageKernel> kernelDependencies;
std::vector<ExternalFunctionUsageExtFunc> extFunDependencies;
bool valid = true;
};
@@ -181,6 +201,7 @@ struct Linker {
using PatchableSegments = std::vector<PatchableSegment>;
using UnresolvedExternals = std::vector<UnresolvedExternal>;
using KernelDescriptorsT = std::vector<KernelDescriptor *>;
using ExternalFunctionsT = std::vector<ExternalFunctionInfo>;
Linker(const LinkerInput &data)
: data(data) {
@@ -188,7 +209,8 @@ struct Linker {
LinkingStatus link(const SegmentInfo &globalVariablesSegInfo, const SegmentInfo &globalConstantsSegInfo, const SegmentInfo &exportedFunctionsSegInfo, const SegmentInfo &globalStringsSegInfo,
GraphicsAllocation *globalVariablesSeg, GraphicsAllocation *globalConstantsSeg, const PatchableSegments &instructionsSegments,
UnresolvedExternals &outUnresolvedExternals, Device *pDevice, const void *constantsInitData, const void *variablesInitData, const KernelDescriptorsT &kernelDescriptors) {
UnresolvedExternals &outUnresolvedExternals, Device *pDevice, const void *constantsInitData, const void *variablesInitData,
const KernelDescriptorsT &kernelDescriptors, ExternalFunctionsT &externalFunctions) {
bool success = data.isValid();
auto initialUnresolvedExternalsCount = outUnresolvedExternals.size();
success = success && processRelocations(globalVariablesSegInfo, globalConstantsSegInfo, exportedFunctionsSegInfo, globalStringsSegInfo);
@@ -203,6 +225,10 @@ struct Linker {
if (initialUnresolvedExternalsCount < outUnresolvedExternals.size()) {
return LinkingStatus::LinkedPartially;
}
success = resolveExternalFunctions(kernelDescriptors, externalFunctions);
if (!success) {
return LinkingStatus::Error;
}
return LinkingStatus::LinkedFully;
}
static void patchAddress(void *relocAddress, const RelocatedSymbol &symbol, const RelocationInfo &relocation);
@@ -228,6 +254,7 @@ struct Linker {
std::vector<UnresolvedExternal> &outUnresolvedExternals, Device *pDevice,
const void *constantsInitData, const void *variablesInitData);
bool resolveExternalFunctions(const KernelDescriptorsT &kernelDescriptors, std::vector<ExternalFunctionInfo> &externalFunctions);
void resolveImplicitArgs(const KernelDescriptorsT &kernelDescriptors, Device *pDevice);
void resolveBuiltins(Device *pDevice, UnresolvedExternals &outUnresolvedExternals, const std::vector<PatchableSegment> &instructionsSegments);

View File

@@ -55,6 +55,7 @@ static constexpr ConstStringRef zeInfo = ".ze_info";
static constexpr ConstStringRef gtpinInfo = ".gtpin_info";
static constexpr ConstStringRef noteIntelGT = ".note.intelgt.compat";
static constexpr ConstStringRef vIsaAsmPrefix = ".visaasm.";
static constexpr ConstStringRef externalFunctions = "Intel_Symbol_Table_Void_Program";
} // namespace SectionsNamesZebin
static constexpr ConstStringRef IntelGtNoteOwnerName = "IntelGT";
@@ -158,6 +159,8 @@ namespace Tags {
static constexpr ConstStringRef kernels("kernels");
static constexpr ConstStringRef version("version");
static constexpr ConstStringRef globalHostAccessTable("global_host_access_table");
static constexpr ConstStringRef functions("functions");
namespace Kernel {
static constexpr ConstStringRef name("name");
static constexpr ConstStringRef executionEnv("execution_env");
@@ -279,10 +282,18 @@ static constexpr ConstStringRef hasNonKernelArgStore("has_non_kernel_arg_store")
static constexpr ConstStringRef hasNonKernelArgAtomic("has_non_kernel_arg_atomic");
} // namespace ExperimentalProperties
} // namespace Kernel
namespace GlobalHostAccessTable {
static constexpr ConstStringRef deviceName("device_name");
static constexpr ConstStringRef hostName("host_name");
} // namespace GlobalHostAccessTable
namespace Function {
static constexpr ConstStringRef name("name");
static constexpr ConstStringRef executionEnv("execution_env");
using namespace Kernel::ExecutionEnv;
} // namespace Function
} // namespace Tags
namespace Types {
@@ -540,6 +551,12 @@ struct globalHostAccessTableT {
std::string hostName;
};
} // namespace GlobalHostAccessTable
namespace Function {
namespace ExecutionEnv {
using namespace Kernel::ExecutionEnv;
}
} // namespace Function
} // namespace Types
} // namespace ZebinKernelMetadata

View File

@@ -1238,6 +1238,38 @@ NEO::DecodeError populateZeInfoVersion(NEO::Elf::ZebinKernelMetadata::Types::Ver
return NEO::DecodeError::Success;
}
NEO::DecodeError populateExternalFunctionsMetadata(NEO::ProgramInfo &dst, NEO::Yaml::YamlParser &yamlParser, const NEO::Yaml::Node &functionNd, std::string &outErrReason, std::string &outWarning) {
ConstStringRef functionName;
NEO::Elf::ZebinKernelMetadata::Types::Function::ExecutionEnv::ExecutionEnvBaseT execEnv = {};
bool isValid = true;
for (const auto &functionMetadataNd : yamlParser.createChildrenRange(functionNd)) {
auto key = yamlParser.readKey(functionMetadataNd);
if (NEO::Elf::ZebinKernelMetadata::Tags::Function::name == key) {
functionName = yamlParser.readValueNoQuotes(functionMetadataNd);
} else if (NEO::Elf::ZebinKernelMetadata::Tags::Function::executionEnv == key) {
auto execEnvErr = readZeInfoExecutionEnvironment(yamlParser, functionMetadataNd, execEnv, "external functions", outErrReason, outWarning);
if (execEnvErr != DecodeError::Success) {
isValid = false;
}
} else {
outWarning.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unknown entry \"" + yamlParser.readKey(functionMetadataNd).str() + "\" in context of : external functions\n");
}
}
if (isValid) {
NEO::ExternalFunctionInfo extFunInfo;
extFunInfo.functionName = functionName.str();
extFunInfo.barrierCount = static_cast<uint8_t>(execEnv.barrierCount);
extFunInfo.numGrfRequired = static_cast<uint16_t>(execEnv.grfCount);
extFunInfo.simdSize = static_cast<uint8_t>(execEnv.simdSize);
dst.externalFunctions.push_back(extFunInfo);
return DecodeError::Success;
} else {
return DecodeError::InvalidBinary;
}
}
template <>
DecodeError decodeSingleDeviceBinary<NEO::DeviceBinaryFormat::Zebin>(ProgramInfo &dst, const SingleDeviceBinary &src, std::string &outErrReason, std::string &outWarning) {
auto elf = Elf::decodeElf<Elf::EI_CLASS_64>(src.deviceBinary, outErrReason, outWarning);
@@ -1299,17 +1331,17 @@ DecodeError decodeSingleDeviceBinary<NEO::DeviceBinaryFormat::Zebin>(ProgramInfo
UniqueNode kernelsSectionNodes;
UniqueNode versionSectionNodes;
UniqueNode globalHostAccessTableNodes;
UniqueNode functionsSectionNodes;
for (const auto &globalScopeNd : yamlParser.createChildrenRange(*yamlParser.getRoot())) {
auto key = yamlParser.readKey(globalScopeNd);
if (NEO::Elf::ZebinKernelMetadata::Tags::kernels == key) {
kernelsSectionNodes.push_back(&globalScopeNd);
continue;
} else if (NEO::Elf::ZebinKernelMetadata::Tags::version == key) {
versionSectionNodes.push_back(&globalScopeNd);
continue;
} else if (NEO::Elf::ZebinKernelMetadata::Tags::globalHostAccessTable == key) {
globalHostAccessTableNodes.push_back(&globalScopeNd);
continue;
} else if (NEO::Elf::ZebinKernelMetadata::Tags::functions == key) {
functionsSectionNodes.push_back(&globalScopeNd);
} else {
outWarning.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unknown entry \"" + yamlParser.readKey(globalScopeNd).str() + "\" in global scope of " + NEO::Elf::SectionsNamesZebin::zeInfo.str() + "\n");
}
@@ -1368,6 +1400,20 @@ DecodeError decodeSingleDeviceBinary<NEO::DeviceBinaryFormat::Zebin>(ProgramInfo
}
}
if (functionsSectionNodes.size() > 1U) {
outErrReason.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Expected at most one " + NEO::Elf::ZebinKernelMetadata::Tags::functions.str() + " entry in global scope of " + NEO::Elf::SectionsNamesZebin::zeInfo.str() + ", got : " + std::to_string(functionsSectionNodes.size()) + "\n");
return DecodeError::InvalidBinary;
}
if (false == functionsSectionNodes.empty()) {
for (const auto &functionNd : yamlParser.createChildrenRange(*functionsSectionNodes[0])) {
auto zeInfoErr = populateExternalFunctionsMetadata(dst, yamlParser, functionNd, outErrReason, outWarning);
if (DecodeError::Success != zeInfoErr) {
return zeInfoErr;
}
}
}
return DecodeError::Success;
}

View File

@@ -117,4 +117,6 @@ NEO::DecodeError populateKernelDescriptor(NEO::ProgramInfo &dst, NEO::Elf::Elf<N
NEO::DecodeError populateZeInfoVersion(NEO::Elf::ZebinKernelMetadata::Types::Version &dst,
NEO::Yaml::YamlParser &yamlParser, const NEO::Yaml::Node &versionNd, std::string &outErrReason, std::string &outWarning);
NEO::DecodeError populateExternalFunctionsMetadata(NEO::ProgramInfo &dst, NEO::Yaml::YamlParser &yamlParser, const NEO::Yaml::Node &functionNd, std::string &outErrReason, std::string &outWarning);
} // namespace NEO

View File

@@ -7,6 +7,7 @@
#pragma once
#include "shared/source/compiler_interface/external_functions.h"
#include "shared/source/compiler_interface/linker.h"
#include <cstddef>
@@ -42,6 +43,7 @@ struct ProgramInfo {
std::unique_ptr<LinkerInput> linkerInput;
std::unordered_map<std::string, std::string> globalsDeviceToHostNameMap;
std::vector<ExternalFunctionInfo> externalFunctions;
std::vector<KernelInfo *> kernelInfos;
Elf::Elf<Elf::EI_CLASS_64> decodedElf;
uint32_t grfSize = 32U;

View File

@@ -1,5 +1,5 @@
#
# Copyright (C) 2019-2021 Intel Corporation
# Copyright (C) 2019-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
#
@@ -9,6 +9,7 @@ target_sources(${TARGET_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/compiler_cache_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/compiler_interface_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/compiler_options_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/external_functions_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/intermediate_representations_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/linker_mock.h
${CMAKE_CURRENT_SOURCE_DIR}/linker_tests.cpp

View File

@@ -0,0 +1,199 @@
/*
* Copyright (C) 2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/source/compiler_interface/external_functions.h"
#include "shared/source/kernel/kernel_descriptor.h"
#include "gtest/gtest.h"
#include <vector>
using namespace NEO;
TEST(DependencyResolverTests, GivenEmptyGraphReturnEmptyResolve) {
std::vector<std::vector<size_t>> graph = {};
DependencyResolver resolver(graph);
const auto &resolve = resolver.resolveDependencies();
EXPECT_TRUE(resolve.empty());
}
TEST(DependencyResolverTests, GivenGraphWithLoopReturnEmptyResolveAndSetLoopDeteckted) {
/*
0 -> 1
^ |
| v
3 <- 2
*/
std::vector<std::vector<size_t>> graph = {{1}, {2}, {3}, {0}};
DependencyResolver resolver(graph);
const auto &resolve = resolver.resolveDependencies();
EXPECT_TRUE(resolve.empty());
EXPECT_TRUE(resolver.hasLoop());
}
TEST(DependencyResolverTests, GivenOneConnectedGraphReturnCorrectResolve) {
/*
0 -> 1 -> 2
^
|
3 -> 4
*/
std::vector<std::vector<size_t>> graph = {{1}, {2}, {}, {1, 4}, {}};
std::vector<size_t> expectedResolve = {2, 1, 0, 4, 3};
DependencyResolver resolver(graph);
const auto &resolve = resolver.resolveDependencies();
EXPECT_EQ(expectedResolve, resolve);
}
TEST(DependencyResolverTests, GivenMultipleDisconnectedGraphsReturnCorrectResolve) {
/*
0
1 -> 2 -> 5
4 -> 3
*/
std::vector<std::vector<size_t>> graph = {{}, {2}, {5}, {}, {3}, {}};
std::vector<size_t> expectedResolve = {5, 2, 1, 3, 4};
DependencyResolver resolver(graph);
const auto &resolve = resolver.resolveDependencies();
EXPECT_EQ(expectedResolve, resolve);
}
struct ExternalFunctionsTests : public ::testing::Test {
void SetUp() override {}
void TearDown() override {}
void addExternalFunction(const std::string &functionName, uint8_t barrierCount) {
funcNameToId[functionName] = extFuncInfoStorage.size();
extFuncInfoStorage.push_back(ExternalFunctionInfo{functionName, barrierCount, 128U, 8U});
}
void addKernel(const std::string &kernelName) {
kernelDescriptorStorage.push_back(std::make_unique<KernelDescriptor>());
auto &kd = *kernelDescriptorStorage.rbegin();
kd->kernelMetadata.kernelName = kernelName;
nameToKernelDescriptor[kernelName] = kd.get();
}
void addFuncDependency(const std::string &calleeName, const std::string &callerName) {
funcDependenciesStorage.push_back({calleeName, callerName});
}
void addKernelDependency(const std::string &calleeName, const std::string &kernelCallerName) {
kernelDependenciesStorage.push_back({calleeName, kernelCallerName});
}
void clear() {
extFuncInfoStorage.clear();
kernelDependenciesStorage.clear();
funcDependenciesStorage.clear();
kernelDescriptorStorage.clear();
funcNameToId.clear();
nameToKernelDescriptor.clear();
}
void set() {
auto toPtrVec = [](auto &inVec, auto &outPtrVec) {
outPtrVec.resize(inVec.size());
for (size_t i = 0; i < inVec.size(); i++) {
outPtrVec[i] = &inVec[i];
}
};
toPtrVec(extFuncInfoStorage, extFuncInfo);
toPtrVec(funcDependenciesStorage, functionDependencies);
toPtrVec(kernelDependenciesStorage, kernelDependencies);
}
ExternalFunctionInfosT extFuncInfo;
FunctionDependenciesT functionDependencies;
KernelDependenciesT kernelDependencies;
KernelDescriptorMapT nameToKernelDescriptor;
FuncNameToIdMapT funcNameToId;
protected:
std::vector<ExternalFunctionInfo> extFuncInfoStorage;
std::vector<ExternalFunctionUsageKernel> kernelDependenciesStorage;
std::vector<ExternalFunctionUsageExtFunc> funcDependenciesStorage;
std::vector<std::unique_ptr<KernelDescriptor>> kernelDescriptorStorage;
};
TEST_F(ExternalFunctionsTests, GivenMissingExtFuncInLookupMapWhenResolvingExtFuncDependenciesThenReturnError) {
addFuncDependency("fun1", "fun0");
set();
auto error = resolveExtFuncDependencies(extFuncInfo, funcNameToId, functionDependencies);
EXPECT_EQ(ERROR_EXTERNAL_FUNCTION_INFO_MISSING, error);
clear();
addFuncDependency("fun1", "fun0");
addExternalFunction("fun1", 0);
set();
error = resolveExtFuncDependencies(extFuncInfo, funcNameToId, functionDependencies);
EXPECT_EQ(ERROR_EXTERNAL_FUNCTION_INFO_MISSING, error);
clear();
addFuncDependency("fun1", "fun0");
addExternalFunction("fun0", 0);
set();
error = resolveExtFuncDependencies(extFuncInfo, funcNameToId, functionDependencies);
EXPECT_EQ(ERROR_EXTERNAL_FUNCTION_INFO_MISSING, error);
}
TEST_F(ExternalFunctionsTests, GivenLoopWhenResolvingExtFuncDependenciesThenReturnError) {
addExternalFunction("fun0", 0);
addExternalFunction("fun1", 0);
addFuncDependency("fun0", "fun1");
addFuncDependency("fun1", "fun0");
set();
auto error = resolveExtFuncDependencies(extFuncInfo, funcNameToId, functionDependencies);
EXPECT_EQ(ERROR_LOOP_DETECKTED, error);
}
TEST_F(ExternalFunctionsTests, GivenMissingExtFuncInLookupMapWhenResolvingKernelDependenciesThenReturnError) {
addKernel("kernel");
addKernelDependency("fun0", "kernel");
set();
auto error = resolveKernelDependencies(extFuncInfo, funcNameToId, kernelDependencies, nameToKernelDescriptor);
EXPECT_EQ(ERROR_EXTERNAL_FUNCTION_INFO_MISSING, error);
}
TEST_F(ExternalFunctionsTests, GivenMissingKernelInLookupMapWhenResolvingKernelDependenciesThenReturnError) {
addExternalFunction("fun0", 0);
addKernelDependency("fun0", "kernel");
set();
auto error = resolveKernelDependencies(extFuncInfo, funcNameToId, kernelDependencies, nameToKernelDescriptor);
EXPECT_EQ(ERROR_KERNEL_DESCRIPTOR_MISSING, error);
}
TEST_F(ExternalFunctionsTests, GivenNoDependenciesWhenResolvingBarrierCountThenReturnSuccess) {
set();
auto error = resolveBarrierCount(extFuncInfo, kernelDependencies, functionDependencies, nameToKernelDescriptor);
EXPECT_EQ(RESOLVE_SUCCESS, error);
}
TEST_F(ExternalFunctionsTests, GivenMissingExtFuncInExtFuncDependenciesWhenResolvingBarrierCountThenReturnError) {
addFuncDependency("fun0", "fun1");
set();
auto error = resolveBarrierCount(extFuncInfo, kernelDependencies, functionDependencies, nameToKernelDescriptor);
EXPECT_EQ(ERROR_EXTERNAL_FUNCTION_INFO_MISSING, error);
}
TEST_F(ExternalFunctionsTests, GivenMissingExtFuncInKernelDependenciesWhenResolvingBarrierCountThenReturnError) {
addKernelDependency("fun0", "kernel");
addKernel("kernel");
set();
auto error = resolveBarrierCount(extFuncInfo, kernelDependencies, functionDependencies, nameToKernelDescriptor);
EXPECT_EQ(ERROR_EXTERNAL_FUNCTION_INFO_MISSING, error);
}
TEST_F(ExternalFunctionsTests, GivenValidFunctionAndKernelDependenciesWhenResolvingBarrierCountThenSetAppropriateBarrierCountAndReturnSuccess) {
addKernel("kernel");
addExternalFunction("fun0", 1U);
addExternalFunction("fun1", 2U);
addFuncDependency("fun1", "fun0");
addKernelDependency("fun0", "kernel");
set();
auto error = resolveBarrierCount(extFuncInfo, kernelDependencies, functionDependencies, nameToKernelDescriptor);
EXPECT_EQ(RESOLVE_SUCCESS, error);
EXPECT_EQ(2U, extFuncInfo[funcNameToId["fun0"]]->barrierCount);
EXPECT_EQ(2U, extFuncInfo[funcNameToId["fun1"]]->barrierCount);
EXPECT_EQ(2U, nameToKernelDescriptor["kernel"]->kernelAttributes.barrierCount);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2021 Intel Corporation
* Copyright (C) 2019-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -24,12 +24,23 @@ struct WhiteBox<NEO::LinkerInput> : NEO::LinkerInput {
using BaseClass::dataRelocations;
using BaseClass::exportedFunctionsSegmentId;
using BaseClass::extFuncSymbols;
using BaseClass::extFunDependencies;
using BaseClass::kernelDependencies;
using BaseClass::parseRelocationForExtFuncUsage;
using BaseClass::relocations;
using BaseClass::symbols;
using BaseClass::traits;
using BaseClass::valid;
};
template <>
struct WhiteBox<NEO::Linker> : NEO::Linker {
using BaseClass = NEO::Linker;
using BaseClass::BaseClass;
using BaseClass::resolveExternalFunctions;
};
template <typename MockT, typename ReturnT, typename... ArgsT>
struct LightMockConfig {
using MockReturnT = ReturnT;

View File

@@ -18,6 +18,7 @@
#include "shared/test/common/mocks/mock_elf.h"
#include "shared/test/common/mocks/mock_graphics_allocation.h"
#include "shared/test/common/mocks/ult_device_factory.h"
#include "shared/test/unit_test/device_binary_format/zebin_tests.h"
#include "RelocationInfo.h"
#include "gmock/gmock.h"
@@ -967,6 +968,29 @@ TEST(LinkerInputTests, GivenGlobalElfSymbolOfNoTypeWhenDecodingThenDebugBreakCal
EXPECT_FALSE(linkerInput.getTraits().exportsFunctions);
}
TEST(LinkerInputTests, GivenInvalidFunctionsSymbolsUsedInFunctionsRelocationsWhenParsingRelocationsForExtFuncUsageThenSetValidToFalse) {
WhiteBox<NEO::LinkerInput> mockLinkerInput;
auto &extFuncSymbols = mockLinkerInput.extFuncSymbols;
extFuncSymbols.resize(1);
auto &funSym = extFuncSymbols[0];
funSym.first = "fun";
funSym.second.offset = 4U;
funSym.second.size = 4U;
NEO::LinkerInput::RelocationInfo relocInfo;
relocInfo.symbolName = "fun";
relocInfo.offset = 0U;
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, NEO::Elf::SectionsNamesZebin::externalFunctions.str());
EXPECT_FALSE(mockLinkerInput.isValid());
mockLinkerInput.valid = true;
relocInfo.offset = 0x10U;
mockLinkerInput.parseRelocationForExtFuncUsage(relocInfo, NEO::Elf::SectionsNamesZebin::externalFunctions.str());
EXPECT_FALSE(mockLinkerInput.isValid());
}
TEST(LinkerTests, givenEmptyLinkerInputThenLinkerOutputIsEmpty) {
NEO::LinkerInput linkerInput;
NEO::Linker linker(linkerInput);
@@ -976,10 +1000,11 @@ TEST(LinkerTests, givenEmptyLinkerInputThenLinkerOutputIsEmpty) {
NEO::Linker::PatchableSegments patchableInstructionSegments;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors);
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
auto relocatedSymbols = linker.extractRelocatedSymbols();
@@ -996,10 +1021,12 @@ TEST(LinkerTests, givenInvalidLinkerInputThenLinkerFails) {
NEO::Linker::PatchableSegments patchableInstructionSegments;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT extFuncs;
auto linkResult = linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors);
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors, extFuncs);
EXPECT_EQ(NEO::LinkingStatus::Error, linkResult);
}
@@ -1055,6 +1082,7 @@ TEST(LinkerTests, givenUnresolvedExternalsWhenLinkThenSubDeviceIDSymbolsAreRemov
unresolvedExternals.push_back({{"__SubDeviceID", 64, NEO::Linker::RelocationInfo::Type::AddressHigh, NEO::SegmentType::Instructions}, 0u, false});
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
NEO::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *patchableConstVarSeg = nullptr;
@@ -1073,7 +1101,7 @@ TEST(LinkerTests, givenUnresolvedExternalsWhenLinkThenSubDeviceIDSymbolsAreRemov
linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, instructionsSegments,
unresolvedExternals, device.get(), nullptr, nullptr, kernelDescriptors);
unresolvedExternals, device.get(), nullptr, nullptr, kernelDescriptors, externalFunctions);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
@@ -1100,6 +1128,7 @@ TEST(LinkerTests, givenUnresolvedExternalWhenPatchingInstructionsThenLinkPartial
NEO::Linker::SegmentInfo globalVar, globalConst, exportedFunc;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
std::vector<char> instructionSegment;
instructionSegment.resize(64);
@@ -1113,7 +1142,7 @@ TEST(LinkerTests, givenUnresolvedExternalWhenPatchingInstructionsThenLinkPartial
auto linkResult = linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors);
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedPartially, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
@@ -1205,11 +1234,12 @@ TEST(LinkerTests, givenValidSymbolsAndRelocationsThenInstructionSegmentsArePrope
NEO::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *patchableConstVarSeg = nullptr;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments, unresolvedExternals,
nullptr, nullptr, nullptr, kernelDescriptors);
nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -1256,6 +1286,7 @@ TEST(LinkerTests, givenInvalidSymbolOffsetWhenPatchingInstructionsThenRelocation
globalVarSegment.segmentSize = symGlobalVariable.s_offset;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
std::vector<char> instructionSegment;
instructionSegment.resize(64, 0);
@@ -1269,7 +1300,7 @@ TEST(LinkerTests, givenInvalidSymbolOffsetWhenPatchingInstructionsThenRelocation
auto linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors);
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::Error, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -1279,7 +1310,7 @@ TEST(LinkerTests, givenInvalidSymbolOffsetWhenPatchingInstructionsThenRelocation
linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments, unresolvedExternals,
nullptr, nullptr, nullptr, kernelDescriptors);
nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
}
@@ -1307,6 +1338,7 @@ TEST(LinkerTests, givenInvalidRelocationOffsetThenPatchingOfInstructionsFails) {
globalVarSegment.segmentSize = symGlobalVariable.s_offset + symGlobalVariable.s_size;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
std::vector<char> instructionSegment;
instructionSegment.resize(relocA.r_offset + sizeof(uintptr_t), 0);
@@ -1320,7 +1352,7 @@ TEST(LinkerTests, givenInvalidRelocationOffsetThenPatchingOfInstructionsFails) {
auto linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors);
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedPartially, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(1U, relocatedSymbols.size());
@@ -1331,7 +1363,7 @@ TEST(LinkerTests, givenInvalidRelocationOffsetThenPatchingOfInstructionsFails) {
linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors);
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
}
@@ -1359,6 +1391,7 @@ TEST(LinkerTests, givenUnknownSymbolTypeWhenPatchingInstructionsThenRelocationFa
globalVarSegment.segmentSize = 64;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
std::vector<char> instructionSegment;
instructionSegment.resize(64, 0);
@@ -1374,7 +1407,7 @@ TEST(LinkerTests, givenUnknownSymbolTypeWhenPatchingInstructionsThenRelocationFa
auto linkResult = linker.link(
globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors);
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::Error, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, relocatedSymbols.size());
@@ -1383,7 +1416,7 @@ TEST(LinkerTests, givenUnknownSymbolTypeWhenPatchingInstructionsThenRelocationFa
linkerInput.symbols["A"].segment = NEO::SegmentType::GlobalVariables;
linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors);
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
}
@@ -1418,10 +1451,11 @@ TEST(LinkerTests, givenValidStringSymbolsAndRelocationsWhenPatchingThenItIsPrope
NEO::Linker linker(linkerInput);
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(
{}, {}, {}, stringSegment,
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
nullptr, nullptr, nullptr, kernelDescriptors);
nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
EXPECT_EQ(1U, linker.extractRelocatedSymbols().size());
@@ -1501,7 +1535,7 @@ TEST(LinkerTests, givenValidSymbolsAndRelocationsWhenPatchingDataSegmentsThenThe
After patching:
Const:
0x00 0x10 + &var1
0x08 0x1234
0x08 0x1234
0x10 0x0 + &fun1
Var:
@@ -1569,9 +1603,10 @@ TEST(LinkerTests, givenValidSymbolsAndRelocationsWhenPatchingDataSegmentsThenThe
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, exportedFunctionsSegmentInfo, {},
&globalVariablesPatchableSegment, &globalConstantsPatchableSegment, {},
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors);
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -1627,9 +1662,10 @@ TEST(LinkerTests, givenInvalidSymbolWhenPatchingDataSegmentsThenRelocationIsUnre
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
&globalVariablesPatchableSegment, &globalConstantsPatchableSegment, {},
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors);
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedPartially, linkResult);
EXPECT_EQ(1U, unresolvedExternals.size());
}
@@ -1665,9 +1701,10 @@ TEST(LinkerTests, givenInvalidRelocationOffsetWhenPatchingDataSegmentsThenReloca
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
&globalVariablesPatchableSegment, &globalConstantsPatchableSegment, {},
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors);
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedPartially, linkResult);
EXPECT_EQ(1U, unresolvedExternals.size());
}
@@ -1688,9 +1725,10 @@ TEST(LinkerTests, givenInvalidRelocationSegmentWhenPatchingDataSegmentsThenReloc
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link({}, {}, {}, {},
nullptr, nullptr, {},
unresolvedExternals, device.get(), nullptr, nullptr, kernelDescriptors);
unresolvedExternals, device.get(), nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedPartially, linkResult);
EXPECT_EQ(1U, unresolvedExternals.size());
}
@@ -1727,9 +1765,10 @@ TEST(LinkerTests, given32BitBinaryWithValidSymbolsAndRelocationsWhenPatchingData
auto device = std::unique_ptr<NEO::MockDevice>(NEO::MockDevice::createWithNewExecutionEnvironment<NEO::MockDevice>(NEO::defaultHwInfo.get()));
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(globalVariablesSegmentInfo, globalConstantsSegmentInfo, {}, {},
&globalVariablesPatchableSegment, &globalConstantsPatchableSegment, {},
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors);
unresolvedExternals, device.get(), initGlobalConstantData, initGlobalVariablesData, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -2007,6 +2046,7 @@ TEST(LinkerTests, givenImplicitArgRelocationAndStackCallsThenPatchRelocationWith
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = false;
@@ -2024,7 +2064,7 @@ TEST(LinkerTests, givenImplicitArgRelocationAndStackCallsThenPatchRelocationWith
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, nullptr, kernelDescriptors);
deviceFactory.rootDevices[0], nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -2064,6 +2104,7 @@ TEST(LinkerTests, givenImplicitArgRelocationAndEnabledDebuggerThenPatchRelocatio
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = false;
@@ -2085,7 +2126,7 @@ TEST(LinkerTests, givenImplicitArgRelocationAndEnabledDebuggerThenPatchRelocatio
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
device, nullptr, nullptr, kernelDescriptors);
device, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -2122,6 +2163,7 @@ TEST(LinkerTests, givenImplicitArgRelocationWithoutStackCallsAndDisabledDebugger
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs = false;
@@ -2141,7 +2183,7 @@ TEST(LinkerTests, givenImplicitArgRelocationWithoutStackCallsAndDisabledDebugger
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
device, nullptr, nullptr, kernelDescriptors);
device, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -2166,6 +2208,7 @@ TEST(LinkerTests, givenNoImplicitArgRelocationAndStackCallsThenImplicitArgsAreNo
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::ExternalFunctionsT externalFunctions;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
@@ -2184,7 +2227,7 @@ TEST(LinkerTests, givenNoImplicitArgRelocationAndStackCallsThenImplicitArgsAreNo
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, nullptr, kernelDescriptors);
deviceFactory.rootDevices[0], nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -2211,6 +2254,7 @@ TEST(LinkerTests, givenNoImplicitArgRelocationAndEnabledDebuggerThenImplicitArgs
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::ExternalFunctionsT externalFunctions;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
@@ -2233,7 +2277,7 @@ TEST(LinkerTests, givenNoImplicitArgRelocationAndEnabledDebuggerThenImplicitArgs
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
device, nullptr, nullptr, kernelDescriptors);
device, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -2271,6 +2315,7 @@ TEST(LinkerTests, givenMultipleImplicitArgsRelocationsWithinSingleKernelWhenLink
exportedFuncSegment.gpuAddress = 4096;
exportedFuncSegment.segmentSize = 1024;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::ExternalFunctionsT externalFunctions;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
KernelDescriptor kernelDescriptor;
kernelDescriptors.push_back(&kernelDescriptor);
@@ -2289,7 +2334,7 @@ TEST(LinkerTests, givenMultipleImplicitArgsRelocationsWithinSingleKernelWhenLink
auto linkResult = linker.link(globalVarSegment, globalConstSegment, exportedFuncSegment, {},
nullptr, nullptr, patchableInstructionSegments, unresolvedExternals,
deviceFactory.rootDevices[0], nullptr, nullptr, kernelDescriptors);
deviceFactory.rootDevices[0], nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(NEO::LinkingStatus::LinkedFully, linkResult);
auto relocatedSymbols = linker.extractRelocatedSymbols();
EXPECT_EQ(0U, unresolvedExternals.size());
@@ -2300,4 +2345,22 @@ TEST(LinkerTests, givenMultipleImplicitArgsRelocationsWithinSingleKernelWhenLink
EXPECT_EQ(sizeof(ImplicitArgs), *addressToPatch);
EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.requiresImplicitArgs);
}
}
}
TEST(LinkerTests, givenDependencyOnMissingExternalFunctionWhenLinkingThenFail) {
WhiteBox<NEO::LinkerInput> linkerInput;
linkerInput.extFunDependencies.push_back({"fun0", "fun1"});
NEO::Linker linker(linkerInput);
NEO::Linker::SegmentInfo globalVar, globalConst, exportedFunc;
NEO::GraphicsAllocation *patchableGlobalVarSeg = nullptr;
NEO::GraphicsAllocation *patchableConstVarSeg = nullptr;
NEO::Linker::PatchableSegments patchableInstructionSegments;
NEO::Linker::UnresolvedExternals unresolvedExternals;
NEO::Linker::KernelDescriptorsT kernelDescriptors;
NEO::Linker::ExternalFunctionsT externalFunctions;
auto linkResult = linker.link(
globalVar, globalConst, exportedFunc, {},
patchableGlobalVarSeg, patchableConstVarSeg, patchableInstructionSegments,
unresolvedExternals, nullptr, nullptr, nullptr, kernelDescriptors, externalFunctions);
EXPECT_EQ(LinkingStatus::Error, linkResult);
}

View File

@@ -2745,6 +2745,108 @@ kernels:
EXPECT_EQ(DeviceBinaryFormat::Zebin, programInfo.kernelInfos[1]->kernelDescriptor.kernelAttributes.binaryFormat);
}
TEST(DecodeSingleDeviceBinaryZebin, GivenValidZeInfoAndExternalFunctionsMetadataThenPopulatesExternalFunctionMetadataProperly) {
std::string validZeInfo = std::string("version :\'") + toString(zeInfoDecoderVersion) + R"===('
kernels:
- name : some_kernel
execution_env :
simd_size : 8
functions:
- name: fun1
execution_env:
grf_count: 128
simd_size: 8
barrier_count: 1
)===";
ZebinTestData::ValidEmptyProgram zebin;
zebin.removeSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo);
zebin.appendSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, ArrayRef<const uint8_t>::fromAny(validZeInfo.data(), validZeInfo.size()));
zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {});
NEO::ProgramInfo programInfo;
NEO::SingleDeviceBinary singleBinary;
singleBinary.deviceBinary = zebin.storage;
std::string decodeErrors;
std::string decodeWarnings;
auto error = NEO::decodeSingleDeviceBinary<NEO::DeviceBinaryFormat::Zebin>(programInfo, singleBinary, decodeErrors, decodeWarnings);
EXPECT_EQ(NEO::DecodeError::Success, error);
EXPECT_TRUE(decodeErrors.empty()) << decodeErrors;
EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings;
ASSERT_EQ(1U, programInfo.externalFunctions.size());
auto &funInfo = programInfo.externalFunctions[0];
EXPECT_STREQ("fun1", funInfo.functionName.c_str());
EXPECT_EQ(128U, funInfo.numGrfRequired);
EXPECT_EQ(8U, funInfo.simdSize);
EXPECT_EQ(1U, funInfo.barrierCount);
}
TEST(DecodeSingleDeviceBinaryZebin, GivenValidZeInfoAndInvalidExternalFunctionsMetadataThenFail) {
std::string validZeInfo = std::string("version :\'") + toString(zeInfoDecoderVersion) + R"===('
kernels:
- name : some_kernel
execution_env :
simd_size : 8
functions:
- name: fun
execution_env:
grf_count: abc
simd_size: defgas
)===";
ZebinTestData::ValidEmptyProgram zebin;
zebin.removeSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo);
zebin.appendSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, ArrayRef<const uint8_t>::fromAny(validZeInfo.data(), validZeInfo.size()));
zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {});
NEO::ProgramInfo programInfo;
NEO::SingleDeviceBinary singleBinary;
singleBinary.deviceBinary = zebin.storage;
std::string decodeErrors;
std::string decodeWarnings;
auto error = NEO::decodeSingleDeviceBinary<NEO::DeviceBinaryFormat::Zebin>(programInfo, singleBinary, decodeErrors, decodeWarnings);
EXPECT_EQ(NEO::DecodeError::InvalidBinary, error);
const std::string expectedError = "DeviceBinaryFormat::Zebin::.ze_info : could not read grf_count from : [abc] in context of : external functions\nDeviceBinaryFormat::Zebin::.ze_info : could not read simd_size from : [defgas] in context of : external functions\n";
EXPECT_STREQ(expectedError.c_str(), decodeErrors.c_str());
EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings;
}
TEST(DecodeSingleDeviceBinaryZebin, GivenZeInfoWithTwoExternalFunctionsEntriesThenFail) {
std::string validZeInfo = std::string("version :\'") + toString(zeInfoDecoderVersion) + R"===('
kernels:
- name : some_kernel
execution_env :
simd_size : 8
functions:
- name: fun
execution_env:
grf_count: 128
simd_size: 8
functions:
- name: fun
execution_env:
grf_count: 128
simd_size: 8
)===";
ZebinTestData::ValidEmptyProgram zebin;
zebin.removeSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo);
zebin.appendSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, ArrayRef<const uint8_t>::fromAny(validZeInfo.data(), validZeInfo.size()));
zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {});
NEO::ProgramInfo programInfo;
NEO::SingleDeviceBinary singleBinary;
singleBinary.deviceBinary = zebin.storage;
std::string decodeErrors;
std::string decodeWarnings;
auto error = NEO::decodeSingleDeviceBinary<NEO::DeviceBinaryFormat::Zebin>(programInfo, singleBinary, decodeErrors, decodeWarnings);
EXPECT_EQ(NEO::DecodeError::InvalidBinary, error);
const std::string expectedError = "DeviceBinaryFormat::Zebin::.ze_info : Expected at most one functions entry in global scope of .ze_info, got : 2\n";
EXPECT_STREQ(expectedError.c_str(), decodeErrors.c_str());
EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings;
}
TEST(PopulateKernelDescriptor, GivenMinimalExecutionEnvThenPopulateKernelDescriptorWithDefaults) {
std::string zeinfo = R"===(
kernels:
@@ -5241,4 +5343,125 @@ TEST(PopulateGlobalDeviceHostNameMapping, givenZebinWithGlobalHostAccessTableSec
std::string expectedWarning("DeviceBinaryFormat::Zebin::.ze_info : Unknown entry \"banana_type\" for payload argument in context of global_host_access_table\n");
EXPECT_STREQ(expectedWarning.c_str(), warnings.c_str());
}
}
TEST(PopulateZeInfoExternalFunctionsMetadata, GivenValidExternalFunctionsMetadataThenParsesItProperly) {
{
NEO::ConstStringRef yaml = R"===(---
functions:
- name: fun0
execution_env:
grf_count: 128
simd_size: 8
barrier_count: 1
- name: fun1
execution_env:
grf_count: 128
simd_size: 8
...
)===";
std::string parserErrors;
std::string parserWarnings;
Yaml::YamlParser parser;
bool success = parser.parse(yaml, parserErrors, parserWarnings);
EXPECT_TRUE(parserErrors.empty()) << parserErrors;
EXPECT_TRUE(parserWarnings.empty()) << parserWarnings;
ASSERT_TRUE(success);
auto &functionsNode = *parser.findNodeWithKeyDfs(Elf::ZebinKernelMetadata::Tags::functions);
std::string errors;
std::string warnings;
ProgramInfo programInfo;
for (const auto &functionNd : parser.createChildrenRange(functionsNode)) {
auto err = populateExternalFunctionsMetadata(programInfo, parser, functionNd, errors, warnings);
EXPECT_EQ(DecodeError::Success, err);
EXPECT_TRUE(errors.empty()) << errors;
EXPECT_TRUE(warnings.empty()) << warnings;
}
ASSERT_EQ(2U, programInfo.externalFunctions.size());
auto &fun0Info = programInfo.externalFunctions[0];
EXPECT_STREQ("fun0", fun0Info.functionName.c_str());
EXPECT_EQ(128U, fun0Info.numGrfRequired);
EXPECT_EQ(8U, fun0Info.simdSize);
EXPECT_EQ(1U, fun0Info.barrierCount);
auto &fun1Info = programInfo.externalFunctions[1];
EXPECT_STREQ("fun1", fun1Info.functionName.c_str());
EXPECT_EQ(128U, fun1Info.numGrfRequired);
EXPECT_EQ(8U, fun1Info.simdSize);
EXPECT_EQ(0U, fun1Info.barrierCount);
}
}
TEST(PopulateZeInfoExternalFunctionsMetadata, GivenValidExternalFunctionsMetadataWithUnknownEntriesThenParsesItProperly) {
NEO::ConstStringRef yaml = R"===(---
functions:
- name: fun0
execution_env:
grf_count: 128
simd_size: 8
barrier_count: 1
unknown: 12345
...
)===";
std::string parserErrors;
std::string parserWarnings;
Yaml::YamlParser parser;
bool success = parser.parse(yaml, parserErrors, parserWarnings);
EXPECT_TRUE(parserErrors.empty()) << parserErrors;
EXPECT_TRUE(parserWarnings.empty()) << parserWarnings;
ASSERT_TRUE(success);
auto &functionsNode = *parser.findNodeWithKeyDfs(Elf::ZebinKernelMetadata::Tags::functions);
auto &functionNode = *parser.createChildrenRange(functionsNode).begin();
std::string errors;
std::string warnings;
ProgramInfo programInfo;
auto err = populateExternalFunctionsMetadata(programInfo, parser, functionNode, errors, warnings);
EXPECT_EQ(DecodeError::Success, err);
EXPECT_TRUE(errors.empty()) << errors;
const auto expectedWarning = "DeviceBinaryFormat::Zebin::.ze_info : Unknown entry \"unknown\" in context of : external functions\n";
EXPECT_STREQ(expectedWarning, warnings.c_str());
ASSERT_EQ(1U, programInfo.externalFunctions.size());
auto &fun0Info = programInfo.externalFunctions[0];
EXPECT_STREQ("fun0", fun0Info.functionName.c_str());
EXPECT_EQ(128U, fun0Info.numGrfRequired);
EXPECT_EQ(8U, fun0Info.simdSize);
EXPECT_EQ(1U, fun0Info.barrierCount);
}
TEST(PopulateZeInfoExternalFunctionsMetadata, GivenInvalidExternalFunctionsMetadataThenFail) {
NEO::ConstStringRef yaml = R"===(---
functions:
- name: fun0
execution_env:
grf_count: abc
simd_size: def
barrier_count: ghi
...
)===";
std::string parserErrors;
std::string parserWarnings;
Yaml::YamlParser parser;
bool success = parser.parse(yaml, parserErrors, parserWarnings);
EXPECT_TRUE(parserErrors.empty()) << parserErrors;
EXPECT_TRUE(parserWarnings.empty()) << parserWarnings;
ASSERT_TRUE(success);
auto &functionsNode = *parser.findNodeWithKeyDfs(Elf::ZebinKernelMetadata::Tags::functions);
auto &functionNode = *parser.createChildrenRange(functionsNode).begin();
std::string errors;
std::string warnings;
ProgramInfo programInfo;
auto err = populateExternalFunctionsMetadata(programInfo, parser, functionNode, errors, warnings);
EXPECT_EQ(DecodeError::InvalidBinary, err);
EXPECT_EQ(0U, programInfo.externalFunctions.size());
const auto expectedError = "DeviceBinaryFormat::Zebin::.ze_info : could not read grf_count from : [abc] in context of : external functions\nDeviceBinaryFormat::Zebin::.ze_info : could not read simd_size from : [def] in context of : external functions\nDeviceBinaryFormat::Zebin::.ze_info : could not read barrier_count from : [ghi] in context of : external functions\n";
EXPECT_STREQ(expectedError, errors.c_str());
EXPECT_TRUE(warnings.empty()) << warnings;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 Intel Corporation
* Copyright (C) 2020-2022 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -11,9 +11,12 @@
#include "shared/source/device_binary_format/elf/elf_encoder.h"
#include "shared/source/device_binary_format/elf/zebin_elf.h"
#include "shared/source/device_binary_format/zebin_decoder.h"
#include "shared/source/utilities/const_stringref.h"
#include "shared/test/common/mocks/mock_elf.h"
#include "igfxfmid.h"
#include <string>
#include <vector>
extern PRODUCT_FAMILY productFamily;
@@ -115,4 +118,98 @@ struct ValidEmptyProgram {
std::vector<uint8_t> storage;
};
struct ZebinWithExternalFunctionsInfo {
ZebinWithExternalFunctionsInfo() {
std::string zeInfo = std::string("version :\'") + toString(NEO::zeInfoDecoderVersion) + R"===('
kernels:
- name: kernel
execution_env:
simd_size: 8
- name: Intel_Symbol_Table_Void_Program
execution_env:
simd_size: 8
functions:
- name: fun0
execution_env:
grf_count: 128
simd_size: 8
barrier_count: 0
- name: fun1
execution_env:
grf_count: 128
simd_size: 8
barrier_count: 2
)===";
MockElfEncoder<> elfEncoder;
auto &elfHeader = elfEncoder.getElfFileHeader();
elfHeader.type = NEO::Elf::ET_ZEBIN_EXE;
elfHeader.flags = 0U;
const uint8_t kData[32] = {0U};
elfEncoder.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "kernel", kData);
auto kernelSectionIdx = elfEncoder.getLastSectionHeaderIndex();
const uint8_t funData[32] = {0U};
elfEncoder.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + NEO::Elf::SectionsNamesZebin::externalFunctions.str(), funData);
auto externalFunctionsIdx = elfEncoder.getLastSectionHeaderIndex();
elfEncoder.appendSection(NEO::Elf::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, zeInfo);
NEO::Elf::ElfSymbolEntry<NEO::Elf::EI_CLASS_64> symbols[2];
symbols[0].name = decltype(symbols[0].name)(elfEncoder.appendSectionName(fun0Name));
symbols[0].info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_FUNC | NEO::Elf::SYMBOL_TABLE_BIND::STB_GLOBAL << 4;
symbols[0].shndx = decltype(symbols[0].shndx)(externalFunctionsIdx);
symbols[0].size = 16;
symbols[0].value = 0;
symbols[1].name = decltype(symbols[1].name)(elfEncoder.appendSectionName(fun1Name));
symbols[1].info = NEO::Elf::SYMBOL_TABLE_TYPE::STT_FUNC | NEO::Elf::SYMBOL_TABLE_BIND::STB_GLOBAL << 4;
symbols[1].shndx = decltype(symbols[1].shndx)(externalFunctionsIdx);
symbols[1].size = 16;
symbols[1].value = 16;
elfEncoder.appendSection(NEO::Elf::SHT_SYMTAB, NEO::Elf::SectionsNamesZebin::symtab,
ArrayRef<const uint8_t>(reinterpret_cast<const uint8_t *>(symbols), sizeof(symbols)));
NEO::Elf::ElfRel<NEO::Elf::EI_CLASS_64> extFuncSegReloc = {}; // fun0 calls fun1
extFuncSegReloc.offset = 0x8;
extFuncSegReloc.info = (uint64_t(1) << 32) | NEO::Elf::RELOC_TYPE_ZEBIN::R_ZE_SYM_ADDR;
auto &extFuncRelSection = elfEncoder.appendSection(NEO::Elf::SHT_REL, NEO::Elf::SpecialSectionNames::relPrefix.str() + NEO::Elf::SectionsNamesZebin::textPrefix.str() + NEO::Elf::SectionsNamesZebin::externalFunctions.str(),
{reinterpret_cast<uint8_t *>(&extFuncSegReloc), sizeof(extFuncSegReloc)});
extFuncRelSection.info = externalFunctionsIdx;
NEO::Elf::ElfRel<NEO::Elf::EI_CLASS_64>
kernelSegReloc = {}; // kernel calls fun0
kernelSegReloc.offset = 0x8;
kernelSegReloc.info = (uint64_t(0) << 32) | NEO::Elf::RELOC_TYPE_ZEBIN::R_ZE_SYM_ADDR;
auto &kernelRelSection = elfEncoder.appendSection(NEO::Elf::SHT_REL, NEO::Elf::SpecialSectionNames::relPrefix.str() + NEO::Elf::SectionsNamesZebin::textPrefix.str() + "kernel",
{reinterpret_cast<uint8_t *>(&kernelSegReloc), sizeof(kernelSegReloc)});
kernelRelSection.info = kernelSectionIdx;
storage = elfEncoder.encode();
recalcPtr();
nameToSegId["kernel"] = 0;
nameToSegId["Intel_Symbol_Table_Void_Program"] = 1;
}
void recalcPtr() {
elfHeader = reinterpret_cast<NEO::Elf::ElfFileHeader<NEO::Elf::EI_CLASS_64> *>(storage.data());
}
void setProductFamily(uint16_t productFamily) {
elfHeader->machine = productFamily;
}
NEO::Elf::Elf<NEO::Elf::EI_CLASS_64> getElf() {
std::string errors, warnings;
auto elf = NEO::Elf::decodeElf({storage.data(), storage.size()}, errors, warnings);
return elf;
}
std::unordered_map<std::string, uint32_t> nameToSegId;
NEO::Elf::ElfFileHeader<NEO::Elf::EI_CLASS_64> *elfHeader;
NEO::ConstStringRef fun0Name = "fun0";
NEO::ConstStringRef fun1Name = "fun1";
const uint8_t barrierCount = 2;
std::vector<uint8_t> storage;
};
} // namespace ZebinTestData

View File

@@ -1,5 +1,5 @@
#
# Copyright (C) 2018-2021 Intel Corporation
# Copyright (C) 2018-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
#