diff --git a/opencl/source/program/program.cpp b/opencl/source/program/program.cpp index f37bcbaa17..23a815f654 100644 --- a/opencl/source/program/program.cpp +++ b/opencl/source/program/program.cpp @@ -441,6 +441,10 @@ void Program::replaceDeviceBinary(std::unique_ptr newBinary, size_t newB this->packedDeviceBinarySize = newBinarySize; this->unpackedDeviceBinary.reset(); this->unpackedDeviceBinarySize = 0U; + if (isAnySingleDeviceBinaryFormat(ArrayRef(reinterpret_cast(this->packedDeviceBinary.get()), this->packedDeviceBinarySize))) { + this->unpackedDeviceBinary = makeCopy(packedDeviceBinary.get(), packedDeviceBinarySize); + this->unpackedDeviceBinarySize = packedDeviceBinarySize; + } } else { this->packedDeviceBinary.reset(); this->packedDeviceBinarySize = 0U; diff --git a/opencl/test/unit_test/program/program_tests.cpp b/opencl/test/unit_test/program/program_tests.cpp index 7ca25bfd69..72c399eda7 100644 --- a/opencl/test/unit_test/program/program_tests.cpp +++ b/opencl/test/unit_test/program/program_tests.cpp @@ -23,6 +23,7 @@ #include "shared/source/memory_manager/surface.h" #include "shared/source/os_interface/os_context.h" #include "shared/test/unit_test/device_binary_format/patchtokens_tests.h" +#include "shared/test/unit_test/device_binary_format/zebin_tests.h" #include "shared/test/unit_test/helpers/debug_manager_state_restore.h" #include "shared/test/unit_test/mocks/mock_compiler_interface.h" #include "shared/test/unit_test/utilities/base_object_utils.h" @@ -3044,3 +3045,17 @@ TEST_F(ProgramBinTest, GivenSourceKernelWhenLinkingProgramThenGtpinInitInfoIsPas EXPECT_EQ(pIgcInitPtr, mockCompilerInterface->gtpinInfoPassed); mockCompilerInterface.release(); } + +TEST(ProgramReplaceDeviceBinary, GivenBinaryZebinThenUseAsBothPackedAndUnpackedBinaryContainer) { + MockExecutionEnvironment execEnv; + ZebinTestData::ValidEmptyProgram zebin; + std::unique_ptr src = makeCopy(zebin.storage.data(), zebin.storage.size()); + MockProgram program{execEnv}; + program.replaceDeviceBinary(std::move(src), zebin.storage.size()); + ASSERT_EQ(zebin.storage.size(), program.packedDeviceBinarySize); + ASSERT_EQ(zebin.storage.size(), program.unpackedDeviceBinarySize); + ASSERT_NE(nullptr, program.packedDeviceBinary); + ASSERT_NE(nullptr, program.unpackedDeviceBinary); + EXPECT_EQ(0, memcmp(program.packedDeviceBinary.get(), zebin.storage.data(), program.packedDeviceBinarySize)); + EXPECT_EQ(0, memcmp(program.unpackedDeviceBinary.get(), zebin.storage.data(), program.unpackedDeviceBinarySize)); +} \ No newline at end of file diff --git a/opencl/test/unit_test/test_files/igdrcl.config b/opencl/test/unit_test/test_files/igdrcl.config index 20570c4e10..e730b16197 100644 --- a/opencl/test/unit_test/test_files/igdrcl.config +++ b/opencl/test/unit_test/test_files/igdrcl.config @@ -177,4 +177,6 @@ ForceCacheFlushForBcs = -1 ForceGpgpuSubmissionForBcsEnqueue = -1 ForceSemaphoreDelayBetweenWaits = -1 ForceLocalMemoryAccessMode = -1 -UseLegacyLevelZeroAffinity = 1 \ No newline at end of file +UseLegacyLevelZeroAffinity = 1 +ZebinAppendElws = 0 +ZebinIgnoreIcbeVersion = 0 diff --git a/shared/source/compiler_interface/compiler_interface.inl b/shared/source/compiler_interface/compiler_interface.inl index ad92efdc52..39e905c06b 100644 --- a/shared/source/compiler_interface/compiler_interface.inl +++ b/shared/source/compiler_interface/compiler_interface.inl @@ -6,6 +6,7 @@ */ #pragma once +#include "shared/source/debug_settings/debug_settings_manager.h" #include "shared/source/os_interface/os_library.h" #include "opencl/source/helpers/validators.h" @@ -105,7 +106,11 @@ inline bool loadCompiler(const char *libName, std::unique_ptr &outLib return false; } - if (false == main->IsCompatible()) { + std::vector interfacesToIgnore; + if (DebugManager.flags.ZebinIgnoreIcbeVersion.get()) { + interfacesToIgnore.push_back(IGC::OclGenBinaryBase::GetInterfaceId()); + } + if (false == main->IsCompatible(&interfacesToIgnore)) { DEBUG_BREAK_IF(true); // given compiler library is not compatible return false; } diff --git a/shared/source/debug_settings/debug_variables_base.inl b/shared/source/debug_settings/debug_variables_base.inl index 261522daa9..9bcbf7185c 100644 --- a/shared/source/debug_settings/debug_variables_base.inl +++ b/shared/source/debug_settings/debug_variables_base.inl @@ -72,6 +72,8 @@ DECLARE_DEBUG_VARIABLE(bool, OverrideInvalidEngineWithDefault, false, "When set DECLARE_DEBUG_VARIABLE(bool, ForceImplicitFlush, false, "Flush after each enqueue. Useful for debugging batched submission logic. ") DECLARE_DEBUG_VARIABLE(bool, ForcePipeControlPriorToWalker, false, "Allows to force pipe contron prior to walker.") DECLARE_DEBUG_VARIABLE(bool, UseLegacyLevelZeroAffinity, true, "Use Level Zero affinity mask as bit set, as defined in v0.91 specification") +DECLARE_DEBUG_VARIABLE(bool, ZebinAppendElws, false, "Append crossthread data with enqueue local work size") +DECLARE_DEBUG_VARIABLE(bool, ZebinIgnoreIcbeVersion, false, "Ignore IGC\'s ICBE version") /*LOGGING FLAGS*/ DECLARE_DEBUG_VARIABLE(bool, PrintDeviceAndEngineIdOnSubmission, false, "print submissions device and engine IDs to standard output") diff --git a/shared/source/device_binary_format/CMakeLists.txt b/shared/source/device_binary_format/CMakeLists.txt index 185ad9bbc9..12be6ad95a 100644 --- a/shared/source/device_binary_format/CMakeLists.txt +++ b/shared/source/device_binary_format/CMakeLists.txt @@ -14,6 +14,7 @@ set(NEO_DEVICE_BINARY_FORMAT ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_format_ar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_format_ocl_elf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_format_patchtokens.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_format_zebin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_formats.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_formats.h ${CMAKE_CURRENT_SOURCE_DIR}/elf/elf.h @@ -22,12 +23,15 @@ set(NEO_DEVICE_BINARY_FORMAT ${CMAKE_CURRENT_SOURCE_DIR}/elf/elf_encoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/elf/elf_encoder.h ${CMAKE_CURRENT_SOURCE_DIR}/elf/ocl_elf.h + ${CMAKE_CURRENT_SOURCE_DIR}/elf/zebin_elf.h ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_decoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_decoder.h ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_dumper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_dumper.h ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_validator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_validator.h + ${CMAKE_CURRENT_SOURCE_DIR}/zebin_decoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zebin_decoder.h ${CMAKE_CURRENT_SOURCE_DIR}/yaml/yaml_parser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/yaml/yaml_parser.h ) diff --git a/shared/source/device_binary_format/device_binary_format_zebin.cpp b/shared/source/device_binary_format/device_binary_format_zebin.cpp new file mode 100644 index 0000000000..55295b0564 --- /dev/null +++ b/shared/source/device_binary_format/device_binary_format_zebin.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/source/compiler_interface/intermediate_representations.h" +#include "shared/source/device_binary_format/device_binary_formats.h" +#include "shared/source/device_binary_format/elf/elf_decoder.h" +#include "shared/source/device_binary_format/elf/elf_encoder.h" +#include "shared/source/device_binary_format/elf/zebin_elf.h" +#include "shared/source/program/program_info.h" + +#include + +namespace NEO { + +template <> +bool isDeviceBinaryFormat(const ArrayRef binary) { + auto header = Elf::decodeElfFileHeader(binary); + if (nullptr == header) { + return false; + } + + return header->type == NEO::Elf::ET_ZEBIN_EXE; +} + +template <> +SingleDeviceBinary unpackSingleDeviceBinary(const ArrayRef archive, const ConstStringRef requestedProductAbbreviation, const TargetDevice &requestedTargetDevice, + std::string &outErrReason, std::string &outWarning) { + auto elf = Elf::decodeElf(archive, outErrReason, outWarning); + if (nullptr == elf.elfFileHeader) { + return {}; + } + + switch (elf.elfFileHeader->type) { + default: + outErrReason = "Unhandled elf type"; + return {}; + case NEO::Elf::ET_ZEBIN_EXE: + break; + } + + const auto &flags = reinterpret_cast(elf.elfFileHeader->flags); + bool validForTarget = flags.machineEntryUsesGfxCoreInsteadOfProductFamily + ? (requestedTargetDevice.coreFamily == static_cast(elf.elfFileHeader->machine)) + : (requestedTargetDevice.productFamily == static_cast(elf.elfFileHeader->machine)); + validForTarget &= (requestedTargetDevice.maxPointerSizeInBytes == 8U); + validForTarget &= (0 == flags.validateRevisionId) | ((requestedTargetDevice.stepping >= flags.minHwRevisionId) & (requestedTargetDevice.stepping <= flags.maxHwRevisionId)); + if (false == validForTarget) { + outErrReason = "Unhandled target device"; + return {}; + } + + SingleDeviceBinary ret; + ret.deviceBinary = archive; + ret.format = NEO::DeviceBinaryFormat::Zebin; + ret.targetDevice = requestedTargetDevice; + + return ret; +} + +} // namespace NEO diff --git a/shared/source/device_binary_format/device_binary_formats.cpp b/shared/source/device_binary_format/device_binary_formats.cpp index 7b2c318f3c..e7ba7a509d 100644 --- a/shared/source/device_binary_format/device_binary_formats.cpp +++ b/shared/source/device_binary_format/device_binary_formats.cpp @@ -10,6 +10,9 @@ namespace NEO { std::vector packDeviceBinary(const SingleDeviceBinary binary, std::string &outErrReason, std::string &outWarning) { + if (NEO::isAnyPackedDeviceBinaryFormat(binary.deviceBinary)) { + return std::vector(binary.deviceBinary.begin(), binary.deviceBinary.end()); + } return packDeviceBinary(binary, outErrReason, outWarning); } diff --git a/shared/source/device_binary_format/device_binary_formats.h b/shared/source/device_binary_format/device_binary_formats.h index e624d529f6..b2534b8cef 100644 --- a/shared/source/device_binary_format/device_binary_formats.h +++ b/shared/source/device_binary_format/device_binary_formats.h @@ -25,7 +25,8 @@ enum class DeviceBinaryFormat : uint8_t { OclLibrary, OclCompiledObject, Patchtokens, - Archive + Archive, + Zebin }; enum class DecodeError : uint8_t { @@ -54,6 +55,7 @@ inline const char *asString(DecodeError err) { struct TargetDevice { GFXCORE_FAMILY coreFamily = IGFX_UNKNOWN_CORE; + PRODUCT_FAMILY productFamily = IGFX_UNKNOWN; uint32_t stepping = 0U; uint32_t maxPointerSizeInBytes = 4U; }; @@ -76,6 +78,8 @@ template <> bool isDeviceBinaryFormat(const ArrayRef); template <> bool isDeviceBinaryFormat(const ArrayRef); +template <> +bool isDeviceBinaryFormat(const ArrayRef); inline bool isAnyDeviceBinaryFormat(const ArrayRef binary) { if (isDeviceBinaryFormat(binary)) { @@ -87,6 +91,9 @@ inline bool isAnyDeviceBinaryFormat(const ArrayRef binary) { if (isDeviceBinaryFormat(binary)) { return true; } + if (isDeviceBinaryFormat(binary)) { + return true; + } return false; } @@ -100,6 +107,8 @@ template <> SingleDeviceBinary unpackSingleDeviceBinary(const ArrayRef, const ConstStringRef, const TargetDevice &, std::string &, std::string &); template <> SingleDeviceBinary unpackSingleDeviceBinary(const ArrayRef, const ConstStringRef, const TargetDevice &, std::string &, std::string &); +template <> +SingleDeviceBinary unpackSingleDeviceBinary(const ArrayRef, const ConstStringRef, const TargetDevice &, std::string &, std::string &); inline SingleDeviceBinary unpackSingleDeviceBinary(const ArrayRef archive, const ConstStringRef requestedProductAbbreviation, const TargetDevice &requestedTargetDevice, std::string &outErrReason, std::string &outWarning) { @@ -111,6 +120,8 @@ inline SingleDeviceBinary unpackSingleDeviceBinary(const ArrayRef return unpackSingleDeviceBinary(archive, requestedProductAbbreviation, requestedTargetDevice, outErrReason, outWarning); } else if (isDeviceBinaryFormat(archive)) { return unpackSingleDeviceBinary(archive, requestedProductAbbreviation, requestedTargetDevice, outErrReason, outWarning); + } else if (isDeviceBinaryFormat(archive)) { + return unpackSingleDeviceBinary(archive, requestedProductAbbreviation, requestedTargetDevice, outErrReason, outWarning); } else { outErrReason = "Unknown format"; } @@ -132,11 +143,14 @@ inline bool isAnyPackedDeviceBinaryFormat(const ArrayRef binary) if (isDeviceBinaryFormat(binary)) { return true; } + if (isDeviceBinaryFormat(binary)) { + return true; + } return false; } inline bool isAnySingleDeviceBinaryFormat(const ArrayRef binary) { - return (false == isAnyPackedDeviceBinaryFormat(binary)) && isAnyDeviceBinaryFormat(binary); + return ((false == isAnyPackedDeviceBinaryFormat(binary)) && isAnyDeviceBinaryFormat(binary)) || isDeviceBinaryFormat(binary); } template @@ -148,6 +162,8 @@ template <> DecodeError decodeSingleDeviceBinary(ProgramInfo &, const SingleDeviceBinary &, std::string &, std::string &); template <> DecodeError decodeSingleDeviceBinary(ProgramInfo &, const SingleDeviceBinary &, std::string &, std::string &); +template <> +DecodeError decodeSingleDeviceBinary(ProgramInfo &, const SingleDeviceBinary &, std::string &, std::string &); inline std::pair decodeSingleDeviceBinary(ProgramInfo &dst, const SingleDeviceBinary &src, std::string &outErrReason, std::string &outWarning) { std::pair ret; @@ -162,6 +178,9 @@ inline std::pair decodeSingleDeviceBinary(Progr } else if (isDeviceBinaryFormat(src.deviceBinary)) { ret.second = DeviceBinaryFormat::Archive; ret.first = decodeSingleDeviceBinary(dst, src, outErrReason, outWarning); + } else if (isDeviceBinaryFormat(src.deviceBinary)) { + ret.second = DeviceBinaryFormat::Zebin; + ret.first = decodeSingleDeviceBinary(dst, src, outErrReason, outWarning); } else { outErrReason = "Unknown format"; } diff --git a/shared/source/device_binary_format/elf/elf.h b/shared/source/device_binary_format/elf/elf.h index 333b29810b..e0feb82de6 100644 --- a/shared/source/device_binary_format/elf/elf.h +++ b/shared/source/device_binary_format/elf/elf.h @@ -303,6 +303,55 @@ struct ElfFileHeader { static_assert(sizeof(ElfFileHeader) == 0x34, ""); static_assert(sizeof(ElfFileHeader) == 0x40, ""); +template +struct ElfSymbolEntryTypes; + +template <> +struct ElfSymbolEntryTypes { + using Name = uint32_t; + using Info = uint8_t; + using Other = uint8_t; + using Shndx = uint16_t; + using Value = uint32_t; + using Size = uint32_t; +}; + +template <> +struct ElfSymbolEntryTypes { + using Name = uint32_t; + using Info = uint8_t; + using Other = uint8_t; + using Shndx = uint16_t; + using Value = uint64_t; + using Size = uint64_t; +}; + +template +struct ElfSymbolEntry; + +template <> +struct ElfSymbolEntry { + ElfSymbolEntryTypes::Name name; + ElfSymbolEntryTypes::Value value; + ElfSymbolEntryTypes::Size size; + ElfSymbolEntryTypes::Info info; + ElfSymbolEntryTypes::Other other; + ElfSymbolEntryTypes::Shndx shndx; +}; + +template <> +struct ElfSymbolEntry { + ElfSymbolEntryTypes::Name name; + ElfSymbolEntryTypes::Info info; + ElfSymbolEntryTypes::Other other; + ElfSymbolEntryTypes::Shndx shndx; + ElfSymbolEntryTypes::Value value; + ElfSymbolEntryTypes::Size size; +}; + +static_assert(sizeof(ElfSymbolEntry) == 0x10, ""); +static_assert(sizeof(ElfSymbolEntry) == 0x18, ""); + namespace SpecialSectionNames { static constexpr ConstStringRef bss = ".bss"; // uninitialized memory static constexpr ConstStringRef comment = ".comment"; // version control information diff --git a/shared/source/device_binary_format/elf/zebin_elf.h b/shared/source/device_binary_format/elf/zebin_elf.h new file mode 100644 index 0000000000..897fd05c9b --- /dev/null +++ b/shared/source/device_binary_format/elf/zebin_elf.h @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2019-2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +// Abstract: Defines the types used for ELF headers/sections. +#pragma once + +#include "shared/source/device_binary_format/elf/elf.h" +#include "shared/source/utilities/const_stringref.h" + +#include +#include + +namespace NEO { + +namespace Elf { + +enum ELF_TYPE_ZEBIN : uint16_t { + ET_ZEBIN_REL = 0xff11, // A relocatable ZE binary file + ET_ZEBIN_EXE = 0xff12, // An executable ZE binary file + ET_ZEBIN_DYN = 0xff13, // A shared object ZE binary file +}; + +enum SHT_ZEBIN : uint32_t { + SHT_ZEBIN_SPIRV = 0xff000009, // .spv.kernel section, value the same as SHT_OPENCL_SPIRV + SHT_ZEBIN_ZEINFO = 0xff000011 // .ze_info section +}; + +namespace SectionsNamesZebin { +static constexpr ConstStringRef textPrefix = ".text."; +static constexpr ConstStringRef dataConst = ".data.const"; +static constexpr ConstStringRef dataGlobal = ".data.global"; +static constexpr ConstStringRef symtab = ".symtab"; +static constexpr ConstStringRef relTablePrefix = ".rel."; +static constexpr ConstStringRef spv = ".spv"; +static constexpr ConstStringRef debugInfo = ".debug_info"; +static constexpr ConstStringRef zeInfo = ".ze_info"; +} // namespace SectionsNamesZebin + +struct ZebinTargetFlags { + union { + struct { + // bit[7:0]: dedicated for specific generator (meaning based on generatorId) + uint8_t generatorSpecificFlags : 8; + + // bit[12:8]: values [0-31], min compatbile device revision Id (stepping) + uint8_t minHwRevisionId : 5; + + // bit[13:13]: + // 0 - full validation during decoding (safer decoding) + // 1 - no validation (faster decoding - recommended for known generators) + bool validateRevisionId : 1; + + // bit[14:14]: + // 0 - ignore minHwRevisionId and maxHwRevisionId + // 1 - underlying device must match specified revisionId info + bool disableExtendedValidation : 1; + + // bit[15:15]: + // 0 - elfFileHeader::machine is PRODUCT_FAMILY + // 1 - elfFileHeader::machine is GFXCORE_FAMILY + bool machineEntryUsesGfxCoreInsteadOfProductFamily : 1; + + // bit[20:16]: max compatbile device revision Id (stepping) + uint8_t maxHwRevisionId : 5; + + // bit[23:21]: generator of this device binary + // 0 - Unregistered + // 1 - IGC + uint8_t generatorId : 3; + + // bit[31:24]: MBZ, reserved for future use + uint8_t reserved : 8; + }; + uint32_t packed = 0U; + }; +}; +static_assert(sizeof(ZebinTargetFlags) == sizeof(uint32_t), ""); + +namespace ZebinKernelMetadata { +namespace Tags { +static constexpr ConstStringRef kernels("kernels"); +namespace Kernel { +static constexpr ConstStringRef name("name"); +static constexpr ConstStringRef executionEnv("execution_env"); +static constexpr ConstStringRef payloadArguments("payload_arguments"); +static constexpr ConstStringRef bindingTableIndices("binding_table_indices"); +static constexpr ConstStringRef perThreadPayloadArguments("per_thread_payload_arguments"); + +namespace ExecutionEnv { +static constexpr ConstStringRef actualKernelStartOffset("actual_kernel_start_offset"); +static constexpr ConstStringRef barrierCount("barrier_count"); +static constexpr ConstStringRef disableMidThreadPreemption("disable_mid_thread_preemption"); +static constexpr ConstStringRef grfCount("grf_count"); +static constexpr ConstStringRef has4gbBuffers("has_4gb_buffers"); +static constexpr ConstStringRef hasDeviceEnqueue("has_device_enqueue"); +static constexpr ConstStringRef hasFenceForImageAccess("has_fence_for_image_access"); +static constexpr ConstStringRef hasGlobalAtomics("has_global_atomics"); +static constexpr ConstStringRef hasMultiScratchSpaces("has_multi_scratch_spaces"); +static constexpr ConstStringRef hasNoStatelessWrite("has_no_stateless_write"); +static constexpr ConstStringRef hwPreemptionMode("hw_preemption_mode"); +static constexpr ConstStringRef offsetToSkipPerThreadDataLoad("offset_to_skip_per_thread_data_load"); +static constexpr ConstStringRef offsetToSkipSetFfidGp("offset_to_skip_set_ffid_gp"); +static constexpr ConstStringRef requiredSubGroupSize("required_sub_group_size"); +static constexpr ConstStringRef requiredWorkGroupSize("required_work_group_size"); +static constexpr ConstStringRef simdSize("simd_size"); +static constexpr ConstStringRef slmSize("slm_size"); +static constexpr ConstStringRef subgroupIndependentForwardProgress("subgroup_independent_forward_progress"); +static constexpr ConstStringRef workGroupWalkOrderDimensions("work_group_walk_order_dimensions"); +} // namespace ExecutionEnv + +namespace PayloadArgument { +static constexpr ConstStringRef argType("arg_type"); +static constexpr ConstStringRef argIndex("arg_index"); +static constexpr ConstStringRef offset("offset"); +static constexpr ConstStringRef size("size"); +static constexpr ConstStringRef addrmode("addrmode"); +static constexpr ConstStringRef addrspace("addrspace"); +static constexpr ConstStringRef accessType("access_type"); +namespace ArgType { +static constexpr ConstStringRef localSize("local_size"); +static constexpr ConstStringRef groupSize("group_size"); +static constexpr ConstStringRef globalIdOffset("global_id_offset"); +static constexpr ConstStringRef privateBaseStateless("private_base_stateless"); +static constexpr ConstStringRef argByvalue("arg_byvalue"); +static constexpr ConstStringRef argBypointer("arg_bypointer"); +} // namespace ArgType +namespace MemoryAddressingMode { +static constexpr ConstStringRef stateless("stateless"); +static constexpr ConstStringRef stateful("stateful"); +static constexpr ConstStringRef bindless("bindless"); +static constexpr ConstStringRef sharedLocalMemory("shared_local_memory"); +} // namespace MemoryAddressingMode +namespace AddrSpace { +static constexpr ConstStringRef global("global"); +static constexpr ConstStringRef local("local"); +static constexpr ConstStringRef constant("constant"); +static constexpr ConstStringRef image("image"); +static constexpr ConstStringRef sampler("sampler"); +} // namespace AddrSpace +namespace AccessType { +static constexpr ConstStringRef readonly("readonly"); +static constexpr ConstStringRef writeonly("writeonly"); +static constexpr ConstStringRef readwrite("readwrite"); +} // namespace AccessType +} // namespace PayloadArgument + +namespace BindingTableIndex { +static constexpr ConstStringRef btiValue("bti_value"); +static constexpr ConstStringRef argIndex("arg_index"); +} // namespace BindingTableIndex + +namespace PerThreadPayloadArgument { +static constexpr ConstStringRef argType("arg_type"); +static constexpr ConstStringRef offset("offset"); +static constexpr ConstStringRef size("size"); +namespace ArgType { +static constexpr ConstStringRef packedLocalIds("packed_local_ids"); +static constexpr ConstStringRef localId("local_id"); +} // namespace ArgType +} // namespace PerThreadPayloadArgument +} // namespace Kernel +} // namespace Tags + +namespace Types { + +namespace Kernel { +namespace ExecutionEnv { +using ActualKernelStartOffsetT = int32_t; +using BarrierCountT = int32_t; +using DisableMidThreadPreemptionT = bool; +using GrfCountT = int32_t; +using Has4GBBuffersT = bool; +using HasDeviceEnqueueT = bool; +using HasFenceForImageAccessT = bool; +using HasGlobalAtomicsT = bool; +using HasMultiScratchSpacesT = bool; +using HasNoStatelessWriteT = bool; +using HwPreemptionModeT = int32_t; +using OffsetToSkipPerThreadDataLoadT = int32_t; +using OffsetToSkipSetFfidGpT = int32_t; +using RequiredSubGroupSizeT = int32_t; +using RequiredWorkGroupSizeT = int32_t[3]; +using SimdSizeT = int32_t; +using SlmSizeT = int32_t; +using SubgroupIndependentForwardProgressT = bool; +using WorkgroupWalkOrderDimensionsT = int32_t[3]; + +namespace Defaults { +static constexpr BarrierCountT barrierCount = 0; +static constexpr DisableMidThreadPreemptionT disableMidThreadPreemption = false; +static constexpr Has4GBBuffersT has4GBBuffers = false; +static constexpr HasDeviceEnqueueT hasDeviceEnqueue = false; +static constexpr HasFenceForImageAccessT hasFenceForImageAccess = false; +static constexpr HasGlobalAtomicsT hasGlobalAtomics = false; +static constexpr HasMultiScratchSpacesT hasMultiScratchSpaces = false; +static constexpr HasNoStatelessWriteT hasNoStatelessWrite = false; +static constexpr HwPreemptionModeT hwPreemptionMode = -1; +static constexpr OffsetToSkipPerThreadDataLoadT offsetToSkipPerThreadDataLoad = 0; +static constexpr OffsetToSkipSetFfidGpT offsetToSkipSetFfidGp = 0; +static constexpr RequiredSubGroupSizeT requiredSubGroupSize = 0; +static constexpr RequiredWorkGroupSizeT requiredWorkGroupSize = {0, 0, 0}; +static constexpr SlmSizeT slmSize = 0; +static constexpr SubgroupIndependentForwardProgressT subgroupIndependentForwardProgress = false; +static constexpr WorkgroupWalkOrderDimensionsT workgroupWalkOrderDimensions = {0, 1, 2}; +} // namespace Defaults + +static constexpr ConstStringRef required[] = { + Tags::Kernel::ExecutionEnv::actualKernelStartOffset, + Tags::Kernel::ExecutionEnv::grfCount, + Tags::Kernel::ExecutionEnv::simdSize}; + +struct ExecutionEnvBaseT { + ActualKernelStartOffsetT actualKernelStartOffset = -1; + BarrierCountT barrierCount = Defaults::barrierCount; + DisableMidThreadPreemptionT disableMidThreadPreemption = Defaults::disableMidThreadPreemption; + GrfCountT grfCount = -1; + Has4GBBuffersT has4GBBuffers = Defaults::has4GBBuffers; + HasDeviceEnqueueT hasDeviceEnqueue = Defaults::hasDeviceEnqueue; + HasFenceForImageAccessT hasFenceForImageAccess = Defaults::hasFenceForImageAccess; + HasGlobalAtomicsT hasGlobalAtomics = Defaults::hasGlobalAtomics; + HasMultiScratchSpacesT hasMultiScratchSpaces = Defaults::hasMultiScratchSpaces; + HasNoStatelessWriteT hasNoStatelessWrite = Defaults::hasNoStatelessWrite; + HwPreemptionModeT hwPreemptionMode = Defaults::hwPreemptionMode; + OffsetToSkipPerThreadDataLoadT offsetToSkipPerThreadDataLoad = Defaults::offsetToSkipPerThreadDataLoad; + OffsetToSkipSetFfidGpT offsetToSkipSetFfidGp = Defaults::offsetToSkipSetFfidGp; + RequiredSubGroupSizeT requiredSubGroupSize = Defaults::requiredSubGroupSize; + RequiredWorkGroupSizeT requiredWorkGroupSize = {Defaults::requiredWorkGroupSize[0], Defaults::requiredWorkGroupSize[1], Defaults::requiredWorkGroupSize[2]}; + SimdSizeT simdSize = -1; + SlmSizeT slmSize = Defaults::slmSize; + SubgroupIndependentForwardProgressT subgroupIndependentForwardProgress = Defaults::subgroupIndependentForwardProgress; + WorkgroupWalkOrderDimensionsT workgroupWalkOrderDimensions{Defaults::workgroupWalkOrderDimensions[0], Defaults::workgroupWalkOrderDimensions[1], Defaults::workgroupWalkOrderDimensions[2]}; +}; + +} // namespace ExecutionEnv + +enum ArgType : uint8_t { + ArgTypeUnknown = 0, + ArgTypePackedLocalIds = 1, + ArgTypeLocalId, + ArgTypeLocalSize, + ArgTypeGroupSize, + ArgTypeGlobalIdOffset, + ArgTypePrivateBaseStateless, + ArgTypeArgByvalue, + ArgTypeArgBypointer, +}; + +namespace PerThreadPayloadArgument { + +using OffsetT = int32_t; +using SizeT = int32_t; +using ArgTypeT = ArgType; + +namespace Defaults { +} + +struct PerThreadPayloadArgumentBaseT { + ArgTypeT argType = ArgTypeUnknown; + OffsetT offset = -1; + SizeT size = -1; +}; + +} // namespace PerThreadPayloadArgument + +namespace PayloadArgument { + +enum MemoryAddressingMode : uint8_t { + MemoryAddressingModeUnknown = 0, + MemoryAddressingModeStateful = 1, + MemoryAddressingModeStateless, + MemoryAddressingModeBindless, + MemoryAddressingModeSharedLocalMemory, +}; + +enum AddressSpace : uint8_t { + AddressSpaceUnknown = 0, + AddressSpaceGlobal = 1, + AddressSpaceLocal, + AddressSpaceConstant, + AddressSpaceImage, + AddressSpaceSampler, +}; + +enum AccessType : uint8_t { + AccessTypeUnknown = 0, + AccessTypeReadonly = 1, + AccessTypeWriteonly, + AccessTypeReadwrite, +}; + +using ArgTypeT = ArgType; +using OffseT = int32_t; +using SizeT = int32_t; +using ArgIndexT = int32_t; +using AddrmodeT = MemoryAddressingMode; +using AddrspaceT = AddressSpace; +using AccessTypeT = AccessType; + +namespace Defaults { +static constexpr ArgIndexT argIndex = -1; +} + +struct PayloadArgumentBaseT { + ArgTypeT argType = ArgTypeUnknown; + OffseT offset = 0; + SizeT size = 0; + ArgIndexT argIndex = Defaults::argIndex; + AddrmodeT addrmode = MemoryAddressingModeUnknown; + AddrspaceT addrspace = AddressSpaceUnknown; + AccessTypeT accessType = AccessTypeUnknown; +}; + +} // namespace PayloadArgument + +namespace BindingTableEntry { +using BtiValueT = int32_t; +using ArgIndexT = int32_t; +struct BindingTableEntryBaseT { + BtiValueT btiValue = 0U; + ArgIndexT argIndex = 0U; +}; +} // namespace BindingTableEntry + +} // namespace Kernel + +} // namespace Types + +} // namespace ZebinKernelMetadata + +} // namespace Elf + +} // namespace NEO \ No newline at end of file diff --git a/shared/source/device_binary_format/yaml/yaml_parser.h b/shared/source/device_binary_format/yaml/yaml_parser.h index 075fed08bc..ead34fa705 100644 --- a/shared/source/device_binary_format/yaml/yaml_parser.h +++ b/shared/source/device_binary_format/yaml/yaml_parser.h @@ -452,6 +452,9 @@ struct YamlParser { } ConstChildrenRange createChildrenRange(const Node &parent) const { + if (0 == parent.numChildren) { + return ConstChildrenRange(invalidNodeID, nodes); + } return ConstChildrenRange(nodes[parent.firstChildId], nodes); } @@ -478,7 +481,7 @@ struct YamlParser { }; template <> -inline bool YamlParser::readValueChecked(const Node &node, uint64_t &outValue) const { +inline bool YamlParser::readValueChecked(const Node &node, int64_t &outValue) const { if (invalidTokenId == node.value) { return false; } @@ -492,6 +495,45 @@ inline bool YamlParser::readValueChecked(const Node &node, uint64_t &o return true; } +template <> +inline bool YamlParser::readValueChecked(const Node &node, int32_t &outValue) const { + int64_t int64V = 0U; + bool validValue = readValueChecked(node, int64V); + validValue &= int64V <= std::numeric_limits::max(); + validValue &= int64V >= std::numeric_limits::min(); + outValue = static_cast(int64V); + return validValue; +} + +template <> +inline bool YamlParser::readValueChecked(const Node &node, int16_t &outValue) const { + int64_t int64V = 0U; + bool validValue = readValueChecked(node, int64V); + validValue &= int64V <= std::numeric_limits::max(); + validValue &= int64V >= std::numeric_limits::min(); + outValue = static_cast(int64V); + return validValue; +} + +template <> +inline bool YamlParser::readValueChecked(const Node &node, int8_t &outValue) const { + int64_t int64V = 0U; + bool validValue = readValueChecked(node, int64V); + validValue &= int64V <= std::numeric_limits::max(); + validValue &= int64V >= std::numeric_limits::min(); + outValue = static_cast(int64V); + return validValue; +} + +template <> +inline bool YamlParser::readValueChecked(const Node &node, uint64_t &outValue) const { + int64_t int64V = 0U; + bool validValue = readValueChecked(node, int64V); + validValue &= int64V >= 0; + outValue = static_cast(int64V); + return validValue; +} + template <> inline bool YamlParser::readValueChecked(const Node &node, uint32_t &outValue) const { uint64_t uint64V = 0U; diff --git a/shared/source/device_binary_format/zebin_decoder.cpp b/shared/source/device_binary_format/zebin_decoder.cpp new file mode 100644 index 0000000000..a22bfda9a2 --- /dev/null +++ b/shared/source/device_binary_format/zebin_decoder.cpp @@ -0,0 +1,834 @@ +/* + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/source/device_binary_format/zebin_decoder.h" + +#include "shared/source/compiler_interface/intermediate_representations.h" +#include "shared/source/debug_settings/debug_settings_manager.h" +#include "shared/source/device_binary_format/device_binary_formats.h" +#include "shared/source/device_binary_format/elf/elf_decoder.h" +#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/yaml/yaml_parser.h" +#include "shared/source/program/program_info.h" +#include "shared/source/utilities/compiler_support.h" +#include "shared/source/utilities/stackvec.h" + +#include "opencl/source/program/kernel_info.h" + +#include + +namespace NEO { + +DecodeError extractZebinSections(NEO::Elf::Elf &elf, ZebinSections &out, std::string &outErrReason, std::string &outWarning) { + if ((elf.elfFileHeader->shStrNdx >= elf.sectionHeaders.size()) || (NEO::Elf::SHN_UNDEF == elf.elfFileHeader->shStrNdx)) { + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid or missing shStrNdx in elf header\n"); + return DecodeError::InvalidBinary; + } + auto sectionHeaderNamesData = elf.sectionHeaders[elf.elfFileHeader->shStrNdx].data; + ConstStringRef sectionHeaderNamesString(reinterpret_cast(sectionHeaderNamesData.begin()), sectionHeaderNamesData.size()); + + for (auto &elfSectionHeader : elf.sectionHeaders) { + ConstStringRef sectionName = ConstStringRef(sectionHeaderNamesString.begin() + elfSectionHeader.header->name); + switch (elfSectionHeader.header->type) { + default: + outErrReason.append("DeviceBinaryFormat::Zebin : Unhandled ELF section header type : " + std::to_string(elfSectionHeader.header->type) + "\n"); + return DecodeError::InvalidBinary; + case Elf::SHT_PROGBITS: + if (sectionName.startsWith(NEO::Elf::SectionsNamesZebin::textPrefix.data())) { + out.textKernelSections.push_back(&elfSectionHeader); + } else if (sectionName == NEO::Elf::SectionsNamesZebin::dataConst) { + out.constDataSections.push_back(&elfSectionHeader); + } else if (sectionName == ".data.global_const") { + outWarning.append("Misspelled section name : " + sectionName.str() + ", should be : " + NEO::Elf::SectionsNamesZebin::dataConst.str() + "\n"); + out.constDataSections.push_back(&elfSectionHeader); + } else if (sectionName == NEO::Elf::SectionsNamesZebin::dataGlobal) { + out.globalDataSections.push_back(&elfSectionHeader); + } else { + outErrReason.append("DeviceBinaryFormat::Zebin : Unhandled SHT_PROGBITS section : " + sectionName.str() + " currently supports only : " + NEO::Elf::SectionsNamesZebin::textPrefix.str() + "KERNEL_NAME, " + NEO::Elf::SectionsNamesZebin::dataConst.str() + " and " + NEO::Elf::SectionsNamesZebin::dataGlobal.str() + ".\n"); + return DecodeError::InvalidBinary; + } + break; + case NEO::Elf::SHT_ZEBIN_ZEINFO: + out.zeInfoSections.push_back(&elfSectionHeader); + break; + case NEO::Elf::SHT_SYMTAB: + out.symtabSections.push_back(&elfSectionHeader); + break; + case NEO::Elf::SHT_ZEBIN_SPIRV: + out.spirvSections.push_back(&elfSectionHeader); + break; + case NEO::Elf::SHT_STRTAB: + // ignoring intentionally - section header names + continue; + case NEO::Elf::SHT_NULL: + // ignoring intentionally, inactive section, probably UNDEF + continue; + } + } + + return DecodeError::Success; +} + +template +bool validateZebinSectionsCountAtMost(const ContainerT §ionsContainer, ConstStringRef sectionName, uint32_t max, std::string &outErrReason, std::string &outWarning) { + if (sectionsContainer.size() <= max) { + return true; + } + + outErrReason.append("DeviceBinaryFormat::Zebin : Expected at most " + std::to_string(max) + " of " + sectionName.str() + " section, got : " + std::to_string(sectionsContainer.size()) + "\n"); + return false; +} + +template +bool validateZebinSectionsCountExactly(const ContainerT §ionsContainer, ConstStringRef sectionName, uint32_t num, std::string &outErrReason, std::string &outWarning) { + if (sectionsContainer.size() == num) { + return true; + } + + outErrReason.append("DeviceBinaryFormat::Zebin : Expected exactly " + std::to_string(num) + " of " + sectionName.str() + " section, got : " + std::to_string(sectionsContainer.size()) + "\n"); + return false; +} + +DecodeError validateZebinSectionsCount(const ZebinSections §ions, std::string &outErrReason, std::string &outWarning) { + bool valid = validateZebinSectionsCountAtMost(sections.zeInfoSections, NEO::Elf::SectionsNamesZebin::zeInfo, 1U, outErrReason, outWarning); + valid &= validateZebinSectionsCountAtMost(sections.globalDataSections, NEO::Elf::SectionsNamesZebin::dataGlobal, 1U, outErrReason, outWarning); + valid &= validateZebinSectionsCountAtMost(sections.constDataSections, NEO::Elf::SectionsNamesZebin::dataConst, 1U, outErrReason, outWarning); + valid &= validateZebinSectionsCountAtMost(sections.symtabSections, NEO::Elf::SectionsNamesZebin::symtab, 1U, outErrReason, outWarning); + valid &= validateZebinSectionsCountAtMost(sections.spirvSections, NEO::Elf::SectionsNamesZebin::spv, 1U, outErrReason, outWarning); + return valid ? DecodeError::Success : DecodeError::InvalidBinary; +} + +void extractZeInfoKernelSections(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &kernelNd, ZeInfoKernelSections &outZeInfoKernelSections, ConstStringRef context, std::string &outWarning) { + for (const auto &kernelMetadataNd : parser.createChildrenRange(kernelNd)) { + auto key = parser.readKey(kernelMetadataNd); + if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::name == key) { + outZeInfoKernelSections.nameNd.push_back(&kernelMetadataNd); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::executionEnv == key) { + outZeInfoKernelSections.executionEnvNd.push_back(&kernelMetadataNd); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::payloadArguments == key) { + outZeInfoKernelSections.payloadArgumentsNd.push_back(&kernelMetadataNd); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::perThreadPayloadArguments == key) { + outZeInfoKernelSections.perThreadPayloadArgumentsNd.push_back(&kernelMetadataNd); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::bindingTableIndices == key) { + outZeInfoKernelSections.bindingTableIndicesNd.push_back(&kernelMetadataNd); + } else { + outWarning.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unknown entry \"" + parser.readKey(kernelMetadataNd).str() + "\" in context of : " + context.str() + "\n"); + } + } +} + +DecodeError validateZeInfoKernelSectionsCount(const ZeInfoKernelSections &outZeInfoKernelSections, std::string &outErrReason, std::string &outWarning) { + bool valid = validateZebinSectionsCountExactly(outZeInfoKernelSections.nameNd, NEO::Elf::ZebinKernelMetadata::Tags::Kernel::name, 1U, outErrReason, outWarning); + valid &= validateZebinSectionsCountExactly(outZeInfoKernelSections.executionEnvNd, NEO::Elf::ZebinKernelMetadata::Tags::Kernel::executionEnv, 1U, outErrReason, outWarning); + valid &= validateZebinSectionsCountAtMost(outZeInfoKernelSections.payloadArgumentsNd, NEO::Elf::ZebinKernelMetadata::Tags::Kernel::payloadArguments, 1U, outErrReason, outWarning); + valid &= validateZebinSectionsCountAtMost(outZeInfoKernelSections.perThreadPayloadArgumentsNd, NEO::Elf::ZebinKernelMetadata::Tags::Kernel::perThreadPayloadArguments, 1U, outErrReason, outWarning); + valid &= validateZebinSectionsCountAtMost(outZeInfoKernelSections.bindingTableIndicesNd, NEO::Elf::ZebinKernelMetadata::Tags::Kernel::bindingTableIndices, 1U, outErrReason, outWarning); + + return valid ? DecodeError::Success : DecodeError::InvalidBinary; +} + +template +bool readZeInfoValueChecked(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &node, T &outValue, ConstStringRef context, std::string &outErrReason) { + if (parser.readValueChecked(node, outValue)) { + return true; + } + outErrReason.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : could not read " + parser.readKey(node).str() + " from : [" + parser.readValue(node).str() + "] in context of : " + context.str() + "\n"); + return false; +} + +DecodeError readZeInfoExecutionEnvironment(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &node, + NEO::Elf::ZebinKernelMetadata::Types::Kernel::ExecutionEnv::ExecutionEnvBaseT &outExecEnv, + ConstStringRef context, + std::string &outErrReason, std::string &outWarning) { + bool validExecEnv = true; + for (const auto &execEnvMetadataNd : parser.createChildrenRange(node)) { + auto key = parser.readKey(execEnvMetadataNd); + if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::actualKernelStartOffset == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.actualKernelStartOffset, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::barrierCount == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.barrierCount, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::disableMidThreadPreemption == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.disableMidThreadPreemption, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::grfCount == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.grfCount, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::has4gbBuffers == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.has4GBBuffers, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::hasDeviceEnqueue == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.hasDeviceEnqueue, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::hasFenceForImageAccess == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.hasFenceForImageAccess, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::hasGlobalAtomics == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.hasGlobalAtomics, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::hasMultiScratchSpaces == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.hasMultiScratchSpaces, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::hasNoStatelessWrite == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.hasNoStatelessWrite, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::hwPreemptionMode == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.hwPreemptionMode, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::offsetToSkipPerThreadDataLoad == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.offsetToSkipPerThreadDataLoad, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::offsetToSkipSetFfidGp == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.offsetToSkipSetFfidGp, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::requiredSubGroupSize == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.requiredSubGroupSize, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::simdSize == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.simdSize, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::slmSize == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.slmSize, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::ExecutionEnv::subgroupIndependentForwardProgress == key) { + validExecEnv = validExecEnv & readZeInfoValueChecked(parser, execEnvMetadataNd, outExecEnv.subgroupIndependentForwardProgress, context, outErrReason); + } else { + outWarning.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unknown entry \"" + key.str() + "\" in context of " + context.str() + "\n"); + } + } + return validExecEnv ? DecodeError::Success : DecodeError::InvalidBinary; +} + +bool readEnumChecked(const Yaml::Token *token, NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgType &out, ConstStringRef context, std::string &outErrReason) { + if (nullptr == token) { + return false; + } + + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel; + using ArgTypeT = NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgType; + auto tokenValue = token->cstrref(); + if (tokenValue == PerThreadPayloadArgument::ArgType::packedLocalIds) { + out = ArgTypeT::ArgTypePackedLocalIds; + } else if (tokenValue == PerThreadPayloadArgument::ArgType::localId) { + out = ArgTypeT::ArgTypeLocalId; + } else if (tokenValue == PayloadArgument::ArgType::localSize) { + out = ArgTypeT::ArgTypeLocalSize; + } else if (tokenValue == PayloadArgument::ArgType::groupSize) { + out = ArgTypeT::ArgTypeGroupSize; + } else if (tokenValue == PayloadArgument::ArgType::globalIdOffset) { + out = ArgTypeT::ArgTypeGlobalIdOffset; + } else if (tokenValue == PayloadArgument::ArgType::privateBaseStateless) { + out = ArgTypeT::ArgTypePrivateBaseStateless; + } else if (tokenValue == PayloadArgument::ArgType::argByvalue) { + out = ArgTypeT::ArgTypeArgByvalue; + } else if (tokenValue == PayloadArgument::ArgType::argBypointer) { + out = ArgTypeT::ArgTypeArgBypointer; + } else { + outErrReason.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unhandled \"" + tokenValue.str() + "\" argument type in context of " + context.str() + "\n"); + return false; + } + return true; +} + +bool readEnumChecked(const Yaml::Token *token, NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingMode &out, + ConstStringRef context, std::string &outErrReason) { + if (nullptr == token) { + return false; + } + + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::MemoryAddressingMode; + using AddrMode = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingMode; + auto tokenValue = token->cstrref(); + + if (stateless == tokenValue) { + out = AddrMode::MemoryAddressingModeStateless; + } else if (stateful == tokenValue) { + out = AddrMode::MemoryAddressingModeStateful; + } else if (bindless == tokenValue) { + out = AddrMode::MemoryAddressingModeBindless; + } else if (sharedLocalMemory == tokenValue) { + out = AddrMode::MemoryAddressingModeSharedLocalMemory; + } else { + outErrReason.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unhandled \"" + tokenValue.str() + "\" memory addressing mode in context of " + context.str() + "\n"); + return false; + } + + return true; +} + +bool readEnumChecked(const Yaml::Token *token, NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpace &out, + ConstStringRef context, std::string &outErrReason) { + if (nullptr == token) { + return false; + } + + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::AddrSpace; + using AddrSpace = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpace; + auto tokenValue = token->cstrref(); + + if (global == tokenValue) { + out = AddrSpace::AddressSpaceGlobal; + } else if (local == tokenValue) { + out = AddrSpace::AddressSpaceLocal; + } else if (constant == tokenValue) { + out = AddrSpace::AddressSpaceConstant; + } else if (image == tokenValue) { + out = AddrSpace::AddressSpaceImage; + } else if (sampler == tokenValue) { + out = AddrSpace::AddressSpaceSampler; + } else { + outErrReason.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unhandled \"" + tokenValue.str() + "\" address space in context of " + context.str() + "\n"); + return false; + } + + return true; +} + +bool readEnumChecked(const Yaml::Token *token, NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessType &out, + ConstStringRef context, std::string &outErrReason) { + if (nullptr == token) { + return false; + } + + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::AccessType; + using AccessType = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessType; + auto tokenValue = token->cstrref(); + + static constexpr ConstStringRef readonly("readonly"); + static constexpr ConstStringRef writeonly("writeonly"); + static constexpr ConstStringRef readwrite("readwrite"); + + if (readonly == tokenValue) { + out = AccessType::AccessTypeReadonly; + } else if (writeonly == tokenValue) { + out = AccessType::AccessTypeWriteonly; + } else if (readwrite == tokenValue) { + out = AccessType::AccessTypeReadwrite; + } else { + outErrReason.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unhandled \"" + tokenValue.str() + "\" access type in context of " + context.str() + "\n"); + return false; + } + + return true; +} + +DecodeError readZeInfoPerThreadPayloadArguments(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &node, + ZeInfoPerThreadPayloadArguments &outPerThreadPayloadArguments, + ConstStringRef context, + std::string &outErrReason, std::string &outWarning) { + bool validPerThreadPayload = true; + for (const auto &perThredPayloadArgumentNd : parser.createChildrenRange(node)) { + outPerThreadPayloadArguments.resize(outPerThreadPayloadArguments.size() + 1); + auto &perThreadPayloadArgMetadata = *outPerThreadPayloadArguments.rbegin(); + ConstStringRef argTypeStr; + for (const auto &perThreadPayloadArgumentMemberNd : parser.createChildrenRange(perThredPayloadArgumentNd)) { + auto key = parser.readKey(perThreadPayloadArgumentMemberNd); + if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PerThreadPayloadArgument::argType == key) { + auto argTypeToken = parser.getValueToken(perThreadPayloadArgumentMemberNd); + argTypeStr = parser.readValue(perThreadPayloadArgumentMemberNd); + validPerThreadPayload &= readEnumChecked(argTypeToken, perThreadPayloadArgMetadata.argType, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PerThreadPayloadArgument::size == key) { + validPerThreadPayload &= readZeInfoValueChecked(parser, perThreadPayloadArgumentMemberNd, perThreadPayloadArgMetadata.size, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PerThreadPayloadArgument::offset == key) { + validPerThreadPayload &= readZeInfoValueChecked(parser, perThreadPayloadArgumentMemberNd, perThreadPayloadArgMetadata.offset, context, outErrReason); + } else { + outWarning.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unknown entry \"" + key.str() + "\" for per-thread payload argument in context of " + context.str() + "\n"); + } + } + if (0 == perThreadPayloadArgMetadata.size) { + outWarning.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Skippinig 0-size per-thread argument of type : " + argTypeStr.str() + " in context of " + context.str() + "\n"); + outPerThreadPayloadArguments.pop_back(); + } + } + + return validPerThreadPayload ? DecodeError::Success : DecodeError::InvalidBinary; +} + +DecodeError readZeInfoPayloadArguments(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &node, + ZeInfoPayloadArguments &ouPayloadArguments, + uint32_t &outMaxPayloadArgumentIndex, + ConstStringRef context, + std::string &outErrReason, std::string &outWarning) { + bool validPayload = true; + for (const auto &payloadArgumentNd : parser.createChildrenRange(node)) { + ouPayloadArguments.resize(ouPayloadArguments.size() + 1); + auto &payloadArgMetadata = *ouPayloadArguments.rbegin(); + for (const auto &payloadArgumentMemberNd : parser.createChildrenRange(payloadArgumentNd)) { + auto key = parser.readKey(payloadArgumentMemberNd); + if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::argType == key) { + auto argTypeToken = parser.getValueToken(payloadArgumentMemberNd); + validPayload &= readEnumChecked(argTypeToken, payloadArgMetadata.argType, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::argIndex == key) { + validPayload &= parser.readValueChecked(payloadArgumentMemberNd, payloadArgMetadata.argIndex); + outMaxPayloadArgumentIndex = std::max(outMaxPayloadArgumentIndex, payloadArgMetadata.argIndex); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::offset == key) { + validPayload &= readZeInfoValueChecked(parser, payloadArgumentMemberNd, payloadArgMetadata.offset, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::size == key) { + validPayload &= readZeInfoValueChecked(parser, payloadArgumentMemberNd, payloadArgMetadata.size, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::addrmode == key) { + auto memTypeToken = parser.getValueToken(payloadArgumentMemberNd); + validPayload &= readEnumChecked(memTypeToken, payloadArgMetadata.addrmode, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::addrspace == key) { + auto addrSpaceToken = parser.getValueToken(payloadArgumentMemberNd); + validPayload &= readEnumChecked(addrSpaceToken, payloadArgMetadata.addrspace, context, outErrReason); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::accessType == key) { + auto accessTypeToken = parser.getValueToken(payloadArgumentMemberNd); + validPayload &= readEnumChecked(accessTypeToken, payloadArgMetadata.accessType, context, outErrReason); + } else { + outWarning.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unknown entry \"" + key.str() + "\" for payload argument in context of " + context.str() + "\n"); + } + } + } + return validPayload ? DecodeError::Success : DecodeError::InvalidBinary; +} + +DecodeError readZeInfoBindingTableIndices(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &node, + ZeInfoBindingTableIndices &outBindingTableIndices, ZeInfoBindingTableIndices::value_type &outMaxBindingTableIndex, + ConstStringRef context, + std::string &outErrReason, std::string &outWarning) { + bool validBindingTableEntries = true; + for (const auto &bindingTableIndexNd : parser.createChildrenRange(node)) { + outBindingTableIndices.resize(outBindingTableIndices.size() + 1); + auto &bindingTableIndexMetadata = *outBindingTableIndices.rbegin(); + for (const auto &bindingTableIndexMemberNd : parser.createChildrenRange(bindingTableIndexNd)) { + auto key = parser.readKey(bindingTableIndexMemberNd); + if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::BindingTableIndex::argIndex == key) { + validBindingTableEntries &= readZeInfoValueChecked(parser, bindingTableIndexMemberNd, bindingTableIndexMetadata.argIndex, context, outErrReason); + outMaxBindingTableIndex.argIndex = std::max(outMaxBindingTableIndex.argIndex, bindingTableIndexMetadata.argIndex); + } else if (NEO::Elf::ZebinKernelMetadata::Tags::Kernel::BindingTableIndex::btiValue == key) { + validBindingTableEntries &= readZeInfoValueChecked(parser, bindingTableIndexMemberNd, bindingTableIndexMetadata.btiValue, context, outErrReason); + outMaxBindingTableIndex.btiValue = std::max(outMaxBindingTableIndex.btiValue, bindingTableIndexMetadata.btiValue); + } else { + outWarning.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Unknown entry \"" + key.str() + "\" for binding table index in context of " + context.str() + "\n"); + } + } + } + + return validBindingTableEntries ? DecodeError::Success : DecodeError::InvalidBinary; +} + +template +bool setVecArgIndicesBasedOnSize(CrossThreadDataOffset (&vec)[Len], size_t vecSize, CrossThreadDataOffset baseOffset) { + switch (vecSize) { + default: + return false; + case sizeof(ElSize) * 3: + vec[2] = static_cast(baseOffset + 2 * sizeof(ElSize)); + CPP_ATTRIBUTE_FALLTHROUGH; + case sizeof(ElSize) * 2: + vec[1] = static_cast(baseOffset + 1 * sizeof(ElSize)); + CPP_ATTRIBUTE_FALLTHROUGH; + case sizeof(ElSize) * 1: + vec[0] = static_cast(baseOffset + 0 * sizeof(ElSize)); + break; + } + return true; +} + +NEO::DecodeError populateArgDescriptor(const NEO::Elf::ZebinKernelMetadata::Types::Kernel::PerThreadPayloadArgument::PerThreadPayloadArgumentBaseT &src, NEO::KernelDescriptor &dst, + std::string &outErrReason, std::string &outWarning) { + switch (src.argType) { + default: + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid arg type in per thread data section in context of : " + dst.kernelMetadata.kernelName + ".\n"); + return DecodeError::InvalidBinary; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeLocalId: { + if (src.offset != 0) { + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid offset for argument of type " + NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PerThreadPayloadArgument::ArgType::localId.str() + " in context of : " + dst.kernelMetadata.kernelName + ". Expected 0.\n"); + return DecodeError::InvalidBinary; + } + using LocalIdT = uint16_t; + + uint32_t singleChannelIndicesCount = (dst.kernelAttributes.simdSize == 32 ? 32 : 16); + uint32_t singleChannelBytes = singleChannelIndicesCount * sizeof(LocalIdT); + auto tupleSize = (src.size / singleChannelBytes); + switch (tupleSize) { + default: + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid size for argument of type " + NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PerThreadPayloadArgument::ArgType::localId.str() + " in context of : " + dst.kernelMetadata.kernelName + ". For simd=" + std::to_string(dst.kernelAttributes.simdSize) + " expected : " + std::to_string(singleChannelBytes) + " or " + std::to_string(singleChannelBytes * 2) + " or " + std::to_string(singleChannelBytes * 3) + ". Got : " + std::to_string(src.size) + " \n"); + return DecodeError::InvalidBinary; + case 1: + CPP_ATTRIBUTE_FALLTHROUGH; + case 2: + CPP_ATTRIBUTE_FALLTHROUGH; + case 3: + dst.kernelAttributes.numLocalIdChannels = static_cast(tupleSize); + break; + } + dst.kernelAttributes.perThreadDataSize = dst.kernelAttributes.simdSize; + dst.kernelAttributes.perThreadDataSize *= dst.kernelAttributes.numLocalIdChannels; + dst.kernelAttributes.perThreadDataSize *= sizeof(LocalIdT); + break; + } + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypePackedLocalIds: { + if (src.offset != 0) { + outErrReason.append("DeviceBinaryFormat::Zebin : Unhandled offset for argument of type " + NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PerThreadPayloadArgument::ArgType::packedLocalIds.str() + " in context of : " + dst.kernelMetadata.kernelName + ". Expected 0.\n"); + return DecodeError::InvalidBinary; + } + using LocalIdT = uint16_t; + auto tupleSize = src.size / sizeof(LocalIdT); + switch (tupleSize) { + default: + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid size for argument of type " + NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PerThreadPayloadArgument::ArgType::packedLocalIds.str() + " in context of : " + dst.kernelMetadata.kernelName + ". Expected : " + std::to_string(sizeof(LocalIdT)) + " or " + std::to_string(sizeof(LocalIdT) * 2) + " or " + std::to_string(sizeof(LocalIdT) * 3) + ". Got : " + std::to_string(src.size) + " \n"); + return DecodeError::InvalidBinary; + + case 1: + CPP_ATTRIBUTE_FALLTHROUGH; + case 2: + CPP_ATTRIBUTE_FALLTHROUGH; + case 3: + dst.kernelAttributes.numLocalIdChannels = static_cast(tupleSize); + break; + } + dst.kernelAttributes.simdSize = 1; + dst.kernelAttributes.perThreadDataSize = dst.kernelAttributes.simdSize; + dst.kernelAttributes.perThreadDataSize *= dst.kernelAttributes.numLocalIdChannels; + dst.kernelAttributes.perThreadDataSize *= sizeof(LocalIdT); + break; + } + } + + return DecodeError::Success; +} + +NEO::DecodeError populateArgDescriptor(const NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::PayloadArgumentBaseT &src, NEO::KernelDescriptor &dst, uint32_t &crossThreadDataSize, + std::string &outErrReason, std::string &outWarning) { + crossThreadDataSize = std::max(crossThreadDataSize, src.offset + src.size); + switch (src.argType) { + default: + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid arg type in cross thread data section in context of : " + dst.kernelMetadata.kernelName + ".\n"); + return DecodeError::InvalidBinary; // unsupported + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeArgBypointer: { + auto &argTraits = dst.payloadMappings.explicitArgs[src.argIndex].getTraits(); + auto &argAsPointer = dst.payloadMappings.explicitArgs[src.argIndex].as(true); + switch (src.addrspace) { + default: + UNRECOVERABLE_IF(NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpaceUnknown != src.addrspace); + argTraits.addressQualifier = KernelArgMetadata::AddrUnknown; + break; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpaceGlobal: + argTraits.addressQualifier = KernelArgMetadata::AddrGlobal; + break; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpaceLocal: + argTraits.addressQualifier = KernelArgMetadata::AddrLocal; + break; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpaceConstant: + argTraits.addressQualifier = KernelArgMetadata::AddrConstant; + break; + } + + switch (src.accessType) { + default: + UNRECOVERABLE_IF(argTraits.accessQualifier != NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessTypeUnknown); + argTraits.accessQualifier = KernelArgMetadata::AccessUnknown; + break; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessTypeReadonly: + argTraits.accessQualifier = KernelArgMetadata::AccessReadOnly; + break; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessTypeReadwrite: + argTraits.accessQualifier = KernelArgMetadata::AccessReadWrite; + break; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessTypeWriteonly: + argTraits.accessQualifier = KernelArgMetadata::AccessWriteOnly; + break; + } + + argTraits.argByValSize = sizeof(void *); + switch (src.addrmode) { + default: + outErrReason.append("Invalid or missing memory addressing mode for arg idx : " + std::to_string(src.argIndex) + " in context of : " + dst.kernelMetadata.kernelName + ".\n"); + return DecodeError::InvalidBinary; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingModeStateful: + break; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingModeStateless: + argAsPointer.stateless = src.offset; + argAsPointer.pointerSize = src.size; + break; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingModeBindless: + argAsPointer.bindless = src.offset; + break; + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingModeSharedLocalMemory: + argAsPointer.slmOffset = src.offset; // what about SLM alignment ? + break; + } + break; + } + + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeArgByvalue: { + auto &argAsValue = dst.payloadMappings.explicitArgs[src.argIndex].as(true); + ArgDescValue::Element valueElement; + valueElement.offset = src.offset; + valueElement.sourceOffset = 0U; + valueElement.size = src.size; + argAsValue.elements.push_back(valueElement); + break; + } + + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeLocalSize: { + using LocalSizeT = uint32_t; + if (false == setVecArgIndicesBasedOnSize(dst.payloadMappings.dispatchTraits.localWorkSize, src.size, src.offset)) { + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid size for argument of type " + NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::ArgType::localSize.str() + " in context of : " + dst.kernelMetadata.kernelName + ". Expected 4 or 8 or 12. Got : " + std::to_string(src.size) + "\n"); + return DecodeError::InvalidBinary; + } + break; + } + + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeGlobalIdOffset: { + using GlovaIdOffsetT = uint32_t; + if (false == setVecArgIndicesBasedOnSize(dst.payloadMappings.dispatchTraits.globalWorkOffset, src.size, src.offset)) { + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid size for argument of type " + NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::ArgType::globalIdOffset.str() + " in context of : " + dst.kernelMetadata.kernelName + ". Expected 4 or 8 or 12. Got : " + std::to_string(src.size) + "\n"); + return DecodeError::InvalidBinary; + } + break; + } + case NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeGroupSize: { + using GroupSizeT = uint32_t; + if (false == setVecArgIndicesBasedOnSize(dst.payloadMappings.dispatchTraits.numWorkGroups, src.size, src.offset)) { + { + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid size for argument of type " + NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::ArgType::groupSize.str() + " in context of : " + dst.kernelMetadata.kernelName + ". Expected 4 or 8 or 12. Got : " + std::to_string(src.size) + "\n"); + return DecodeError::InvalidBinary; + } + } + break; + } + } + + return DecodeError::Success; +} + +NEO::DecodeError populateKernelDescriptor(NEO::ProgramInfo &dst, NEO::Elf::Elf &elf, NEO::ZebinSections &zebinSections, + NEO::Yaml::YamlParser &yamlParser, const NEO::Yaml::Node &kernelNd, std::string &outErrReason, std::string &outWarning) { + auto kernelInfo = std::make_unique(); + auto &kernelDescriptor = kernelInfo->kernelDescriptor; + + ZeInfoKernelSections zeInfokernelSections; + extractZeInfoKernelSections(yamlParser, kernelNd, zeInfokernelSections, NEO::Elf::SectionsNamesZebin::zeInfo, outWarning); + auto extractError = validateZeInfoKernelSectionsCount(zeInfokernelSections, outErrReason, outWarning); + if (DecodeError::Success != extractError) { + return extractError; + } + + kernelDescriptor.kernelMetadata.kernelName = yamlParser.readValue(*zeInfokernelSections.nameNd[0]).str(); + + NEO::Elf::ZebinKernelMetadata::Types::Kernel::ExecutionEnv::ExecutionEnvBaseT execEnv; + auto execEnvErr = readZeInfoExecutionEnvironment(yamlParser, *zeInfokernelSections.executionEnvNd[0], execEnv, kernelInfo->kernelDescriptor.kernelMetadata.kernelName, outErrReason, outWarning); + if (DecodeError::Success != execEnvErr) { + return execEnvErr; + } + + ZeInfoPerThreadPayloadArguments perThreadPayloadArguments; + if (false == zeInfokernelSections.perThreadPayloadArgumentsNd.empty()) { + auto perThreadPayloadArgsErr = readZeInfoPerThreadPayloadArguments(yamlParser, *zeInfokernelSections.perThreadPayloadArgumentsNd[0], perThreadPayloadArguments, + kernelDescriptor.kernelMetadata.kernelName, outErrReason, outWarning); + if (DecodeError::Success != perThreadPayloadArgsErr) { + return perThreadPayloadArgsErr; + } + } + + uint32_t maxArgumentIndex = 0U; + ZeInfoPayloadArguments payloadArguments; + if (false == zeInfokernelSections.payloadArgumentsNd.empty()) { + auto payloadArgsErr = readZeInfoPayloadArguments(yamlParser, *zeInfokernelSections.payloadArgumentsNd[0], payloadArguments, maxArgumentIndex, + kernelDescriptor.kernelMetadata.kernelName, outErrReason, outWarning); + if (DecodeError::Success != payloadArgsErr) { + return payloadArgsErr; + } + } + + kernelDescriptor.kernelAttributes.hasBarriers = execEnv.barrierCount; + kernelDescriptor.kernelAttributes.flags.usesBarriers = (kernelDescriptor.kernelAttributes.hasBarriers > 0U); + kernelDescriptor.kernelAttributes.flags.requiresDisabledMidThreadPreemption = execEnv.disableMidThreadPreemption; + kernelDescriptor.kernelAttributes.numGrfRequired = execEnv.grfCount; + if (execEnv.has4GBBuffers) { + kernelDescriptor.kernelAttributes.bufferAddressingMode = KernelDescriptor::Stateless; + } + kernelDescriptor.kernelAttributes.flags.usesDeviceSideEnqueue = execEnv.hasDeviceEnqueue; + kernelDescriptor.kernelAttributes.flags.usesFencesForReadWriteImages = execEnv.hasFenceForImageAccess; + kernelDescriptor.kernelAttributes.flags.useGlobalAtomics = execEnv.hasGlobalAtomics; + kernelDescriptor.kernelAttributes.flags.usesStatelessWrites = (false == execEnv.hasNoStatelessWrite); + kernelDescriptor.entryPoints.skipPerThreadDataLoad = execEnv.offsetToSkipPerThreadDataLoad; + kernelDescriptor.entryPoints.skipSetFFIDGP = execEnv.offsetToSkipSetFfidGp; + kernelDescriptor.kernelMetadata.requiredSubGroupSize = execEnv.requiredSubGroupSize; + kernelDescriptor.kernelAttributes.simdSize = execEnv.simdSize; + kernelDescriptor.kernelAttributes.slmInlineSize = execEnv.slmSize; + kernelDescriptor.kernelAttributes.flags.requiresSubgroupIndependentForwardProgress = execEnv.subgroupIndependentForwardProgress; + + if ((kernelDescriptor.kernelAttributes.simdSize != 1) && (kernelDescriptor.kernelAttributes.simdSize != 8) && (kernelDescriptor.kernelAttributes.simdSize != 16) && (kernelDescriptor.kernelAttributes.simdSize != 32)) { + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid simd size : " + std::to_string(kernelDescriptor.kernelAttributes.simdSize) + " in context of : " + kernelDescriptor.kernelMetadata.kernelName + ". Expected 1, 8, 16 or 32. Got : " + std::to_string(kernelDescriptor.kernelAttributes.simdSize) + "\n"); + return DecodeError::InvalidBinary; + } + + for (const auto &arg : perThreadPayloadArguments) { + auto decodeErr = populateArgDescriptor(arg, kernelDescriptor, outErrReason, outWarning); + if (DecodeError::Success != decodeErr) { + return decodeErr; + } + } + + kernelDescriptor.payloadMappings.explicitArgs.resize(maxArgumentIndex + 1); + kernelDescriptor.explicitArgsExtendedMetadata.resize(maxArgumentIndex + 1); + kernelDescriptor.kernelAttributes.numArgsToPatch = maxArgumentIndex + 1; + + uint32_t crossThreadDataSize = 0; + for (const auto &arg : payloadArguments) { + auto decodeErr = populateArgDescriptor(arg, kernelDescriptor, crossThreadDataSize, outErrReason, outWarning); + if (DecodeError::Success != decodeErr) { + return decodeErr; + } + } + + if (NEO::DebugManager.flags.ZebinAppendElws.get()) { + kernelDescriptor.payloadMappings.dispatchTraits.enqueuedLocalWorkSize[0] = alignDown(crossThreadDataSize + 12, 32); + kernelDescriptor.payloadMappings.dispatchTraits.enqueuedLocalWorkSize[1] = kernelDescriptor.payloadMappings.dispatchTraits.enqueuedLocalWorkSize[0] + 4; + kernelDescriptor.payloadMappings.dispatchTraits.enqueuedLocalWorkSize[2] = kernelDescriptor.payloadMappings.dispatchTraits.enqueuedLocalWorkSize[1] + 4; + crossThreadDataSize = kernelDescriptor.payloadMappings.dispatchTraits.enqueuedLocalWorkSize[2] + 4; + } + kernelDescriptor.kernelAttributes.crossThreadDataSize = static_cast(alignUp(crossThreadDataSize, 32)); + + ZeInfoBindingTableIndices bindingTableIndices; + ZeInfoBindingTableIndices::value_type maximumBindingTableEntry; + if (false == zeInfokernelSections.bindingTableIndicesNd.empty()) { + auto btisErr = readZeInfoBindingTableIndices(yamlParser, *zeInfokernelSections.bindingTableIndicesNd[0], bindingTableIndices, maximumBindingTableEntry, + kernelDescriptor.kernelMetadata.kernelName, outErrReason, outWarning); + if (DecodeError::Success != btisErr) { + return btisErr; + } + } + + auto generatedSshPos = kernelDescriptor.generatedHeaps.size(); + uint32_t generatedSshSize = 0U; + if (bindingTableIndices.empty() == false) { + static constexpr auto maxSurfaceStateSize = 64U; + static constexpr auto btiSize = sizeof(int); + auto numEntries = maximumBindingTableEntry.btiValue + 1; + kernelDescriptor.generatedHeaps.resize(alignUp(generatedSshPos, maxSurfaceStateSize), 0U); + generatedSshPos = kernelInfo->kernelDescriptor.generatedHeaps.size(); + + // make room for surface states + kernelDescriptor.generatedHeaps.resize(generatedSshPos + numEntries * maxSurfaceStateSize, 0U); + + auto generatedBindingTablePos = kernelDescriptor.generatedHeaps.size(); + kernelDescriptor.generatedHeaps.resize(generatedBindingTablePos + numEntries * btiSize, 0U); + auto bindingTableIt = reinterpret_cast(kernelDescriptor.generatedHeaps.data() + generatedBindingTablePos); + for (auto &bti : bindingTableIndices) { + *bindingTableIt = bti.btiValue * 64U; + ++bindingTableIt; + auto &explicitArg = kernelDescriptor.payloadMappings.explicitArgs[bti.argIndex]; + switch (explicitArg.type) { + default: + outErrReason.append("DeviceBinaryFormat::Zebin::.ze_info : Invalid binding table entry for non-pointer and non-image argument idx : " + std::to_string(bti.argIndex) + ".\n"); + return DecodeError::InvalidBinary; + case ArgDescriptor::ArgTPointer: { + explicitArg.as().bindful = bti.btiValue * maxSurfaceStateSize; + break; + } + } + } + kernelDescriptor.generatedHeaps.resize(alignUp(kernelDescriptor.generatedHeaps.size(), maxSurfaceStateSize), 0U); + generatedSshSize = static_cast(kernelDescriptor.generatedHeaps.size() - generatedSshPos); + + kernelDescriptor.payloadMappings.bindingTable.numEntries = numEntries; + kernelDescriptor.payloadMappings.bindingTable.tableOffset = static_cast(generatedBindingTablePos - generatedSshPos); + } + + ZebinSections::SectionHeaderData *correspondingTextSegment = nullptr; + auto sectionHeaderNamesData = elf.sectionHeaders[elf.elfFileHeader->shStrNdx].data; + ConstStringRef sectionHeaderNamesString(reinterpret_cast(sectionHeaderNamesData.begin()), sectionHeaderNamesData.size()); + for (auto *textSection : zebinSections.textKernelSections) { + ConstStringRef sectionName = ConstStringRef(sectionHeaderNamesString.begin() + textSection->header->name); + auto sufix = sectionName.substr(static_cast(NEO::Elf::SectionsNamesZebin::textPrefix.length())); + if (sufix == kernelDescriptor.kernelMetadata.kernelName) { + correspondingTextSegment = textSection; + } + } + + if (nullptr == correspondingTextSegment) { + outErrReason.append("Could not find text section for kernel " + kernelDescriptor.kernelMetadata.kernelName + "\n"); + return DecodeError::InvalidBinary; + } + + kernelInfo->heapInfo.pKernelHeap = correspondingTextSegment->data.begin(); + kernelInfo->heapInfo.KernelHeapSize = static_cast(correspondingTextSegment->data.size()); + kernelInfo->heapInfo.KernelUnpaddedSize = static_cast(correspondingTextSegment->data.size()); + kernelInfo->heapInfo.pSsh = kernelDescriptor.generatedHeaps.data() + generatedSshPos; + kernelInfo->heapInfo.SurfaceStateHeapSize = generatedSshSize; + dst.kernelInfos.push_back(kernelInfo.release()); + return DecodeError::Success; +} + +template <> +DecodeError decodeSingleDeviceBinary(ProgramInfo &dst, const SingleDeviceBinary &src, std::string &outErrReason, std::string &outWarning) { + auto elf = Elf::decodeElf(src.deviceBinary, outErrReason, outWarning); + if (nullptr == elf.elfFileHeader) { + return DecodeError::InvalidBinary; + } + + ZebinSections zebinSections; + auto extractError = extractZebinSections(elf, zebinSections, outErrReason, outWarning); + if (DecodeError::Success != extractError) { + return extractError; + } + + extractError = validateZebinSectionsCount(zebinSections, outErrReason, outWarning); + if (DecodeError::Success != extractError) { + return extractError; + } + + if (false == zebinSections.globalDataSections.empty()) { + dst.globalVariables.initData = zebinSections.globalDataSections[0]->data.begin(); + dst.globalVariables.size = zebinSections.globalDataSections[0]->data.size(); + } + + if (false == zebinSections.constDataSections.empty()) { + dst.globalConstants.initData = zebinSections.constDataSections[0]->data.begin(); + dst.globalConstants.size = zebinSections.constDataSections[0]->data.size(); + } + + if (false == zebinSections.symtabSections.empty()) { + dst.prepareLinkerInputStorage(); + auto expectedSymSize = sizeof(NEO::Elf::ElfSymbolEntry); + auto gotSymSize = zebinSections.symtabSections[0]->header->entsize; + if (expectedSymSize != gotSymSize) { + outErrReason.append("DeviceBinaryFormat::Zebin : Invalid symbol table entries size - expected : " + std::to_string(expectedSymSize) + ", got : " + std::to_string(gotSymSize) + "\n"); + return DecodeError::InvalidBinary; + } + outWarning.append("DeviceBinaryFormat::Zebin : Ignoring symbol table\n"); + } + + if (zebinSections.zeInfoSections.empty()) { + outWarning.append("DeviceBinaryFormat::Zebin : Expected at least one " + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " section, got 0\n"); + return DecodeError::Success; + } + + auto metadataSectionData = zebinSections.zeInfoSections[0]->data; + ConstStringRef metadataString(reinterpret_cast(metadataSectionData.begin()), metadataSectionData.size()); + NEO::Yaml::YamlParser yamlParser; + bool parseSuccess = yamlParser.parse(metadataString, outErrReason, outWarning); + if (false == parseSuccess) { + return DecodeError::InvalidBinary; + } + + if (yamlParser.empty()) { + outWarning.append("DeviceBinaryFormat::Zebin : Empty kernels metadata section (" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + ")\n"); + return DecodeError::Success; + } + + UniqueNode kernelsSectionNodes; + 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; + } + 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"); + } + + if (kernelsSectionNodes.size() > 1U) { + outErrReason.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Expected at most one " + NEO::Elf::ZebinKernelMetadata::Tags::kernels.str() + " entry in global scope of " + NEO::Elf::SectionsNamesZebin::zeInfo.str() + ", got : " + std::to_string(kernelsSectionNodes.size()) + "\n"); + return DecodeError::InvalidBinary; + } + + if (kernelsSectionNodes.empty()) { + outWarning.append("DeviceBinaryFormat::Zebin::" + NEO::Elf::SectionsNamesZebin::zeInfo.str() + " : Expected one " + NEO::Elf::ZebinKernelMetadata::Tags::kernels.str() + " entry in global scope of " + NEO::Elf::SectionsNamesZebin::zeInfo.str() + ", got : " + std::to_string(kernelsSectionNodes.size()) + "\n"); + return DecodeError::Success; + } + + for (const auto &kernelNd : yamlParser.createChildrenRange(*kernelsSectionNodes[0])) { + auto zeInfoErr = populateKernelDescriptor(dst, elf, zebinSections, yamlParser, kernelNd, outErrReason, outWarning); + if (DecodeError::Success != zeInfoErr) { + return zeInfoErr; + } + } + + return DecodeError::Success; +} + +} // namespace NEO diff --git a/shared/source/device_binary_format/zebin_decoder.h b/shared/source/device_binary_format/zebin_decoder.h new file mode 100644 index 0000000000..f8a5fa60eb --- /dev/null +++ b/shared/source/device_binary_format/zebin_decoder.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/source/device_binary_format/device_binary_formats.h" +#include "shared/source/device_binary_format/elf/elf_decoder.h" +#include "shared/source/device_binary_format/elf/zebin_elf.h" +#include "shared/source/device_binary_format/yaml/yaml_parser.h" +#include "shared/source/kernel/kernel_descriptor.h" +#include "shared/source/utilities/stackvec.h" + +#include + +namespace NEO { + +struct ZebinSections { + using SectionHeaderData = NEO::Elf::Elf::SectionHeaderAndData; + StackVec textKernelSections; + StackVec zeInfoSections; + StackVec globalDataSections; + StackVec constDataSections; + StackVec symtabSections; + StackVec spirvSections; +}; + +using UniqueNode = StackVec; +struct ZeInfoKernelSections { + UniqueNode nameNd; + UniqueNode executionEnvNd; + UniqueNode payloadArgumentsNd; + UniqueNode bindingTableIndicesNd; + UniqueNode perThreadPayloadArgumentsNd; +}; + +DecodeError extractZebinSections(NEO::Elf::Elf &elf, ZebinSections &out, std::string &outErrReason, std::string &outWarning); +DecodeError validateZebinSectionsCount(const ZebinSections §ions, std::string &outErrReason, std::string &outWarning); +void extractZeInfoKernelSections(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &kernelNd, ZeInfoKernelSections &outZeInfoKernelSections, ConstStringRef context, std::string &outWarning); +DecodeError validateZeInfoKernelSectionsCount(const ZeInfoKernelSections &outZeInfoKernelSections, std::string &outErrReason, std::string &outWarning); +DecodeError readZeInfoExecutionEnvironment(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &node, + NEO::Elf::ZebinKernelMetadata::Types::Kernel::ExecutionEnv::ExecutionEnvBaseT &outExecEnv, + ConstStringRef context, std::string &outErrReason, std::string &outWarning); +bool readEnumChecked(const Yaml::Token *token, NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgType &out, + ConstStringRef context, std::string &outErrReason); +bool readEnumChecked(const Yaml::Token *token, NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingMode &out, + ConstStringRef context, std::string &outErrReason); +bool readEnumChecked(const Yaml::Token *token, NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpace &out, + ConstStringRef context, std::string &outErrReason); +bool readEnumChecked(const Yaml::Token *token, NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessType &out, + ConstStringRef context, std::string &outErrReason); + +using ZeInfoPerThreadPayloadArguments = StackVec; +DecodeError readZeInfoPerThreadPayloadArguments(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &node, + ZeInfoPerThreadPayloadArguments &outPerThreadPayloadArguments, + ConstStringRef context, + std::string &outErrReason, std::string &outWarning); + +using ZeInfoPayloadArguments = StackVec; +DecodeError readZeInfoPayloadArguments(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &node, + ZeInfoPayloadArguments &ouPayloadArguments, + uint32_t &outMaxPayloadArgumentIndex, + ConstStringRef context, + std::string &outErrReason, std::string &outWarning); + +using ZeInfoBindingTableIndices = StackVec; +DecodeError readZeInfoBindingTableIndices(const NEO::Yaml::YamlParser &parser, const NEO::Yaml::Node &node, + ZeInfoBindingTableIndices &outBindingTableIndices, ZeInfoBindingTableIndices::value_type &outMaxBindingTableIndex, + ConstStringRef context, + std::string &outErrReason, std::string &outWarning); + +NEO::DecodeError populateArgDescriptor(const NEO::Elf::ZebinKernelMetadata::Types::Kernel::PerThreadPayloadArgument::PerThreadPayloadArgumentBaseT &src, NEO::KernelDescriptor &dst, + std::string &outErrReason, std::string &outWarning); + +NEO::DecodeError populateArgDescriptor(const NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::PayloadArgumentBaseT &src, NEO::KernelDescriptor &dst, uint32_t &crossThreadDataSize, + std::string &outErrReason, std::string &outWarning); + +NEO::DecodeError populateKernelDescriptor(NEO::ProgramInfo &dst, NEO::Elf::Elf &elf, NEO::ZebinSections &zebinSections, + NEO::Yaml::YamlParser &yamlParser, const NEO::Yaml::Node &kernelNd, std::string &outErrReason, std::string &outWarning); +} // namespace NEO diff --git a/shared/source/kernel/kernel_descriptor.h b/shared/source/kernel/kernel_descriptor.h index 764456fd28..7c14740c3b 100644 --- a/shared/source/kernel/kernel_descriptor.h +++ b/shared/source/kernel/kernel_descriptor.h @@ -168,6 +168,8 @@ struct KernelDescriptor final { std::unique_ptr debugData; const void *igcInfoForGtpin = nullptr; } external; + + std::vector generatedHeaps; }; } // namespace NEO diff --git a/shared/source/utilities/const_stringref.h b/shared/source/utilities/const_stringref.h index efaaa109b7..fdeb8f5c71 100644 --- a/shared/source/utilities/const_stringref.h +++ b/shared/source/utilities/const_stringref.h @@ -137,6 +137,17 @@ class ConstStringRef { return false; } + constexpr bool startsWith(const char *subString) const noexcept { + const char *findEnd = ptr + len; + const char *lhs = ptr; + const char *rhs = subString; + while ((lhs < findEnd) && (*lhs == *rhs) && ('\0' != *rhs)) { + ++lhs; + ++rhs; + } + return ('\0' == *rhs); + } + protected: ConstStringRef(std::nullptr_t) = delete; @@ -159,7 +170,8 @@ constexpr bool equals(const ConstStringRef &lhs, const ConstStringRef &rhs) { } constexpr bool equals(const ConstStringRef &lhs, const char *rhs) { - for (size_t i = 0, e = lhs.size(); i < e; ++i) { + size_t i = 0; + for (size_t e = lhs.size(); i < e; ++i) { if (lhs[i] != rhs[i]) { return false; } @@ -168,7 +180,7 @@ constexpr bool equals(const ConstStringRef &lhs, const char *rhs) { } } - return true; + return (rhs[i] == '\0'); } constexpr bool operator==(const ConstStringRef &lhs, const ConstStringRef &rhs) { diff --git a/shared/source/utilities/stackvec.h b/shared/source/utilities/stackvec.h index 37b4bd50d3..cb79a8f21a 100644 --- a/shared/source/utilities/stackvec.h +++ b/shared/source/utilities/stackvec.h @@ -32,6 +32,7 @@ template ::SizeT> class StackVec { public: + using value_type = DataType; // NOLINT using SizeT = StackSizeT; using iterator = DataType *; using const_iterator = const DataType *; diff --git a/shared/test/unit_test/compiler_interface/compiler_interface_tests.cpp b/shared/test/unit_test/compiler_interface/compiler_interface_tests.cpp index ca6add64dd..0ce78058f5 100644 --- a/shared/test/unit_test/compiler_interface/compiler_interface_tests.cpp +++ b/shared/test/unit_test/compiler_interface/compiler_interface_tests.cpp @@ -689,6 +689,19 @@ TEST(LoadCompilerTest, whenEntrypointInterfaceIsNotCompatibleThenReturnFalseAndN EXPECT_EQ(nullptr, retMain.get()); } +TEST(LoadCompilerTest, GivenZebinIgnoreIcbeVersionDebugFlagThenIgnoreIgcsIcbeVersion) { + DebugManagerStateRestore dbgRestore; + DebugManager.flags.ZebinIgnoreIcbeVersion.set(true); + + MockCompilerEnableGuard mock; + std::unique_ptr retLib; + CIF::RAII::UPtr_t retMain; + bool retVal = loadCompiler("", retLib, retMain); + EXPECT_TRUE(retVal); + EXPECT_NE(nullptr, retLib.get()); + EXPECT_NE(nullptr, retMain.get()); +} + template struct MockCompilerDeviceCtx : DeviceCtxBase { TranslationCtx *CreateTranslationCtxImpl(CIF::Version_t ver, IGC::CodeType::CodeType_t inType, diff --git a/shared/test/unit_test/device_binary_format/CMakeLists.txt b/shared/test/unit_test/device_binary_format/CMakeLists.txt index 66835f74b1..27ff0609fa 100644 --- a/shared/test/unit_test/device_binary_format/CMakeLists.txt +++ b/shared/test/unit_test/device_binary_format/CMakeLists.txt @@ -13,11 +13,14 @@ target_sources(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_format_ar_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_format_ocl_elf_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_format_patchtokens_tests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_format_zebin_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device_binary_formats_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_decoder_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_dumper_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_tests.h ${CMAKE_CURRENT_SOURCE_DIR}/patchtokens_validator_tests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zebin_decoder_tests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zebin_tests.h ${CMAKE_CURRENT_SOURCE_DIR}/yaml/yaml_parser_tests.cpp ) diff --git a/shared/test/unit_test/device_binary_format/device_binary_format_ocl_elf_tests.cpp b/shared/test/unit_test/device_binary_format/device_binary_format_ocl_elf_tests.cpp index 56640b5a48..92125bf65f 100644 --- a/shared/test/unit_test/device_binary_format/device_binary_format_ocl_elf_tests.cpp +++ b/shared/test/unit_test/device_binary_format/device_binary_format_ocl_elf_tests.cpp @@ -215,7 +215,6 @@ TEST(UnpackSingleDeviceBinaryOclElf, GivenOclElfExecutableWhenPatchtokensBinaryI TEST(DecodeSingleDeviceBinaryOclElf, WhenUsedAsSingleDeviceBinaryThenDecodingFails) { PatchTokensTestData::ValidEmptyProgram patchtokensProgram; - ; NEO::Elf::ElfEncoder elfEnc64; elfEnc64.getElfFileHeader().type = NEO::Elf::ET_OPENCL_EXECUTABLE; diff --git a/shared/test/unit_test/device_binary_format/device_binary_format_patchtokens_tests.cpp b/shared/test/unit_test/device_binary_format/device_binary_format_patchtokens_tests.cpp index d98afe3b1b..d55d32bc22 100644 --- a/shared/test/unit_test/device_binary_format/device_binary_format_patchtokens_tests.cpp +++ b/shared/test/unit_test/device_binary_format/device_binary_format_patchtokens_tests.cpp @@ -16,7 +16,7 @@ TEST(IsDeviceBinaryFormatPatchtokens, GivenValidBinaryReturnTrue) { EXPECT_TRUE(NEO::isDeviceBinaryFormat(programTokens.storage)); } -TEST(IsDeviceBinaryFormatPatchtokens, GivenInvalidBinaryReturnTrue) { +TEST(IsDeviceBinaryFormatPatchtokens, GivenInvalidBinaryReturnFalse) { const uint8_t binary[] = "not_patchtokens"; EXPECT_FALSE(NEO::isDeviceBinaryFormat(binary)); } diff --git a/shared/test/unit_test/device_binary_format/device_binary_format_zebin_tests.cpp b/shared/test/unit_test/device_binary_format/device_binary_format_zebin_tests.cpp new file mode 100644 index 0000000000..d6f1556a2c --- /dev/null +++ b/shared/test/unit_test/device_binary_format/device_binary_format_zebin_tests.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/source/device_binary_format/device_binary_formats.h" +#include "shared/source/device_binary_format/elf/elf.h" +#include "shared/source/device_binary_format/elf/zebin_elf.h" +#include "shared/source/program/program_info.h" + +#include "test.h" + +TEST(IsDeviceBinaryFormatZebin, GivenValidBinaryReturnTrue) { + NEO::Elf::ElfFileHeader zebin; + zebin.type = NEO::Elf::ET_ZEBIN_EXE; + EXPECT_TRUE(NEO::isDeviceBinaryFormat(ArrayRef::fromAny(&zebin, 1U))); +} + +TEST(IsDeviceBinaryFormatZebin, GivenInvalidBinaryReturnFalse) { + NEO::Elf::ElfFileHeader someElf; + EXPECT_FALSE(NEO::isDeviceBinaryFormat(ArrayRef::fromAny(&someElf, 1U))); +} + +TEST(UnpackSingleDeviceBinaryZebin, WhenInvalidElfThenUnpackingFails) { + std::string unpackErrors; + std::string unpackWarnings; + auto unpackResult = NEO::unpackSingleDeviceBinary({}, "", {}, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Unknown, unpackResult.format); + EXPECT_TRUE(unpackResult.deviceBinary.empty()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_STREQ("Invalid or missing ELF header", unpackErrors.c_str()); +} + +TEST(UnpackSingleDeviceBinaryZebin, WhenUnhandledElfTypeThenUnpackingFails) { + NEO::Elf::ElfFileHeader someElf; + std::string unpackErrors; + std::string unpackWarnings; + auto unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&someElf, 1U), "", {}, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Unknown, unpackResult.format); + EXPECT_TRUE(unpackResult.deviceBinary.empty()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_STREQ("Unhandled elf type", unpackErrors.c_str()); +} + +TEST(UnpackSingleDeviceBinaryZebin, WhenValidBinaryAndMatchedWithRequestedTargetDeviceThenReturnSelf) { + NEO::Elf::ElfFileHeader zebin; + zebin.type = NEO::Elf::ET_ZEBIN_EXE; + zebin.machine = static_cast(IGFX_SKYLAKE); + NEO::TargetDevice targetDevice; + targetDevice.productFamily = static_cast(zebin.machine); + targetDevice.stepping = 0U; + targetDevice.maxPointerSizeInBytes = 8; + + std::string unpackErrors; + std::string unpackWarnings; + auto unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Zebin, unpackResult.format); + EXPECT_EQ(targetDevice.coreFamily, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(targetDevice.stepping, unpackResult.targetDevice.stepping); + EXPECT_EQ(targetDevice.maxPointerSizeInBytes, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_FALSE(unpackResult.deviceBinary.empty()); + EXPECT_EQ(reinterpret_cast(&zebin), unpackResult.deviceBinary.begin()); + EXPECT_EQ(sizeof(zebin), unpackResult.deviceBinary.size()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_TRUE(unpackErrors.empty()); + + zebin.machine = static_cast(IGFX_GEN9_CORE); + NEO::Elf::ZebinTargetFlags targetFlags; + targetDevice.productFamily = IGFX_UNKNOWN; + targetDevice.coreFamily = static_cast(zebin.machine); + targetFlags.machineEntryUsesGfxCoreInsteadOfProductFamily = true; + zebin.flags = targetFlags.packed; + unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Zebin, unpackResult.format); + EXPECT_EQ(targetDevice.coreFamily, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(targetDevice.stepping, unpackResult.targetDevice.stepping); + EXPECT_EQ(targetDevice.maxPointerSizeInBytes, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_FALSE(unpackResult.deviceBinary.empty()); + EXPECT_EQ(reinterpret_cast(&zebin), unpackResult.deviceBinary.begin()); + EXPECT_EQ(sizeof(zebin), unpackResult.deviceBinary.size()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_TRUE(unpackErrors.empty()); +} + +TEST(UnpackSingleDeviceBinaryZebin, WhenValidBinaryForDifferentDeviceThenUnpackingFails) { + NEO::Elf::ElfFileHeader zebin; + zebin.type = NEO::Elf::ET_ZEBIN_EXE; + zebin.machine = static_cast(IGFX_SKYLAKE); + NEO::TargetDevice targetDevice; + targetDevice.productFamily = static_cast(zebin.machine + 1); + targetDevice.stepping = 0U; + targetDevice.maxPointerSizeInBytes = 8U; + + std::string unpackErrors; + std::string unpackWarnings; + auto unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Unknown, unpackResult.format); + EXPECT_EQ(IGFX_UNKNOWN_CORE, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(0U, unpackResult.targetDevice.stepping); + EXPECT_EQ(4U, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_TRUE(unpackResult.deviceBinary.empty()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_FALSE(unpackErrors.empty()); + EXPECT_STREQ("Unhandled target device", unpackErrors.c_str()); + + zebin.machine = static_cast(IGFX_GEN9_CORE); + NEO::Elf::ZebinTargetFlags targetFlags; + targetDevice.productFamily = IGFX_UNKNOWN; + targetDevice.coreFamily = static_cast(zebin.machine + 1); + targetFlags.machineEntryUsesGfxCoreInsteadOfProductFamily = true; + zebin.flags = targetFlags.packed; + unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Unknown, unpackResult.format); + EXPECT_EQ(IGFX_UNKNOWN_CORE, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(0U, unpackResult.targetDevice.stepping); + EXPECT_EQ(4U, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_TRUE(unpackResult.deviceBinary.empty()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_FALSE(unpackErrors.empty()); + EXPECT_STREQ("Unhandled target device", unpackErrors.c_str()); +} + +TEST(UnpackSingleDeviceBinaryZebin, WhenValidBinaryWithUnsupportedPointerSizeThenUnpackingFails) { + NEO::Elf::ElfFileHeader zebin; + zebin.type = NEO::Elf::ET_ZEBIN_EXE; + zebin.machine = IGFX_SKYLAKE; + NEO::TargetDevice targetDevice; + targetDevice.productFamily = static_cast(zebin.machine); + targetDevice.stepping = 0U; + targetDevice.maxPointerSizeInBytes = 4U; + + std::string unpackErrors; + std::string unpackWarnings; + auto unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Unknown, unpackResult.format); + EXPECT_EQ(IGFX_UNKNOWN_CORE, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(0U, unpackResult.targetDevice.stepping); + EXPECT_EQ(4U, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_TRUE(unpackResult.deviceBinary.empty()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_FALSE(unpackErrors.empty()); + EXPECT_STREQ("Unhandled target device", unpackErrors.c_str()); +} + +TEST(UnpackSingleDeviceBinaryZebin, WhenNotRequestedThenDontValidateDeviceRevision) { + NEO::Elf::ElfFileHeader zebin; + zebin.type = NEO::Elf::ET_ZEBIN_EXE; + zebin.machine = IGFX_SKYLAKE; + NEO::TargetDevice targetDevice; + targetDevice.productFamily = static_cast(zebin.machine); + targetDevice.stepping = 0U; + targetDevice.maxPointerSizeInBytes = 8U; + + std::string unpackErrors; + std::string unpackWarnings; + NEO::Elf::ZebinTargetFlags targetFlags; + targetFlags.validateRevisionId = false; + targetFlags.minHwRevisionId = 5; + targetFlags.maxHwRevisionId = 7; + zebin.flags = targetFlags.packed; + auto unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Zebin, unpackResult.format); + EXPECT_EQ(targetDevice.coreFamily, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(targetDevice.stepping, unpackResult.targetDevice.stepping); + EXPECT_EQ(targetDevice.maxPointerSizeInBytes, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_FALSE(unpackResult.deviceBinary.empty()); + EXPECT_EQ(reinterpret_cast(&zebin), unpackResult.deviceBinary.begin()); + EXPECT_EQ(sizeof(zebin), unpackResult.deviceBinary.size()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_TRUE(unpackErrors.empty()); + + targetDevice.stepping = 8U; + unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Zebin, unpackResult.format); + EXPECT_EQ(targetDevice.coreFamily, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(targetDevice.stepping, unpackResult.targetDevice.stepping); + EXPECT_EQ(targetDevice.maxPointerSizeInBytes, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_FALSE(unpackResult.deviceBinary.empty()); + EXPECT_EQ(reinterpret_cast(&zebin), unpackResult.deviceBinary.begin()); + EXPECT_EQ(sizeof(zebin), unpackResult.deviceBinary.size()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_TRUE(unpackErrors.empty()); +} + +TEST(UnpackSingleDeviceBinaryZebin, WhenRequestedThenValidateRevision) { + NEO::Elf::ElfFileHeader zebin; + zebin.type = NEO::Elf::ET_ZEBIN_EXE; + zebin.machine = IGFX_SKYLAKE; + NEO::TargetDevice targetDevice; + targetDevice.productFamily = static_cast(zebin.machine); + targetDevice.stepping = 0U; + targetDevice.maxPointerSizeInBytes = 8U; + + std::string unpackErrors; + std::string unpackWarnings; + NEO::Elf::ZebinTargetFlags targetFlags; + targetFlags.validateRevisionId = true; + targetFlags.minHwRevisionId = 5; + targetFlags.maxHwRevisionId = 7; + zebin.flags = targetFlags.packed; + auto unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Unknown, unpackResult.format); + EXPECT_EQ(IGFX_UNKNOWN_CORE, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(0U, unpackResult.targetDevice.stepping); + EXPECT_EQ(4U, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_TRUE(unpackResult.deviceBinary.empty()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_FALSE(unpackErrors.empty()); + EXPECT_STREQ("Unhandled target device", unpackErrors.c_str()); + + targetDevice.stepping = 8U; + unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Unknown, unpackResult.format); + EXPECT_EQ(IGFX_UNKNOWN_CORE, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(0U, unpackResult.targetDevice.stepping); + EXPECT_EQ(4U, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_TRUE(unpackResult.deviceBinary.empty()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_FALSE(unpackErrors.empty()); + EXPECT_STREQ("Unhandled target device", unpackErrors.c_str()); + + unpackErrors.clear(); + targetDevice.stepping = 5U; + unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Zebin, unpackResult.format); + EXPECT_EQ(targetDevice.coreFamily, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(targetDevice.stepping, unpackResult.targetDevice.stepping); + EXPECT_EQ(targetDevice.maxPointerSizeInBytes, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_FALSE(unpackResult.deviceBinary.empty()); + EXPECT_EQ(reinterpret_cast(&zebin), unpackResult.deviceBinary.begin()); + EXPECT_EQ(sizeof(zebin), unpackResult.deviceBinary.size()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_TRUE(unpackErrors.empty()); + + targetDevice.stepping = 6U; + unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Zebin, unpackResult.format); + EXPECT_EQ(targetDevice.coreFamily, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(targetDevice.stepping, unpackResult.targetDevice.stepping); + EXPECT_EQ(targetDevice.maxPointerSizeInBytes, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_FALSE(unpackResult.deviceBinary.empty()); + EXPECT_EQ(reinterpret_cast(&zebin), unpackResult.deviceBinary.begin()); + EXPECT_EQ(sizeof(zebin), unpackResult.deviceBinary.size()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_TRUE(unpackErrors.empty()); + + targetDevice.stepping = 7U; + unpackResult = NEO::unpackSingleDeviceBinary(ArrayRef::fromAny(&zebin, 1U), "", targetDevice, unpackErrors, unpackWarnings); + EXPECT_EQ(NEO::DeviceBinaryFormat::Zebin, unpackResult.format); + EXPECT_EQ(targetDevice.coreFamily, unpackResult.targetDevice.coreFamily); + EXPECT_EQ(targetDevice.stepping, unpackResult.targetDevice.stepping); + EXPECT_EQ(targetDevice.maxPointerSizeInBytes, unpackResult.targetDevice.maxPointerSizeInBytes); + EXPECT_FALSE(unpackResult.deviceBinary.empty()); + EXPECT_EQ(reinterpret_cast(&zebin), unpackResult.deviceBinary.begin()); + EXPECT_EQ(sizeof(zebin), unpackResult.deviceBinary.size()); + EXPECT_TRUE(unpackResult.debugData.empty()); + EXPECT_TRUE(unpackResult.intermediateRepresentation.empty()); + EXPECT_TRUE(unpackResult.buildOptions.empty()); + EXPECT_TRUE(unpackWarnings.empty()); + EXPECT_TRUE(unpackErrors.empty()); +} diff --git a/shared/test/unit_test/device_binary_format/device_binary_formats_tests.cpp b/shared/test/unit_test/device_binary_format/device_binary_formats_tests.cpp index 3b9814c42f..8f1e64b6ba 100644 --- a/shared/test/unit_test/device_binary_format/device_binary_formats_tests.cpp +++ b/shared/test/unit_test/device_binary_format/device_binary_formats_tests.cpp @@ -11,6 +11,7 @@ #include "shared/source/device_binary_format/elf/ocl_elf.h" #include "shared/source/program/program_info.h" #include "shared/test/unit_test/device_binary_format/patchtokens_tests.h" +#include "shared/test/unit_test/device_binary_format/zebin_tests.h" #include "test.h" @@ -41,6 +42,11 @@ TEST(IsAnyDeviceBinaryFormat, GivenArFormatThenReturnsTrue) { EXPECT_TRUE(NEO::isAnyDeviceBinaryFormat(ArrayRef::fromAny(NEO::Ar::arMagic.begin(), NEO::Ar::arMagic.size()))); } +TEST(IsAnyDeviceBinaryFormat, GivenZebinFormatThenReturnsTrue) { + ZebinTestData::ValidEmptyProgram zebinProgram; + EXPECT_TRUE(NEO::isAnyDeviceBinaryFormat(zebinProgram.storage)); +} + TEST(UnpackSingleDeviceBinary, GivenUnknownBinaryThenReturnError) { const uint8_t data[] = "none of known formats"; auto requestedProductAbbreviation = "unk"; @@ -153,6 +159,30 @@ TEST(UnpackSingleDeviceBinary, GivenArBinaryWithOclElfThenReturnPatchtokensBinar EXPECT_EQ(0, memcmp(patchtokensProgram.storage.data(), unpacked.deviceBinary.begin(), unpacked.deviceBinary.size())); } +TEST(UnpackSingleDeviceBinary, GivenZebinThenReturnSelf) { + ZebinTestData::ValidEmptyProgram zebinProgram; + auto requestedProductAbbreviation = "unk"; + NEO::TargetDevice requestedTargetDevice; + requestedTargetDevice.productFamily = static_cast(zebinProgram.elfHeader->machine); + requestedTargetDevice.stepping = 0U; + requestedTargetDevice.maxPointerSizeInBytes = 8U; + std::string outErrors; + std::string outWarnings; + auto unpacked = NEO::unpackSingleDeviceBinary(zebinProgram.storage, requestedProductAbbreviation, requestedTargetDevice, outErrors, outWarnings); + EXPECT_TRUE(unpacked.buildOptions.empty()); + EXPECT_TRUE(unpacked.debugData.empty()); + EXPECT_FALSE(unpacked.deviceBinary.empty()); + EXPECT_EQ(zebinProgram.storage.data(), unpacked.deviceBinary.begin()); + EXPECT_EQ(zebinProgram.storage.size(), unpacked.deviceBinary.size()); + EXPECT_TRUE(unpacked.intermediateRepresentation.empty()); + EXPECT_EQ(NEO::DeviceBinaryFormat::Zebin, unpacked.format); + EXPECT_EQ(requestedTargetDevice.coreFamily, unpacked.targetDevice.coreFamily); + EXPECT_EQ(requestedTargetDevice.stepping, unpacked.targetDevice.stepping); + EXPECT_EQ(8U, unpacked.targetDevice.maxPointerSizeInBytes); + EXPECT_TRUE(outWarnings.empty()); + EXPECT_TRUE(outErrors.empty()); +} + TEST(IsAnyPackedDeviceBinaryFormat, GivenUnknownFormatThenReturnFalse) { const uint8_t data[] = "none of known formats"; EXPECT_FALSE(NEO::isAnyPackedDeviceBinaryFormat(data)); @@ -173,6 +203,11 @@ TEST(IsAnyPackedDeviceBinaryFormat, GivenArFormatThenReturnsTrue) { EXPECT_TRUE(NEO::isAnyPackedDeviceBinaryFormat(ArrayRef::fromAny(NEO::Ar::arMagic.begin(), NEO::Ar::arMagic.size()))); } +TEST(IsAnyPackedDeviceBinaryFormat, GivenZebinFormatThenReturnsTrue) { + ZebinTestData::ValidEmptyProgram zebinProgram; + EXPECT_TRUE(NEO::isAnyPackedDeviceBinaryFormat(zebinProgram.storage)); +} + TEST(IsAnySingleDeviceBinaryFormat, GivenUnknownFormatThenReturnFalse) { const uint8_t data[] = "none of known formats"; EXPECT_FALSE(NEO::isAnySingleDeviceBinaryFormat(data)); @@ -193,6 +228,11 @@ TEST(IsAnySingleDeviceBinaryFormat, GivenArFormatThenReturnsFalse) { EXPECT_FALSE(NEO::isAnySingleDeviceBinaryFormat(ArrayRef::fromAny(NEO::Ar::arMagic.begin(), NEO::Ar::arMagic.size()))); } +TEST(IsAnySingleDeviceBinaryFormat, GivenZebinFormatThenReturnsTrue) { + ZebinTestData::ValidEmptyProgram zebinProgram; + EXPECT_TRUE(NEO::isAnySingleDeviceBinaryFormat(zebinProgram.storage)); +} + TEST(DecodeSingleDeviceBinary, GivenUnknownFormatThenReturnFalse) { const uint8_t data[] = "none of known formats"; NEO::ProgramInfo programInfo; @@ -225,6 +265,22 @@ TEST(DecodeSingleDeviceBinary, GivenPatchTokensFormatThenDecodingSucceeds) { EXPECT_TRUE(decodeErrors.empty()); } +TEST(DecodeSingleDeviceBinary, GivenZebinFormatThenDecodingSucceeds) { + ZebinTestData::ValidEmptyProgram zebinElf; + NEO::ProgramInfo programInfo; + std::string decodeErrors; + std::string decodeWarnings; + NEO::SingleDeviceBinary bin; + bin.deviceBinary = zebinElf.storage; + NEO::DecodeError status; + NEO::DeviceBinaryFormat format; + std::tie(status, format) = NEO::decodeSingleDeviceBinary(programInfo, bin, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::Success, status); + EXPECT_EQ(NEO::DeviceBinaryFormat::Zebin, format); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_TRUE(decodeErrors.empty()) << decodeErrors; +} + TEST(DecodeSingleDeviceBinary, GivenOclElfFormatThenDecodingFails) { PatchTokensTestData::ValidEmptyProgram patchtokensProgram; @@ -274,3 +330,20 @@ TEST(PackDeviceBinary, GivenRequestToPackThenUsesOclElfFormat) { EXPECT_TRUE(packWarnings.empty()); EXPECT_TRUE(NEO::isDeviceBinaryFormat(packed)); } + +TEST(PackDeviceBinary, GivenRequestToPackWhenFormatIsAlreadyPackedThenReturnsInput) { + NEO::SingleDeviceBinary deviceBinary; + + std::string packErrors; + std::string packWarnings; + auto packed = NEO::packDeviceBinary(deviceBinary, packErrors, packWarnings); + EXPECT_TRUE(packErrors.empty()); + EXPECT_TRUE(packWarnings.empty()); + EXPECT_TRUE(NEO::isDeviceBinaryFormat(packed)); + deviceBinary.deviceBinary = packed; + auto packed2 = NEO::packDeviceBinary(deviceBinary, packErrors, packWarnings); + EXPECT_TRUE(packErrors.empty()); + EXPECT_TRUE(packWarnings.empty()); + EXPECT_TRUE(NEO::isDeviceBinaryFormat(packed)); + EXPECT_EQ(packed, packed2); +} diff --git a/shared/test/unit_test/device_binary_format/yaml/yaml_parser_tests.cpp b/shared/test/unit_test/device_binary_format/yaml/yaml_parser_tests.cpp index cf04f8fc27..d5ad0634a2 100644 --- a/shared/test/unit_test/device_binary_format/yaml/yaml_parser_tests.cpp +++ b/shared/test/unit_test/device_binary_format/yaml/yaml_parser_tests.cpp @@ -2060,6 +2060,21 @@ TEST(YamlParser, GivenNodeWhenCreateChildrenRangeIsCalledThenCreatesProperRange) EXPECT_EQ(childrenRange.end(), it); } +TEST(YamlParser, GivenNodeWithoutChildrenWhenCreateChildrenRangeIsCalledThenCreatesEmptyRange) { + ConstStringRef yaml = "- a"; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(success); + auto root = parser.getRoot(); + ASSERT_EQ(1U, root->numChildren); + auto first = parser.createChildrenRange(*root).begin(); + EXPECT_EQ(0U, first->numChildren); + auto range = parser.createChildrenRange(*first); + EXPECT_EQ(range.end(), range.begin()); +} + TEST(YamlParser, WhenBuildDebugNodesIsCalledThenBuildsDebugFriendlyRepresentation) { ConstStringRef yaml = R"===( @@ -2170,7 +2185,7 @@ banana : 5 EXPECT_STREQ("5", parser.readValueNoQuotes(*banana).str().c_str()); } -TEST(YamlParserReadValueCheckedUint64, GivenNodeWithoutASingleValueThenReturnFalse) { +TEST(YamlParserReadValueCheckedInt64, GivenNodeWithoutASingleValueThenReturnFalse) { ConstStringRef yaml = R"===( list : - a @@ -2183,12 +2198,12 @@ list : EXPECT_TRUE(success); auto notIntNode = parser.getChild(*parser.getRoot(), "list"); ASSERT_NE(notIntNode, nullptr); - uint64_t readUint64 = 0; - auto readSuccess = parser.readValueChecked(*notIntNode, readUint64); + int64_t readInt64 = 0; + auto readSuccess = parser.readValueChecked(*notIntNode, readInt64); EXPECT_FALSE(readSuccess); } -TEST(YamlParserReadValueCheckedUint64, GivenNonIntegerValueThenReturnFalse) { +TEST(YamlParserReadValueCheckedint64, GivenNonIntegerValueThenReturnFalse) { ConstStringRef yaml = "not_integer : five"; std::string parserErrors; std::string parserWarnings; @@ -2197,25 +2212,163 @@ TEST(YamlParserReadValueCheckedUint64, GivenNonIntegerValueThenReturnFalse) { EXPECT_TRUE(success); auto notIntNode = parser.getChild(*parser.getRoot(), "not_integer"); ASSERT_NE(notIntNode, nullptr); - uint64_t readUint64 = 0; - auto readSuccess = parser.readValueChecked(*notIntNode, readUint64); + int64_t readInt64 = 0; + auto readSuccess = parser.readValueChecked(*notIntNode, readInt64); + EXPECT_FALSE(readSuccess); +} + +TEST(YamlParserReadValueCheckedInt64, GivenIntegerThenParsesItCorrectly) { + ConstStringRef yaml = "positive : 9223372036854775807\nnegative : -9223372036854775807"; + int64_t expectedInt64 = 9223372036854775807; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(success); + auto positiveNode = parser.getChild(*parser.getRoot(), "positive"); + auto negativeNode = parser.getChild(*parser.getRoot(), "negative"); + ASSERT_NE(positiveNode, nullptr); + ASSERT_NE(negativeNode, nullptr); + int64_t readPositive = 0; + int64_t readNegative = 0; + auto readSuccess = parser.readValueChecked(*positiveNode, readPositive); + EXPECT_TRUE(readSuccess); + EXPECT_EQ(expectedInt64, readPositive); + readSuccess = parser.readValueChecked(*negativeNode, readNegative); + EXPECT_TRUE(readSuccess); + EXPECT_EQ(-expectedInt64, readNegative); +} + +TEST(YamlParserReadValueCheckedInt32, GivenIntegerThenParsesItCorrectly) { + ConstStringRef yaml = R"===( +positive64 : 9223372036854775807 +negative64 : -9223372036854775807 +positive32 : 2147483647 +negative32 : -2147483647 +)==="; + int32_t expectedInt32 = 2147483647; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(success); + auto positive32Node = parser.getChild(*parser.getRoot(), "positive32"); + auto negative32Node = parser.getChild(*parser.getRoot(), "negative32"); + ASSERT_NE(positive32Node, nullptr); + ASSERT_NE(negative32Node, nullptr); + int32_t readPositive = 0; + int32_t readNegative = 0; + auto readSuccess = parser.readValueChecked(*positive32Node, readPositive); + EXPECT_TRUE(readSuccess); + EXPECT_EQ(expectedInt32, readPositive); + readSuccess = parser.readValueChecked(*negative32Node, readNegative); + EXPECT_TRUE(readSuccess); + EXPECT_EQ(-expectedInt32, readNegative); + + auto positive64Node = parser.getChild(*parser.getRoot(), "positive64"); + auto negative64Node = parser.getChild(*parser.getRoot(), "negative64"); + readSuccess = parser.readValueChecked(*positive64Node, readPositive); + EXPECT_FALSE(readSuccess); + readSuccess = parser.readValueChecked(*negative64Node, readNegative); + EXPECT_FALSE(readSuccess); +} + +TEST(YamlParserReadValueCheckedInt16, GivenIntegerThenParsesItCorrectly) { + ConstStringRef yaml = R"===( +positive32 : 2147483647 +negative32 : -2147483647 +positive16 : 32767 +negative16 : -32767 +)==="; + int16_t expectedInt16 = 32767; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(success); + auto positive16Node = parser.getChild(*parser.getRoot(), "positive16"); + auto negative16Node = parser.getChild(*parser.getRoot(), "negative16"); + ASSERT_NE(positive16Node, nullptr); + ASSERT_NE(negative16Node, nullptr); + int16_t readPositive = 0; + int16_t readNegative = 0; + auto readSuccess = parser.readValueChecked(*positive16Node, readPositive); + EXPECT_TRUE(readSuccess); + EXPECT_EQ(expectedInt16, readPositive); + readSuccess = parser.readValueChecked(*negative16Node, readNegative); + EXPECT_TRUE(readSuccess); + EXPECT_EQ(-expectedInt16, readNegative); + + auto positive32Node = parser.getChild(*parser.getRoot(), "positive32"); + auto negative32Node = parser.getChild(*parser.getRoot(), "negative32"); + readSuccess = parser.readValueChecked(*positive32Node, readPositive); + EXPECT_FALSE(readSuccess); + readSuccess = parser.readValueChecked(*negative32Node, readNegative); + EXPECT_FALSE(readSuccess); +} + +TEST(YamlParserReadValueCheckedInt8, GivenIntegerThenParsesItCorrectly) { + ConstStringRef yaml = R"===( +positive16 : 65530 +negative16 : -65530 +positive8 : 127 +negative8 : -127 +)==="; + int8_t expectedInt8 = 127; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(success); + auto positive8Node = parser.getChild(*parser.getRoot(), "positive8"); + auto negative8Node = parser.getChild(*parser.getRoot(), "negative8"); + ASSERT_NE(positive8Node, nullptr); + ASSERT_NE(negative8Node, nullptr); + int8_t readPositive = 0; + int8_t readNegative = 0; + auto readSuccess = parser.readValueChecked(*positive8Node, readPositive); + EXPECT_TRUE(readSuccess); + EXPECT_EQ(expectedInt8, readPositive); + readSuccess = parser.readValueChecked(*negative8Node, readNegative); + EXPECT_TRUE(readSuccess); + EXPECT_EQ(-expectedInt8, readNegative); + + auto positive16Node = parser.getChild(*parser.getRoot(), "positive16"); + auto negative16Node = parser.getChild(*parser.getRoot(), "negative16"); + readSuccess = parser.readValueChecked(*positive16Node, readPositive); + EXPECT_FALSE(readSuccess); + readSuccess = parser.readValueChecked(*negative16Node, readNegative); EXPECT_FALSE(readSuccess); } TEST(YamlParserReadValueCheckedUint64, GivenIntegerThenParsesItCorrectly) { - ConstStringRef yaml = "integer64 : 6294967295"; + ConstStringRef yaml = "positive : 6294967295"; uint64_t expectedUint64 = 6294967295; std::string parserErrors; std::string parserWarnings; NEO::Yaml::YamlParser parser; bool success = parser.parse(yaml, parserErrors, parserWarnings); EXPECT_TRUE(success); + auto positiveNode = parser.getChild(*parser.getRoot(), "positive"); + ASSERT_NE(positiveNode, nullptr); + uint64_t readPositive = 0; + auto readSuccess = parser.readValueChecked(*positiveNode, readPositive); + EXPECT_TRUE(readSuccess); + EXPECT_EQ(expectedUint64, readPositive); +} + +TEST(YamlParserReadValueCheckedUint64, GivenNegativeIntegerThenFail) { + ConstStringRef yaml = "integer64 : -10"; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(success); auto int64Node = parser.getChild(*parser.getRoot(), "integer64"); ASSERT_NE(int64Node, nullptr); uint64_t readUint64 = 0; auto readSuccess = parser.readValueChecked(*int64Node, readUint64); - EXPECT_TRUE(readSuccess); - EXPECT_EQ(expectedUint64, readUint64); + EXPECT_FALSE(readSuccess); } TEST(YamlParserReadValueCheckedUint32, GivenIntegerThenParsesItCorrectly) { @@ -2244,6 +2397,20 @@ integer32 : 294967295 EXPECT_EQ(expectedUint32, readUint32); } +TEST(YamlParserReadValueCheckedUint32, GivenNegativeIntegerThenFail) { + ConstStringRef yaml = "integer32 : -10"; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(success); + auto int32Node = parser.getChild(*parser.getRoot(), "integer32"); + ASSERT_NE(int32Node, nullptr); + uint32_t readUint32 = 0; + auto readSuccess = parser.readValueChecked(*int32Node, readUint32); + EXPECT_FALSE(readSuccess); +} + TEST(YamlParserReadValueCheckedUint16, GivenIntegerThenParsesItCorrectly) { ConstStringRef yaml = R"===( integer32 : 294967295 @@ -2270,6 +2437,20 @@ integer16 : 65530 EXPECT_EQ(expectedUint16, readUint16); } +TEST(YamlParserReadValueCheckedUint16, GivenNegativeIntegerThenFail) { + ConstStringRef yaml = "integer16 : -10"; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(success); + auto int16Node = parser.getChild(*parser.getRoot(), "integer16"); + ASSERT_NE(int16Node, nullptr); + uint16_t readUint16 = 0; + auto readSuccess = parser.readValueChecked(*int16Node, readUint16); + EXPECT_FALSE(readSuccess); +} + TEST(YamlParserReadValueCheckedUint8, GivenIntegerThenParsesItCorrectly) { ConstStringRef yaml = R"===( integer16 : 65530 @@ -2296,6 +2477,20 @@ integer8 : 250 EXPECT_EQ(expectedUint8, readUint8); } +TEST(YamlParserReadValueCheckedUint8, GivenNegativeIntegerThenFail) { + ConstStringRef yaml = "integer8 : -10"; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(success); + auto int8Node = parser.getChild(*parser.getRoot(), "integer8"); + ASSERT_NE(int8Node, nullptr); + uint8_t readUint8 = 0; + auto readSuccess = parser.readValueChecked(*int8Node, readUint8); + EXPECT_FALSE(readSuccess); +} + TEST(YamlParserReadValueCheckedBool, GivenFlagsThenParsesThemCorrectly) { ConstStringRef yaml = R"===( ones_that_are_true : diff --git a/shared/test/unit_test/device_binary_format/zebin_decoder_tests.cpp b/shared/test/unit_test/device_binary_format/zebin_decoder_tests.cpp new file mode 100644 index 0000000000..c208cefaae --- /dev/null +++ b/shared/test/unit_test/device_binary_format/zebin_decoder_tests.cpp @@ -0,0 +1,2546 @@ +/* + * Copyright (C) 2019-2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#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/helpers/ptr_math.h" +#include "shared/source/program/program_info.h" +#include "shared/test/unit_test/device_binary_format/zebin_tests.h" +#include "shared/test/unit_test/helpers/debug_manager_state_restore.h" + +#include "opencl/source/program/kernel_info.h" +#include "test.h" + +#include + +TEST(ExtractZebinSections, WhenElfDoesNotContainValidStringSectionThenFail) { + NEO::Elf::Elf elf; + NEO::Elf::ElfFileHeader elfHeader; + elf.elfFileHeader = &elfHeader; + + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + elfHeader.shStrNdx = NEO::Elf::SHN_UNDEF; + elf.sectionHeaders.resize(1); + auto decodeError = NEO::extractZebinSections(elf, sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, decodeError); + EXPECT_TRUE(warnings.empty()) << warnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid or missing shStrNdx in elf header\n", errors.c_str()); + elf.sectionHeaders.clear(); + + errors.clear(); + warnings.clear(); + elfHeader.shStrNdx = elfHeader.shNum + 1; + decodeError = NEO::extractZebinSections(elf, sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, decodeError); + EXPECT_TRUE(warnings.empty()) << warnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid or missing shStrNdx in elf header\n", errors.c_str()); +} + +TEST(ExtractZebinSections, GivenUnknownElfSectionThenFail) { + NEO::Elf::ElfEncoder<> elfEncoder; + elfEncoder.appendSection(NEO::Elf::SHT_OPENCL_RESERVED_START, "someSection", std::string{}); + auto encodedElf = elfEncoder.encode(); + std::string elfDecodeErrors; + std::string elfDecodeWarnings; + auto decodedElf = NEO::Elf::decodeElf(encodedElf, elfDecodeErrors, elfDecodeWarnings); + + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + auto decodeError = NEO::extractZebinSections(decodedElf, sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, decodeError); + EXPECT_TRUE(warnings.empty()) << warnings; + auto expectedError = "DeviceBinaryFormat::Zebin : Unhandled ELF section header type : " + std::to_string(NEO::Elf::SHT_OPENCL_RESERVED_START) + "\n"; + EXPECT_STREQ(expectedError.c_str(), errors.c_str()); +} + +TEST(ExtractZebinSections, GivenUnknownElfProgbitsSectionThenFail) { + NEO::Elf::ElfEncoder<> elfEncoder; + elfEncoder.appendSection(NEO::Elf::SHT_PROGBITS, "someSection", std::string{}); + auto encodedElf = elfEncoder.encode(); + std::string elfDecodeErrors; + std::string elfDecodeWarnings; + auto decodedElf = NEO::Elf::decodeElf(encodedElf, elfDecodeErrors, elfDecodeWarnings); + + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + auto decodeError = NEO::extractZebinSections(decodedElf, sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, decodeError); + EXPECT_TRUE(warnings.empty()) << warnings; + auto expectedError = "DeviceBinaryFormat::Zebin : Unhandled SHT_PROGBITS section : someSection currently supports only : .text.KERNEL_NAME, .data.const and .data.global.\n"; + EXPECT_STREQ(expectedError, errors.c_str()); +} + +TEST(ExtractZebinSections, GivenKnownSectionsThenCapturesThemProperly) { + NEO::Elf::ElfEncoder<> elfEncoder; + elfEncoder.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "someKernel", std::string{}); + elfEncoder.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "someOtherKernel", std::string{}); + elfEncoder.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::dataConst, std::string{}); + elfEncoder.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::dataGlobal, std::string{}); + elfEncoder.appendSection(NEO::Elf::SHT_SYMTAB, NEO::Elf::SectionsNamesZebin::symtab, std::string{}); + elfEncoder.appendSection(NEO::Elf::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, std::string{}); + elfEncoder.appendSection(NEO::Elf::SHT_ZEBIN_SPIRV, NEO::Elf::SectionsNamesZebin::spv, std::string{}); + auto encodedElf = elfEncoder.encode(); + std::string elfDecodeErrors; + std::string elfDecodeWarnings; + auto decodedElf = NEO::Elf::decodeElf(encodedElf, elfDecodeErrors, elfDecodeWarnings); + + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + auto decodeError = NEO::extractZebinSections(decodedElf, sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, decodeError); + EXPECT_TRUE(warnings.empty()) << warnings; + EXPECT_TRUE(errors.empty()) << errors; + + ASSERT_EQ(2U, sections.textKernelSections.size()); + ASSERT_EQ(1U, sections.globalDataSections.size()); + ASSERT_EQ(1U, sections.constDataSections.size()); + ASSERT_EQ(1U, sections.zeInfoSections.size()); + ASSERT_EQ(1U, sections.symtabSections.size()); + ASSERT_EQ(1U, sections.spirvSections.size()); + + auto stringSection = decodedElf.sectionHeaders[decodedElf.elfFileHeader->shStrNdx]; + const char *strings = stringSection.data.toArrayRef().begin(); + EXPECT_STREQ((NEO::Elf::SectionsNamesZebin::textPrefix.str() + "someKernel").c_str(), strings + sections.textKernelSections[0]->header->name); + EXPECT_STREQ((NEO::Elf::SectionsNamesZebin::textPrefix.str() + "someOtherKernel").c_str(), strings + sections.textKernelSections[1]->header->name); + EXPECT_STREQ(NEO::Elf::SectionsNamesZebin::dataGlobal.data(), strings + sections.globalDataSections[0]->header->name); + EXPECT_STREQ(NEO::Elf::SectionsNamesZebin::dataConst.data(), strings + sections.constDataSections[0]->header->name); + EXPECT_STREQ(NEO::Elf::SectionsNamesZebin::zeInfo.data(), strings + sections.zeInfoSections[0]->header->name); + EXPECT_STREQ(NEO::Elf::SectionsNamesZebin::symtab.data(), strings + sections.symtabSections[0]->header->name); + EXPECT_STREQ(NEO::Elf::SectionsNamesZebin::spv.data(), strings + sections.spirvSections[0]->header->name); +} + +TEST(ExtractZebinSections, GivenMispelledConstDataSectionThenAllowItButEmitError) { + NEO::Elf::ElfEncoder<> elfEncoder; + elfEncoder.appendSection(NEO::Elf::SHT_PROGBITS, ".data.global_const", std::string{}); + auto encodedElf = elfEncoder.encode(); + std::string elfDecodeErrors; + std::string elfDecodeWarnings; + auto decodedElf = NEO::Elf::decodeElf(encodedElf, elfDecodeErrors, elfDecodeWarnings); + + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + auto decodeError = NEO::extractZebinSections(decodedElf, sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, decodeError); + EXPECT_STREQ("Misspelled section name : .data.global_const, should be : .data.const\n", warnings.c_str()); + EXPECT_TRUE(errors.empty()) << errors; + + ASSERT_EQ(1U, sections.constDataSections.size()); + + auto stringSection = decodedElf.sectionHeaders[decodedElf.elfFileHeader->shStrNdx]; + const char *strings = stringSection.data.toArrayRef().begin(); + EXPECT_STREQ(".data.global_const", strings + sections.constDataSections[0]->header->name); +} + +TEST(ValidateZebinSectionsCount, GivenEmptyZebinThenReturnSuccess) { + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + auto err = NEO::validateZebinSectionsCount(sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZebinSectionsCount, GivenCorrectNumberOfSectionsThenReturnSuccess) { + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + sections.constDataSections.resize(1); + sections.globalDataSections.resize(1); + sections.spirvSections.resize(1); + sections.symtabSections.resize(1); + sections.textKernelSections.resize(10U); + sections.zeInfoSections.resize(1U); + auto err = NEO::validateZebinSectionsCount(sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZebinSectionsCount, GivenTwoConstDataSectionsThenFail) { + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + sections.constDataSections.resize(2); + auto err = NEO::validateZebinSectionsCount(sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected at most 1 of .data.const section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZebinSectionsCount, GivenTwoGlobalDataSectionsThenFail) { + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + sections.globalDataSections.resize(2); + auto err = NEO::validateZebinSectionsCount(sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected at most 1 of .data.global section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZebinSectionsCount, GivenTwoZeInfoSectionsThenFail) { + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + sections.zeInfoSections.resize(2); + auto err = NEO::validateZebinSectionsCount(sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected at most 1 of .ze_info section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZebinSectionsCount, GivenTwoSymtabSectionsThenFail) { + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + sections.symtabSections.resize(2); + auto err = NEO::validateZebinSectionsCount(sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected at most 1 of .symtab section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZebinSectionsCount, GivenTwoSpirvSectionsThenFail) { + NEO::ZebinSections sections; + std::string errors; + std::string warnings; + sections.spirvSections.resize(2); + auto err = NEO::validateZebinSectionsCount(sections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected at most 1 of .spv section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ExtractZeInfoKernelSections, GivenKnownSectionsThenCapturesThemProperly) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + execution_env: + actual_kernel_start_offset: 0 + payload_arguments: + - arg_type: global_id_offset + offset: 0 + size: 12 + per_thread_payload_arguments: + - arg_type: local_id + offset: 0 + size: 192 + binding_table_indices: + - bti_value: 0 + arg_index: 0 +... +)==="; + + NEO::ZeInfoKernelSections kernelSections; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(parserErrors.empty()) << parserErrors; + EXPECT_TRUE(parserWarnings.empty()) << parserWarnings; + ASSERT_TRUE(success); + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string warnings; + NEO::extractZeInfoKernelSections(parser, kernelNode, kernelSections, "some_kernel", warnings); + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_FALSE(kernelSections.nameNd.empty()); + ASSERT_FALSE(kernelSections.executionEnvNd.empty()); + ASSERT_FALSE(kernelSections.payloadArgumentsNd.empty()); + ASSERT_FALSE(kernelSections.bindingTableIndicesNd.empty()); + ASSERT_FALSE(kernelSections.perThreadPayloadArgumentsNd.empty()); + + EXPECT_EQ("name", parser.readKey(*kernelSections.nameNd[0])) << parser.readKey(*kernelSections.nameNd[0]).str(); + EXPECT_EQ("execution_env", parser.readKey(*kernelSections.executionEnvNd[0])) << parser.readKey(*kernelSections.executionEnvNd[0]).str(); + EXPECT_EQ("payload_arguments", parser.readKey(*kernelSections.payloadArgumentsNd[0])) << parser.readKey(*kernelSections.payloadArgumentsNd[0]).str(); + EXPECT_EQ("per_thread_payload_arguments", parser.readKey(*kernelSections.perThreadPayloadArgumentsNd[0])) << parser.readKey(*kernelSections.perThreadPayloadArgumentsNd[0]).str(); + EXPECT_EQ("binding_table_indices", parser.readKey(*kernelSections.bindingTableIndicesNd[0])) << parser.readKey(*kernelSections.bindingTableIndicesNd[0]).str(); +} + +TEST(ExtractZeInfoKernelSections, GivenUnknownSectionThenEmitsAWarning) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + apple : + - red + - green +... +)==="; + + NEO::ZeInfoKernelSections kernelSections; + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + EXPECT_TRUE(parserErrors.empty()) << parserErrors; + EXPECT_TRUE(parserWarnings.empty()) << parserWarnings; + ASSERT_TRUE(success); + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string warnings; + NEO::extractZeInfoKernelSections(parser, kernelNode, kernelSections, "some_kernel", warnings); + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unknown entry \"apple\" in context of : some_kernel\n", warnings.c_str()); + ASSERT_FALSE(kernelSections.nameNd.empty()); + EXPECT_EQ("name", parser.readKey(*kernelSections.nameNd[0])) << parser.readKey(*kernelSections.nameNd[0]).str(); +} + +TEST(ValidateZeInfoKernelSectionsCount, GivenCorrectNumberOfSectionsThenReturnSuccess) { + NEO::ZeInfoKernelSections kernelSections; + kernelSections.nameNd.resize(1); + kernelSections.executionEnvNd.resize(1); + kernelSections.bindingTableIndicesNd.resize(1); + kernelSections.payloadArgumentsNd.resize(1); + kernelSections.perThreadPayloadArgumentsNd.resize(1); + std::string errors; + std::string warnings; + auto err = NEO::validateZeInfoKernelSectionsCount(kernelSections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZeInfoKernelSectionsCount, WhenNameSectionIsMissingThenFail) { + NEO::ZeInfoKernelSections kernelSections; + std::string errors; + std::string warnings; + kernelSections.executionEnvNd.resize(1); + auto err = NEO::validateZeInfoKernelSectionsCount(kernelSections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected exactly 1 of name section, got : 0\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZeInfoKernelSectionsCount, WhenExecutionEnvSectionIsMissingThenFail) { + NEO::ZeInfoKernelSections kernelSections; + std::string errors; + std::string warnings; + kernelSections.nameNd.resize(1); + auto err = NEO::validateZeInfoKernelSectionsCount(kernelSections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected exactly 1 of execution_env section, got : 0\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZeInfoKernelSectionsCount, GivenTwoNameSectionsThenFail) { + NEO::ZeInfoKernelSections kernelSections; + std::string errors; + std::string warnings; + kernelSections.nameNd.resize(2); + kernelSections.executionEnvNd.resize(1); + auto err = NEO::validateZeInfoKernelSectionsCount(kernelSections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected exactly 1 of name section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZeInfoKernelSectionsCount, GivenTwoExecutionEnvSectionsThenFail) { + NEO::ZeInfoKernelSections kernelSections; + std::string errors; + std::string warnings; + kernelSections.nameNd.resize(1); + kernelSections.executionEnvNd.resize(2); + auto err = NEO::validateZeInfoKernelSectionsCount(kernelSections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected exactly 1 of execution_env section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZeInfoKernelSectionsCount, GivenTwoBindingTableIndicesSectionsThenFail) { + NEO::ZeInfoKernelSections kernelSections; + std::string errors; + std::string warnings; + kernelSections.nameNd.resize(1); + kernelSections.executionEnvNd.resize(1); + kernelSections.bindingTableIndicesNd.resize(2); + auto err = NEO::validateZeInfoKernelSectionsCount(kernelSections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected at most 1 of binding_table_indices section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZeInfoKernelSectionsCount, GivenTwoPayloadArgumentsSectionsThenFail) { + NEO::ZeInfoKernelSections kernelSections; + std::string errors; + std::string warnings; + kernelSections.nameNd.resize(1); + kernelSections.executionEnvNd.resize(1); + kernelSections.payloadArgumentsNd.resize(2); + auto err = NEO::validateZeInfoKernelSectionsCount(kernelSections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected at most 1 of payload_arguments section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ValidateZeInfoKernelSectionsCount, GivenTwoPerThreadPayloadArgumentsSectionsThenFail) { + NEO::ZeInfoKernelSections kernelSections; + std::string errors; + std::string warnings; + kernelSections.nameNd.resize(1); + kernelSections.executionEnvNd.resize(1); + kernelSections.perThreadPayloadArgumentsNd.resize(2); + auto err = NEO::validateZeInfoKernelSectionsCount(kernelSections, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected at most 1 of per_thread_payload_arguments section, got : 2\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(ReadZeInfoExecutionEnvironment, GivenValidYamlEntriesThenSetProperMembers) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + execution_env: + actual_kernel_start_offset : 5 + barrier_count : 7 + disable_mid_thread_preemption : true + grf_count : 13 + has_4gb_buffers : true + has_device_enqueue : true + has_fence_for_image_access : true + has_global_atomics : true + has_multi_scratch_spaces : true + has_no_stateless_write : true + hw_preemption_mode : 2 + offset_to_skip_per_thread_data_load : 23 + offset_to_skip_set_ffid_gp : 29 + required_sub_group_size : 16 + simd_size : 32 + slm_size : 1024 + subgroup_independent_forward_progress : true +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &execEnvNode = *parser.findNodeWithKeyDfs("execution_env"); + std::string errors; + std::string warnings; + NEO::Elf::ZebinKernelMetadata::Types::Kernel::ExecutionEnv::ExecutionEnvBaseT execEnv; + auto err = NEO::readZeInfoExecutionEnvironment(parser, execEnvNode, execEnv, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + EXPECT_EQ(5, execEnv.actualKernelStartOffset); + EXPECT_EQ(7, execEnv.barrierCount); + EXPECT_TRUE(execEnv.disableMidThreadPreemption); + EXPECT_EQ(13, execEnv.grfCount); + EXPECT_TRUE(execEnv.has4GBBuffers); + EXPECT_TRUE(execEnv.hasDeviceEnqueue); + EXPECT_TRUE(execEnv.hasFenceForImageAccess); + EXPECT_TRUE(execEnv.hasGlobalAtomics); + EXPECT_TRUE(execEnv.hasMultiScratchSpaces); + EXPECT_TRUE(execEnv.hasNoStatelessWrite); + EXPECT_EQ(2, execEnv.hwPreemptionMode); + EXPECT_EQ(23, execEnv.offsetToSkipPerThreadDataLoad); + EXPECT_EQ(29, execEnv.offsetToSkipSetFfidGp); + EXPECT_EQ(16, execEnv.requiredSubGroupSize); + EXPECT_EQ(32, execEnv.simdSize); + EXPECT_EQ(1024, execEnv.slmSize); + EXPECT_TRUE(execEnv.subgroupIndependentForwardProgress); +} + +TEST(ReadZeInfoExecutionEnvironment, GivenUnknownEntryThenEmmitsWarning) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + execution_env: + actual_kernel_start_offset : 17 + something_new : 36 +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &execEnvNode = *parser.findNodeWithKeyDfs("execution_env"); + std::string errors; + std::string warnings; + NEO::Elf::ZebinKernelMetadata::Types::Kernel::ExecutionEnv::ExecutionEnvBaseT execEnv; + auto err = NEO::readZeInfoExecutionEnvironment(parser, execEnvNode, execEnv, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unknown entry \"something_new\" in context of some_kernel\n", warnings.c_str()); + EXPECT_EQ(17, execEnv.actualKernelStartOffset); +} + +TEST(ReadZeInfoExecutionEnvironment, GivenInvalidValueForKnownEntryThenFails) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + execution_env: + actual_kernel_start_offset : true +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &execEnvNode = *parser.findNodeWithKeyDfs("execution_env"); + std::string errors; + std::string warnings; + NEO::Elf::ZebinKernelMetadata::Types::Kernel::ExecutionEnv::ExecutionEnvBaseT execEnv; + auto err = NEO::readZeInfoExecutionEnvironment(parser, execEnvNode, execEnv, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(warnings.empty()) << warnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : could not read actual_kernel_start_offset from : [true] in context of : some_kernel\n", errors.c_str()); +} + +TEST(ReadEnumCheckedArgType, GivenValidStringRepresentationThenParseItCorrectly) { + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::ArgType; + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PerThreadPayloadArgument::ArgType; + NEO::Yaml::Token tokPackedLocalIds(packedLocalIds, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokLocalId(localId, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokLocalSize(localSize, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokGroupSize(groupSize, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokGlobalIdOffset(globalIdOffset, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokPrivateBaseStateless(privateBaseStateless, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokArgByValue(argByvalue, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokArgByPointer(argBypointer, NEO::Yaml::Token::Token::LiteralString); + + using ArgType = NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgType; + ArgType enumPackedLocalIds, enumLocalId, enumLocalSize, enumGroupSize, enumGlobalIdOffset, + enumPrivateBaseStateless, enumArgByValue, enumArgByPointer; + std::string errors; + bool success; + + success = NEO::readEnumChecked(&tokPackedLocalIds, enumPackedLocalIds, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(ArgType::ArgTypePackedLocalIds, enumPackedLocalIds); + + success = NEO::readEnumChecked(&tokLocalId, enumLocalId, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(ArgType::ArgTypeLocalId, enumLocalId); + + success = NEO::readEnumChecked(&tokLocalSize, enumLocalSize, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(ArgType::ArgTypeLocalSize, enumLocalSize); + + success = NEO::readEnumChecked(&tokGroupSize, enumGroupSize, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(ArgType::ArgTypeGroupSize, enumGroupSize); + + success = NEO::readEnumChecked(&tokGlobalIdOffset, enumGlobalIdOffset, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(ArgType::ArgTypeGlobalIdOffset, enumGlobalIdOffset); + + success = NEO::readEnumChecked(&tokPrivateBaseStateless, enumPrivateBaseStateless, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(ArgType::ArgTypePrivateBaseStateless, enumPrivateBaseStateless); + + success = NEO::readEnumChecked(&tokArgByValue, enumArgByValue, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(ArgType::ArgTypeArgByvalue, enumArgByValue); + + success = NEO::readEnumChecked(&tokArgByPointer, enumArgByPointer, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(ArgType::ArgTypeArgBypointer, enumArgByPointer); +} + +TEST(ReadEnumCheckedArgType, GivenNullTokenThenFail) { + using ArgType = NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgType; + ArgType enumRepresentation; + std::string errors; + bool success; + + success = NEO::readEnumChecked(nullptr, enumRepresentation, "some_kernel", errors); + EXPECT_FALSE(success); +} + +TEST(ReadEnumCheckedArgType, GivenUnknownStringRepresentationThenFail) { + using ArgType = NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgType; + ArgType enumRepresentation; + std::string errors; + bool success; + + NEO::Yaml::Token someEntry("some_entry", NEO::Yaml::Token::Token::LiteralString); + success = NEO::readEnumChecked(&someEntry, enumRepresentation, "some_kernel", errors); + EXPECT_FALSE(success); + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unhandled \"some_entry\" argument type in context of some_kernel\n", errors.c_str()); +} + +TEST(ReadEnumCheckedMemoryAddressingMode, GivenValidStringRepresentationThenParseItCorrectly) { + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::MemoryAddressingMode; + + NEO::Yaml::Token tokStateless(stateless, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokStateful(stateful, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokBindless(bindless, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokSharedLocalMemory(sharedLocalMemory, NEO::Yaml::Token::Token::LiteralString); + + using MemoryAddressingMode = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingMode; + MemoryAddressingMode enumStateless, enumStateful, enumBindless, enumSharedLocalMemory; + std::string errors; + bool success; + + success = NEO::readEnumChecked(&tokStateless, enumStateless, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(MemoryAddressingMode::MemoryAddressingModeStateless, enumStateless); + + success = NEO::readEnumChecked(&tokStateful, enumStateful, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(MemoryAddressingMode::MemoryAddressingModeStateful, enumStateful); + + success = NEO::readEnumChecked(&tokBindless, enumBindless, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(MemoryAddressingMode::MemoryAddressingModeBindless, enumBindless); + + success = NEO::readEnumChecked(&tokSharedLocalMemory, enumSharedLocalMemory, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(MemoryAddressingMode::MemoryAddressingModeSharedLocalMemory, enumSharedLocalMemory); +} + +TEST(ReadEnumCheckedMemoryAddressingMode, GivenNullTokenThenFail) { + using MemoryAddressingMode = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingMode; + MemoryAddressingMode enumRepresentation; + std::string errors; + bool success; + + success = NEO::readEnumChecked(nullptr, enumRepresentation, "some_kernel", errors); + EXPECT_FALSE(success); +} + +TEST(ReadEnumCheckedMemoryAddressingMode, GivenUnknownStringRepresentationThenFail) { + using MemoryAddressingMode = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingMode; + MemoryAddressingMode enumRepresentation; + std::string errors; + bool success; + + NEO::Yaml::Token someEntry("some_entry", NEO::Yaml::Token::Token::LiteralString); + success = NEO::readEnumChecked(&someEntry, enumRepresentation, "some_kernel", errors); + EXPECT_FALSE(success); + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unhandled \"some_entry\" memory addressing mode in context of some_kernel\n", errors.c_str()); +} + +TEST(ReadEnumCheckedAddressSpace, GivenValidStringRepresentationThenParseItCorrectly) { + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::AddrSpace; + + NEO::Yaml::Token tokGlobal(global, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokLocal(local, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokConstant(constant, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokImage(image, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokSampler(sampler, NEO::Yaml::Token::Token::LiteralString); + + using AddressSpace = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpace; + AddressSpace enumGlobal, enumLocal, enumConstant, enumImage, enumSampler; + std::string errors; + bool success; + + success = NEO::readEnumChecked(&tokGlobal, enumGlobal, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(AddressSpace::AddressSpaceGlobal, enumGlobal); + + success = NEO::readEnumChecked(&tokLocal, enumLocal, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(AddressSpace::AddressSpaceLocal, enumLocal); + + success = NEO::readEnumChecked(&tokConstant, enumConstant, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(AddressSpace::AddressSpaceConstant, enumConstant); + + success = NEO::readEnumChecked(&tokImage, enumImage, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(AddressSpace::AddressSpaceImage, enumImage); + + success = NEO::readEnumChecked(&tokSampler, enumSampler, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(AddressSpace::AddressSpaceSampler, enumSampler); +} + +TEST(ReadEnumCheckedAddressSpace, GivenNullTokenThenFail) { + using AddressSpace = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpace; + AddressSpace enumRepresentation; + std::string errors; + bool success; + + success = NEO::readEnumChecked(nullptr, enumRepresentation, "some_kernel", errors); + EXPECT_FALSE(success); +} + +TEST(ReadEnumCheckedAddressSpace, GivenUnknownStringRepresentationThenFail) { + using AddressSpace = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpace; + AddressSpace enumRepresentation; + std::string errors; + bool success; + + NEO::Yaml::Token someEntry("some_entry", NEO::Yaml::Token::Token::LiteralString); + success = NEO::readEnumChecked(&someEntry, enumRepresentation, "some_kernel", errors); + EXPECT_FALSE(success); + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unhandled \"some_entry\" address space in context of some_kernel\n", errors.c_str()); +} + +TEST(ReadEnumCheckedAccessType, GivenValidStringRepresentationThenParseItCorrectly) { + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::AccessType; + + NEO::Yaml::Token tokReadOnly(readonly, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokWriteOnly(writeonly, NEO::Yaml::Token::Token::LiteralString); + NEO::Yaml::Token tokReadWrite(readwrite, NEO::Yaml::Token::Token::LiteralString); + + using AccessType = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessType; + AccessType enumReadOnly, enumWriteOnly, enumReadWrite; + std::string errors; + bool success; + + success = NEO::readEnumChecked(&tokReadOnly, enumReadOnly, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(AccessType::AccessTypeReadonly, enumReadOnly); + + success = NEO::readEnumChecked(&tokWriteOnly, enumWriteOnly, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(AccessType::AccessTypeWriteonly, enumWriteOnly); + + success = NEO::readEnumChecked(&tokReadWrite, enumReadWrite, "some_kernel", errors); + EXPECT_TRUE(success); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_EQ(AccessType::AccessTypeReadwrite, enumReadWrite); +} + +TEST(ReadEnumCheckedAccessType, GivenNullTokenThenFail) { + using AccessType = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessType; + AccessType enumRepresentation; + std::string errors; + bool success; + + success = NEO::readEnumChecked(nullptr, enumRepresentation, "some_kernel", errors); + EXPECT_FALSE(success); +} + +TEST(ReadEnumCheckedAccessType, GivenUnknownStringRepresentationThenFail) { + using AccessType = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessType; + AccessType enumRepresentation; + std::string errors; + bool success; + + NEO::Yaml::Token someEntry("some_entry", NEO::Yaml::Token::Token::LiteralString); + success = NEO::readEnumChecked(&someEntry, enumRepresentation, "some_kernel", errors); + EXPECT_FALSE(success); + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unhandled \"some_entry\" access type in context of some_kernel\n", errors.c_str()); +} + +TEST(ReadZeInfoPerThreadPayloadArguments, GivenValidYamlEntriesThenSetProperMembers) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + per_thread_payload_arguments: + - arg_type : packed_local_ids + offset : 8 + size : 16 + - arg_type : local_id + offset : 32 + size : 192 +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &argsNode = *parser.findNodeWithKeyDfs("per_thread_payload_arguments"); + std::string errors; + std::string warnings; + NEO::ZeInfoPerThreadPayloadArguments args; + auto err = NEO::readZeInfoPerThreadPayloadArguments(parser, argsNode, args, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(2U, args.size()); + EXPECT_EQ(NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypePackedLocalIds, args[0].argType); + EXPECT_EQ(8, args[0].offset); + EXPECT_EQ(16, args[0].size); + + EXPECT_EQ(NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeLocalId, args[1].argType); + EXPECT_EQ(32, args[1].offset); + EXPECT_EQ(192, args[1].size); +} + +TEST(ReadZeInfoPerThreadPayloadArguments, GivenUnknownEntryThenEmmitsWarning) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + per_thread_payload_arguments: + - arg_type : packed_local_ids + offset : 8 + size : 16 + something_new : 256 +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &argsNode = *parser.findNodeWithKeyDfs("per_thread_payload_arguments"); + std::string errors; + std::string warnings; + NEO::ZeInfoPerThreadPayloadArguments args; + auto err = NEO::readZeInfoPerThreadPayloadArguments(parser, argsNode, args, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unknown entry \"something_new\" for per-thread payload argument in context of some_kernel\n", warnings.c_str()); + + ASSERT_EQ(1U, args.size()); + EXPECT_EQ(NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypePackedLocalIds, args[0].argType); + EXPECT_EQ(8, args[0].offset); + EXPECT_EQ(16, args[0].size); +} + +TEST(ReadZeInfoPerThreadPayloadArguments, GivenInvalidValueForKnownEntryThenFails) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + per_thread_payload_arguments: + - arg_type : packed_local_ids + offset : true + size : 16 +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &argsNode = *parser.findNodeWithKeyDfs("per_thread_payload_arguments"); + std::string errors; + std::string warnings; + NEO::ZeInfoPerThreadPayloadArguments args; + auto err = NEO::readZeInfoPerThreadPayloadArguments(parser, argsNode, args, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(warnings.empty()) << warnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : could not read offset from : [true] in context of : some_kernel\n", errors.c_str()); +} + +TEST(ReadZeInfoPerThreadPayloadArguments, GivenZeroSizeEntryThenSkipsItAndEmitsWarning) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + per_thread_payload_arguments: + - arg_type : packed_local_ids + offset : 16 + size : 0 +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &argsNode = *parser.findNodeWithKeyDfs("per_thread_payload_arguments"); + std::string errors; + std::string warnings; + NEO::ZeInfoPerThreadPayloadArguments args; + auto err = NEO::readZeInfoPerThreadPayloadArguments(parser, argsNode, args, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Skippinig 0-size per-thread argument of type : packed_local_ids in context of some_kernel\n", warnings.c_str()); + EXPECT_TRUE(args.empty()); +} + +TEST(ReadZeInfoPayloadArguments, GivenValidYamlEntriesThenSetProperMembers) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + payload_arguments: + - arg_type : arg_bypointer + offset : 16 + size : 8 + arg_index : 1 + addrmode : stateless + addrspace : global + access_type : readwrite + - arg_type : arg_byvalue + offset : 24 + size : 4 + arg_index : 2 +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &argsNode = *parser.findNodeWithKeyDfs("payload_arguments"); + std::string errors; + std::string warnings; + NEO::ZeInfoPayloadArguments args; + uint32_t maxArgIndex = 0U; + auto err = NEO::readZeInfoPayloadArguments(parser, argsNode, args, maxArgIndex, "some_kernel", errors, warnings); + EXPECT_EQ(2U, maxArgIndex); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(2U, args.size()); + + EXPECT_EQ(NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeArgBypointer, args[0].argType); + EXPECT_EQ(16, args[0].offset); + EXPECT_EQ(8, args[0].size); + EXPECT_EQ(1, args[0].argIndex); + EXPECT_EQ(NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingModeStateless, args[0].addrmode); + EXPECT_EQ(NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AddressSpaceGlobal, args[0].addrspace); + EXPECT_EQ(NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::AccessTypeReadwrite, args[0].accessType); + + EXPECT_EQ(NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeArgByvalue, args[1].argType); + EXPECT_EQ(24, args[1].offset); + EXPECT_EQ(4, args[1].size); +} + +TEST(ReadZeInfoPayloadArguments, GivenUnknownEntryThenEmmitsWarning) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + payload_arguments: + - arg_type : arg_byvalue + offset : 24 + size : 4 + arg_index : 2 + something_new : 7 +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &argsNode = *parser.findNodeWithKeyDfs("payload_arguments"); + std::string errors; + std::string warnings; + NEO::ZeInfoPayloadArguments args; + uint32_t maxArgIndex = 0U; + auto err = NEO::readZeInfoPayloadArguments(parser, argsNode, args, maxArgIndex, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unknown entry \"something_new\" for payload argument in context of some_kernel\n", warnings.c_str()); + + ASSERT_EQ(1U, args.size()); + EXPECT_EQ(NEO::Elf::ZebinKernelMetadata::Types::Kernel::ArgTypeArgByvalue, args[0].argType); + EXPECT_EQ(24, args[0].offset); + EXPECT_EQ(4, args[0].size); + EXPECT_EQ(2, args[0].argIndex); +} + +TEST(ReadZeInfoBindingTableIndices, GivenInvalidValueForKnownEntryThenFails) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + payload_arguments: + - arg_type : arg_byvalue + offset : 24 + size : true + arg_index : 2 +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &argsNode = *parser.findNodeWithKeyDfs("payload_arguments"); + std::string errors; + std::string warnings; + NEO::ZeInfoPayloadArguments args; + uint32_t maxArgIndex = 0U; + auto err = NEO::readZeInfoPayloadArguments(parser, argsNode, args, maxArgIndex, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(warnings.empty()) << warnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : could not read size from : [true] in context of : some_kernel\n", errors.c_str()); +} + +TEST(ReadZeInfoBindingTableIndices, GivenValidYamlEntriesThenSetProperMembers) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + binding_table_indices: + - bti_value : 1 + arg_index : 7 + - bti_value : 5 + arg_index : 13 +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &btisNode = *parser.findNodeWithKeyDfs("binding_table_indices"); + std::string errors; + std::string warnings; + NEO::ZeInfoBindingTableIndices btis; + NEO::ZeInfoBindingTableIndices::value_type maxBindingTableEntry = {}; + auto err = NEO::readZeInfoBindingTableIndices(parser, btisNode, btis, maxBindingTableEntry, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(2U, btis.size()); + EXPECT_EQ(5, maxBindingTableEntry.btiValue); + EXPECT_EQ(13, maxBindingTableEntry.argIndex); + + EXPECT_EQ(1, btis[0].btiValue); + EXPECT_EQ(7, btis[0].argIndex); + + EXPECT_EQ(5, btis[1].btiValue); + EXPECT_EQ(13, btis[1].argIndex); +} + +TEST(ReadZeInfoBindingTableIndices, GivenUnknownEntryThenEmmitsWarning) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + binding_table_indices: + - bti_value : 1 + arg_index : 7 + something_new : true +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &argsNode = *parser.findNodeWithKeyDfs("binding_table_indices"); + std::string errors; + std::string warnings; + NEO::ZeInfoBindingTableIndices btis; + NEO::ZeInfoBindingTableIndices::value_type maxBindingTableEntry = {}; + auto err = NEO::readZeInfoBindingTableIndices(parser, argsNode, btis, maxBindingTableEntry, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unknown entry \"something_new\" for binding table index in context of some_kernel\n", warnings.c_str()); + + ASSERT_EQ(1U, btis.size()); + EXPECT_EQ(1, btis[0].btiValue); + EXPECT_EQ(7, btis[0].argIndex); +} + +TEST(ReadZeInfoPayloadArguments, GivenInvalidValueForKnownEntryThenFails) { + NEO::ConstStringRef yaml = R"===(--- +kernels: + - name: some_kernel + binding_table_indices: + - bti_value : 1 + arg_index : any +... +)==="; + + std::string parserErrors; + std::string parserWarnings; + NEO::Yaml::YamlParser parser; + bool success = parser.parse(yaml, parserErrors, parserWarnings); + ASSERT_TRUE(success); + auto &argsNode = *parser.findNodeWithKeyDfs("binding_table_indices"); + std::string errors; + std::string warnings; + NEO::ZeInfoBindingTableIndices btis; + NEO::ZeInfoBindingTableIndices::value_type maxBindingTableEntry = {}; + auto err = NEO::readZeInfoBindingTableIndices(parser, argsNode, btis, maxBindingTableEntry, "some_kernel", errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(warnings.empty()) << warnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : could not read arg_index from : [any] in context of : some_kernel\n", errors.c_str()); +} + +TEST(DecodeSingleDeviceBinaryZebin, GivenInvalidElfThenReturnError) { + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, error); + EXPECT_TRUE(decodeWarnings.empty()); + EXPECT_FALSE(decodeErrors.empty()); + EXPECT_STREQ("Invalid or missing ELF header", decodeErrors.c_str()); +} + +TEST(DecodeSingleDeviceBinaryZebin, WhenFailedToExtractZebinSectionsThenDecodingFails) { + ZebinTestData::ValidEmptyProgram zebin; + zebin.elfHeader->shStrNdx = NEO::Elf::SHN_UNDEF; + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, error); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_FALSE(decodeErrors.empty()); + + std::string elfErrors; + std::string elfWarnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, elfErrors, elfWarnings); + ASSERT_TRUE((nullptr != elf.elfFileHeader) && elfErrors.empty() && elfWarnings.empty()); + NEO::ZebinSections sections; + std::string extractErrors; + std::string extractWarnings; + auto extractErr = NEO::extractZebinSections(elf, sections, extractErrors, extractWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, extractErr); + EXPECT_STREQ(extractErrors.c_str(), decodeErrors.c_str()); + EXPECT_STREQ(extractWarnings.c_str(), decodeWarnings.c_str()); +} + +TEST(DecodeSingleDeviceBinaryZebin, WhenValidationOfZebinSectionsCountFailsThenDecodingFails) { + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_ZEBIN_SPIRV, NEO::Elf::SectionsNamesZebin::spv, {}); + zebin.appendSection(NEO::Elf::SHT_ZEBIN_SPIRV, NEO::Elf::SectionsNamesZebin::spv, {}); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, error); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_FALSE(decodeErrors.empty()); + + std::string elfErrors; + std::string elfWarnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, elfErrors, elfWarnings); + ASSERT_TRUE((nullptr != elf.elfFileHeader) && elfErrors.empty() && elfWarnings.empty()); + NEO::ZebinSections sections; + std::string extractErrors; + std::string extractWarnings; + auto extractErr = NEO::extractZebinSections(elf, sections, extractErrors, extractWarnings); + EXPECT_EQ(NEO::DecodeError::Success, extractErr); + EXPECT_TRUE(extractErrors.empty()) << extractErrors; + EXPECT_TRUE(extractWarnings.empty()) << extractWarnings; + + std::string validateErrors; + std::string validateWarnings; + auto validateErr = NEO::validateZebinSectionsCount(sections, validateErrors, validateWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, validateErr); + EXPECT_STREQ(validateErrors.c_str(), decodeErrors.c_str()); + EXPECT_STREQ(validateWarnings.c_str(), decodeWarnings.c_str()); +} + +TEST(DecodeSingleDeviceBinaryZebin, GivenGlobalDataSectionThenSetsUpInitDataAndSize) { + ZebinTestData::ValidEmptyProgram zebin; + const uint8_t data[] = {2, 3, 5, 7, 11, 13, 17, 19}; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::dataGlobal, data); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::Success, error); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_TRUE(decodeErrors.empty()) << decodeErrors; + EXPECT_EQ(sizeof(data), programInfo.globalVariables.size); + EXPECT_EQ(0, memcmp(programInfo.globalVariables.initData, data, sizeof(data))); + EXPECT_EQ(0U, programInfo.globalConstants.size); + EXPECT_EQ(nullptr, programInfo.globalConstants.initData); +} + +TEST(DecodeSingleDeviceBinaryZebin, GivenConstDataSectionThenSetsUpInitDataAndSize) { + ZebinTestData::ValidEmptyProgram zebin; + const uint8_t data[] = {2, 3, 5, 7, 11, 13, 17, 19}; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::dataConst, data); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::Success, error); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_TRUE(decodeErrors.empty()) << decodeErrors; + EXPECT_EQ(sizeof(data), programInfo.globalConstants.size); + EXPECT_EQ(0, memcmp(programInfo.globalConstants.initData, data, sizeof(data))); + EXPECT_EQ(0U, programInfo.globalVariables.size); + EXPECT_EQ(nullptr, programInfo.globalVariables.initData); +} + +TEST(DecodeSingleDeviceBinaryZebin, GivenSymtabSectionThenEmirsWarningAndSkipsIt) { + ZebinTestData::ValidEmptyProgram zebin; + const uint8_t data[] = {2, 3, 5, 7, 11, 13, 17, 19}; + zebin.appendSection(NEO::Elf::SHT_SYMTAB, NEO::Elf::SectionsNamesZebin::symtab, data).entsize = sizeof(NEO::Elf::ElfSymbolEntry); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::Success, error); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Ignoring symbol table\n", decodeWarnings.c_str()); + EXPECT_TRUE(decodeErrors.empty()) << decodeErrors; +} + +TEST(DecodeSingleDeviceBinaryZebin, GivenSymtabWithInvalidSymEntriesThenFails) { + ZebinTestData::ValidEmptyProgram zebin; + const uint8_t data[] = {2, 3, 5, 7, 11, 13, 17, 19}; + zebin.appendSection(NEO::Elf::SHT_SYMTAB, NEO::Elf::SectionsNamesZebin::symtab, data).entsize = sizeof(NEO::Elf::ElfSymbolEntry) - 1; + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, error); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid symbol table entries size - expected : 24, got : 23\n", decodeErrors.c_str()); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; +} + +TEST(DecodeSingleDeviceBinaryZebin, WhenZeInfoSectionIsEmptyThenEmitsWarning) { + ZebinTestData::ValidEmptyProgram zebin; + zebin.removeSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::Success, error); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected at least one .ze_info section, got 0\n", decodeWarnings.c_str()); + EXPECT_TRUE(decodeErrors.empty()) << decodeErrors; +} + +TEST(DecodeSingleDeviceBinaryZebin, WhenYamlParserForZeInfoFailsThenDecodingFails) { + ZebinTestData::ValidEmptyProgram zebin; + zebin.removeSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo); + auto brokenZeInfo = NEO::ConstStringRef("unterminated_string : \""); + zebin.appendSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, ArrayRef::fromAny(brokenZeInfo.data(), brokenZeInfo.size())); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, error); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + + NEO::Yaml::YamlParser parser; + std::string parserErrors; + std::string parserWarnings; + bool validYaml = parser.parse((brokenZeInfo), parserErrors, parserWarnings); + EXPECT_FALSE(validYaml); + EXPECT_STREQ(parserWarnings.c_str(), decodeWarnings.c_str()); + EXPECT_STREQ(parserErrors.c_str(), decodeErrors.c_str()); +} + +TEST(DecodeSingleDeviceBinaryZebin, GivenEmptyInZeInfoThenEmitsWarning) { + ZebinTestData::ValidEmptyProgram zebin; + zebin.removeSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo); + auto brokenZeInfo = NEO::ConstStringRef("#no data\n"); + zebin.appendSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, ArrayRef::fromAny(brokenZeInfo.data(), brokenZeInfo.size())); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::Success, error); + EXPECT_STREQ("NEO::Yaml : Text has no data\nDeviceBinaryFormat::Zebin : Empty kernels metadata section (.ze_info)\n", decodeWarnings.c_str()); + EXPECT_TRUE(decodeErrors.empty()) << decodeErrors; +} + +TEST(DecodeSingleDeviceBinaryZebin, GivenUnknownEntryInZeInfoGlobalScopeThenEmitsWarning) { + ZebinTestData::ValidEmptyProgram zebin; + zebin.removeSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo); + auto brokenZeInfo = NEO::ConstStringRef("some_entry : a\nkernels : \n"); + zebin.appendSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, ArrayRef::fromAny(brokenZeInfo.data(), brokenZeInfo.size())); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::Success, error); + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unknown entry \"some_entry\" in global scope of .ze_info\n", decodeWarnings.c_str()); + EXPECT_TRUE(decodeErrors.empty()) << decodeErrors; +} + +TEST(DecodeSingleDeviceBinaryZebin, WhenZeInfoDoesNotContainKernelsSectionThenEmitsWarning) { + ZebinTestData::ValidEmptyProgram zebin; + zebin.removeSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo); + auto brokenZeInfo = NEO::ConstStringRef("a:b\n"); + zebin.appendSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, ArrayRef::fromAny(brokenZeInfo.data(), brokenZeInfo.size())); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::Success, error); + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Unknown entry \"a\" in global scope of .ze_info\nDeviceBinaryFormat::Zebin::.ze_info : Expected one kernels entry in global scope of .ze_info, got : 0\n", decodeWarnings.c_str()); + EXPECT_TRUE(decodeErrors.empty()) << decodeErrors; +} + +TEST(DecodeSingleDeviceBinaryZebin, WhenZeInfoContainsMultipleKernelSectionsThenFails) { + ZebinTestData::ValidEmptyProgram zebin; + zebin.removeSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo); + auto brokenZeInfo = NEO::ConstStringRef("kernels:\nkernels:\n"); + zebin.appendSection(NEO::Elf::SHT_ZEBIN::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, ArrayRef::fromAny(brokenZeInfo.data(), brokenZeInfo.size())); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, error); + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Expected at most one kernels entry in global scope of .ze_info, got : 2\n", decodeErrors.c_str()); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; +} + +TEST(DecodeSingleDeviceBinaryZebin, WhenDecodeZeInfoFailsThenDecodingFails) { + NEO::ConstStringRef brokenZeInfo = R"===( +kernels: + - +)==="; + 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::fromAny(brokenZeInfo.data(), brokenZeInfo.size())); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, error); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected exactly 1 of name section, got : 0\nDeviceBinaryFormat::Zebin : Expected exactly 1 of execution_env section, got : 0\n", decodeErrors.c_str()); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; +} + +TEST(DecodeSingleDeviceBinaryZebin, GivenValidZeInfoThenPopulatesKernelDescriptorProperly) { + NEO::ConstStringRef validZeInfo = R"===( +kernels: + - name : some_kernel + execution_env : + simd_size : 8 + - name : some_other_kernel + execution_env : + simd_size : 32 +)==="; + 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::fromAny(validZeInfo.data(), validZeInfo.size())); + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_other_kernel", {}); + + NEO::ProgramInfo programInfo; + NEO::SingleDeviceBinary singleBinary; + singleBinary.deviceBinary = zebin.storage; + std::string decodeErrors; + std::string decodeWarnings; + auto error = NEO::decodeSingleDeviceBinary(programInfo, singleBinary, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::Success, error); + EXPECT_TRUE(decodeErrors.empty()) << decodeErrors; + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + + ASSERT_EQ(2U, programInfo.kernelInfos.size()); + EXPECT_STREQ("some_kernel", programInfo.kernelInfos[0]->kernelDescriptor.kernelMetadata.kernelName.c_str()); + EXPECT_STREQ("some_other_kernel", programInfo.kernelInfos[1]->kernelDescriptor.kernelMetadata.kernelName.c_str()); + EXPECT_EQ(8, programInfo.kernelInfos[0]->kernelDescriptor.kernelAttributes.simdSize); + EXPECT_EQ(32, programInfo.kernelInfos[1]->kernelDescriptor.kernelAttributes.simdSize); +} + +TEST(PopulateKernelDescriptor, WhenValidationOfZeinfoSectionsCountFailsThenDecodingFails) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel +)==="; + NEO::ProgramInfo programInfo; + NEO::Elf::Elf elf; + NEO::Yaml::YamlParser parser; + std::string parseErrors; + std::string parseWarnings; + bool parseSuccess = parser.parse(zeinfo, parseErrors, parseWarnings); + ASSERT_TRUE(parseSuccess) << parseErrors << " " << parseWarnings; + NEO::ZebinSections zebinSections; + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string decodeErrors; + std::string decodeWarnings; + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Expected exactly 1 of name section, got : 0\nDeviceBinaryFormat::Zebin : Expected exactly 1 of execution_env section, got : 0\n", decodeErrors.c_str()); +} + +TEST(PopulateKernelDescriptor, GivenInvalidExecutionEnvironmentThanFails) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env : + simd_size : true +)==="; + NEO::ProgramInfo programInfo; + NEO::Elf::Elf elf; + NEO::Yaml::YamlParser parser; + std::string parseErrors; + std::string parseWarnings; + bool parseSuccess = parser.parse(zeinfo, parseErrors, parseWarnings); + ASSERT_TRUE(parseSuccess) << parseErrors << " " << parseWarnings; + NEO::ZebinSections zebinSections; + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string decodeErrors; + std::string decodeWarnings; + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : could not read simd_size from : [true] in context of : some_kernel\n", decodeErrors.c_str()); +} + +TEST(PopulateKernelDescriptor, GivenInvalidPerThreadPayloadArgYamlEntriesThenFails) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + actual_kernel_start_offset: 0 + per_thread_payload_arguments: + - arg_type: local_id + offset: aaa + size: 8 +)==="; + NEO::ProgramInfo programInfo; + NEO::Elf::Elf elf; + NEO::Yaml::YamlParser parser; + std::string parseErrors; + std::string parseWarnings; + bool parseSuccess = parser.parse(zeinfo, parseErrors, parseWarnings); + ASSERT_TRUE(parseSuccess) << parseErrors << " " << parseWarnings; + NEO::ZebinSections zebinSections; + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string decodeErrors; + std::string decodeWarnings; + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : could not read offset from : [aaa] in context of : some_kernel\n", decodeErrors.c_str()); +} + +TEST(PopulateKernelDescriptor, GivenInvalidPayloadArgYamlEntriesThenFails) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + actual_kernel_start_offset: 0 + payload_arguments: + - arg_type: global_id_offset + offset: aaa + size: 12 +)==="; + NEO::ProgramInfo programInfo; + NEO::Elf::Elf elf; + NEO::Yaml::YamlParser parser; + std::string parseErrors; + std::string parseWarnings; + bool parseSuccess = parser.parse(zeinfo, parseErrors, parseWarnings); + ASSERT_TRUE(parseSuccess) << parseErrors << " " << parseWarnings; + NEO::ZebinSections zebinSections; + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string decodeErrors; + std::string decodeWarnings; + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : could not read offset from : [aaa] in context of : some_kernel\n", decodeErrors.c_str()); +} + +TEST(PopulateKernelDescriptor, GivenInvalidSimdSizeThenFails) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 7 +)==="; + NEO::ProgramInfo programInfo; + NEO::Elf::Elf elf; + NEO::Yaml::YamlParser parser; + std::string parseErrors; + std::string parseWarnings; + bool parseSuccess = parser.parse(zeinfo, parseErrors, parseWarnings); + ASSERT_TRUE(parseSuccess) << parseErrors << " " << parseWarnings; + NEO::ZebinSections zebinSections; + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string decodeErrors; + std::string decodeWarnings; + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid simd size : 7 in context of : some_kernel. Expected 1, 8, 16 or 32. Got : 7\n", decodeErrors.c_str()); +} + +TEST(PopulateKernelDescriptor, GivenValidSimdSizeThenSetsItCorrectly) { + uint32_t validSimdSizes[] = {1, 8, 16, 32}; + for (auto simdSize : validSimdSizes) { + std::string zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: )===" + + std::to_string(simdSize) + "\n"; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + EXPECT_EQ(simdSize, (*programInfo.kernelInfos.begin())->kernelDescriptor.kernelAttributes.simdSize); + } +} + +TEST(PopulateKernelDescriptor, GivenInvalidPerThreadArgThenFails) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 + per_thread_payload_arguments: + - arg_type: local_size + offset: 0 + size: 8 +)==="; + NEO::ProgramInfo programInfo; + NEO::Elf::Elf elf; + NEO::Yaml::YamlParser parser; + std::string parseErrors; + std::string parseWarnings; + bool parseSuccess = parser.parse(zeinfo, parseErrors, parseWarnings); + ASSERT_TRUE(parseSuccess) << parseErrors << " " << parseWarnings; + NEO::ZebinSections zebinSections; + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string decodeErrors; + std::string decodeWarnings; + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid arg type in per thread data section in context of : some_kernel.\n", decodeErrors.c_str()); +} + +TEST(PopulateKernelDescriptor, GivenValidPerThreadArgThenPopulatesKernelDescriptor) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 32 + per_thread_payload_arguments: + - arg_type: local_id + offset: 0 + size: 192 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + EXPECT_EQ(3U, programInfo.kernelInfos[0]->kernelDescriptor.kernelAttributes.numLocalIdChannels); +} + +TEST(PopulateKernelDescriptor, GivenInvalidPayloadArgThenFails) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 + payload_arguments: + - arg_type: local_id + offset: 0 + size: 12 +)==="; + NEO::ProgramInfo programInfo; + NEO::Elf::Elf elf; + NEO::Yaml::YamlParser parser; + std::string parseErrors; + std::string parseWarnings; + bool parseSuccess = parser.parse(zeinfo, parseErrors, parseWarnings); + ASSERT_TRUE(parseSuccess) << parseErrors << " " << parseWarnings; + NEO::ZebinSections zebinSections; + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string decodeErrors; + std::string decodeWarnings; + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid arg type in cross thread data section in context of : some_kernel.\n", decodeErrors.c_str()); +} + +TEST(PopulateKernelDescriptor, GivenZebinAppendElwsThenInjectsElwsArg) { + DebugManagerStateRestore dbgRestore; + NEO::DebugManager.flags.ZebinAppendElws.set(true); + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 + payload_arguments: + - arg_type: local_size + offset: 16 + size: 12 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + EXPECT_EQ(64, programInfo.kernelInfos[0]->kernelDescriptor.kernelAttributes.crossThreadDataSize); + EXPECT_EQ(16, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.dispatchTraits.localWorkSize[0]); + EXPECT_EQ(20, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.dispatchTraits.localWorkSize[1]); + EXPECT_EQ(24, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.dispatchTraits.localWorkSize[2]); + EXPECT_EQ(32, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.dispatchTraits.enqueuedLocalWorkSize[0]); + EXPECT_EQ(36, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.dispatchTraits.enqueuedLocalWorkSize[1]); + EXPECT_EQ(40, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.dispatchTraits.enqueuedLocalWorkSize[2]); +} + +TEST(PopulateKernelDescriptor, GivenInvalidBindingTableYamlEntriesThenFails) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 + payload_arguments: + - arg_type: arg_byvalue + offset: 0 + size: 12 + arg_index: 0 + binding_table_indices: + - arg_index: 0 + bti_value:true +)==="; + NEO::ProgramInfo programInfo; + NEO::Elf::Elf elf; + NEO::Yaml::YamlParser parser; + std::string parseErrors; + std::string parseWarnings; + bool parseSuccess = parser.parse(zeinfo, parseErrors, parseWarnings); + ASSERT_TRUE(parseSuccess) << parseErrors << " " << parseWarnings; + NEO::ZebinSections zebinSections; + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string decodeErrors; + std::string decodeWarnings; + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : could not read bti_value from : [true] in context of : some_kernel\n", decodeErrors.c_str()); +} + +TEST(PopulateKernelDescriptor, GivenValidBindingTableEntriesThenGeneratesSsh) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 + payload_arguments: + - arg_type: arg_bypointer + offset: 0 + size: 8 + arg_index: 0 + addrmode : stateful + - arg_type: arg_bypointer + offset: 8 + size: 8 + arg_index: 1 + addrmode : stateful + binding_table_indices: + - arg_index: 0 + bti_value:2 + - arg_index: 1 + bti_value:7 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + + ASSERT_EQ(2U, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs.size()); + EXPECT_EQ(128, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs[0].as().bindful); + EXPECT_EQ(448, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs[1].as().bindful); + EXPECT_EQ(8U, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.bindingTable.numEntries); + EXPECT_EQ(512U, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.bindingTable.tableOffset); + ASSERT_EQ(576U, programInfo.kernelInfos[0]->heapInfo.SurfaceStateHeapSize); + ASSERT_NE(nullptr, programInfo.kernelInfos[0]->heapInfo.pSsh); + EXPECT_EQ(128U, reinterpret_cast(ptrOffset(programInfo.kernelInfos[0]->heapInfo.pSsh, 512U))[0]); + EXPECT_EQ(448U, reinterpret_cast(ptrOffset(programInfo.kernelInfos[0]->heapInfo.pSsh, 512U))[1]); +} + +TEST(PopulateKernelDescriptor, GivenBtiEntryForWrongArgTypeThenFail) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 + payload_arguments: + - arg_type: arg_byvalue + offset: 0 + size: 12 + arg_index: 0 + binding_table_indices: + - arg_index: 0 + bti_value:0 +)==="; + NEO::ProgramInfo programInfo; + NEO::Elf::Elf elf; + NEO::Yaml::YamlParser parser; + std::string parseErrors; + std::string parseWarnings; + bool parseSuccess = parser.parse(zeinfo, parseErrors, parseWarnings); + ASSERT_TRUE(parseSuccess) << parseErrors << " " << parseWarnings; + NEO::ZebinSections zebinSections; + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + std::string decodeErrors; + std::string decodeWarnings; + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, decodeErrors, decodeWarnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_TRUE(decodeWarnings.empty()) << decodeWarnings; + EXPECT_STREQ("DeviceBinaryFormat::Zebin::.ze_info : Invalid binding table entry for non-pointer and non-image argument idx : 0.\n", decodeErrors.c_str()); +} + +TEST(PopulateKernelDescriptor, GivenKernelWithoutCorrespondingTextSectionThenFail) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("Could not find text section for kernel some_kernel\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(PopulateKernelDescriptor, GivenValidExeuctionEnvironmentThenPopulatedKernelDescriptorProperly) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + actual_kernel_start_offset : 5 + barrier_count : 7 + disable_mid_thread_preemption : true + grf_count : 13 + has_4gb_buffers : true + has_device_enqueue : true + has_fence_for_image_access : true + has_global_atomics : true + has_multi_scratch_spaces : true + has_no_stateless_write : true + hw_preemption_mode : 2 + offset_to_skip_per_thread_data_load : 23 + offset_to_skip_set_ffid_gp : 29 + required_sub_group_size : 16 + simd_size : 32 + slm_size : 1024 + subgroup_independent_forward_progress : true +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + + auto &kernelDescriptor = programInfo.kernelInfos[0]->kernelDescriptor; + EXPECT_EQ(7U, kernelDescriptor.kernelAttributes.hasBarriers); + EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.usesBarriers); + EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.requiresDisabledMidThreadPreemption); + EXPECT_EQ(13U, kernelDescriptor.kernelAttributes.numGrfRequired); + EXPECT_EQ(KernelDescriptor::Stateless, kernelDescriptor.kernelAttributes.bufferAddressingMode); + EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.usesDeviceSideEnqueue); + EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.usesFencesForReadWriteImages); + EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.useGlobalAtomics); + EXPECT_FALSE(kernelDescriptor.kernelAttributes.flags.usesStatelessWrites); + EXPECT_EQ(23U, kernelDescriptor.entryPoints.skipPerThreadDataLoad); + EXPECT_EQ(29U, kernelDescriptor.entryPoints.skipSetFFIDGP); + EXPECT_EQ(16U, kernelDescriptor.kernelMetadata.requiredSubGroupSize); + EXPECT_EQ(32U, kernelDescriptor.kernelAttributes.simdSize); + EXPECT_EQ(1024U, kernelDescriptor.kernelAttributes.slmInlineSize); + EXPECT_TRUE(kernelDescriptor.kernelAttributes.flags.requiresSubgroupIndependentForwardProgress); +} + +TEST(PopulateArgDescriptorPerThreadPayload, GivenArgTypeLocalIdWhenOffsetIsNonZeroThenFail) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 + per_thread_payload_arguments: + - arg_type: local_id + offset: 4 + size: 192 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid offset for argument of type local_id in context of : some_kernel. Expected 0.\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(PopulateArgDescriptorPerThreadPayload, GivenArgTypeLocalIdWhenSizeIsInvalidThenFail) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 + per_thread_payload_arguments: + - arg_type: local_id + offset: 0 + size: 7 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid size for argument of type local_id in context of : some_kernel. For simd=8 expected : 32 or 64 or 96. Got : 7 \n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(PopulateArgDescriptorPerThreadPayload, GivenArgTypeLocalIdWhenSizeIsValidThenCalculateNumChannelAccordingly) { + uint32_t simdSizes[] = {8, 16, 32}; + uint32_t numChannelsOpts[] = {1, 2, 3}; + + for (auto simdSize : simdSizes) { + for (auto numChannels : numChannelsOpts) { + std::string zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: )===" + + std::to_string(simdSize) + R"===( + per_thread_payload_arguments: + - arg_type: local_id + offset: 0 + size: )===" + + std::to_string(((simdSize == 32) ? 32 : 16) * numChannels * sizeof(short)) + R"===( + )==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err) << "simd : " << simdSize << ", num channels : " << numChannels; + EXPECT_TRUE(errors.empty()) << errors << "simd : " << simdSize << ", num channels : " << numChannels; + EXPECT_TRUE(warnings.empty()) << warnings << "simd : " << simdSize << ", num channels : " << numChannels; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + EXPECT_EQ(numChannels, programInfo.kernelInfos[0]->kernelDescriptor.kernelAttributes.numLocalIdChannels) << warnings << "simd : " << simdSize << ", num channels : " << numChannels; + EXPECT_EQ(simdSize, programInfo.kernelInfos[0]->kernelDescriptor.kernelAttributes.simdSize) << warnings << "simd : " << simdSize << ", num channels : " << numChannels; + } + } +} + +TEST(PopulateArgDescriptorPerThreadPayload, GivenArgTypePackedLocalIdWhenOffsetIsNonZeroThenFail) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 1 + per_thread_payload_arguments: + - arg_type: packed_local_ids + offset: 4 + size: 6 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Unhandled offset for argument of type packed_local_ids in context of : some_kernel. Expected 0.\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(PopulateArgDescriptorPerThreadPayload, GivenArgTypePackedLocalIdWhenSizeIsInvalidThenFail) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 1 + per_thread_payload_arguments: + - arg_type: packed_local_ids + offset: 0 + size: 1 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid size for argument of type packed_local_ids in context of : some_kernel. Expected : 2 or 4 or 6. Got : 1 \n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(PopulateArgDescriptorPerThreadPayload, GivenArgTypePackedLocalIdWhenSizeIsValidThenCalculateNumChannelAccordingly) { + uint32_t simdSizes[] = {1}; + uint32_t numChannelsOpts[] = {1, 2, 3}; + + for (auto simdSize : simdSizes) { + for (auto numChannels : numChannelsOpts) { + std::string zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: )===" + + std::to_string(simdSize) + R"===( + per_thread_payload_arguments: + - arg_type: packed_local_ids + offset: 0 + size: )===" + + std::to_string(numChannels * sizeof(short)) + R"===( + )==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err) << "simd : " << simdSize << ", num channels : " << numChannels; + EXPECT_TRUE(errors.empty()) << errors << "simd : " << simdSize << ", num channels : " << numChannels; + EXPECT_TRUE(warnings.empty()) << warnings << "simd : " << simdSize << ", num channels : " << numChannels; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + EXPECT_EQ(numChannels, programInfo.kernelInfos[0]->kernelDescriptor.kernelAttributes.numLocalIdChannels) << warnings << "simd : " << simdSize << ", num channels : " << numChannels; + EXPECT_EQ(simdSize, programInfo.kernelInfos[0]->kernelDescriptor.kernelAttributes.simdSize) << warnings << "simd : " << simdSize << ", num channels : " << numChannels; + } + } +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenPointerArgWhenAddresspaceIsKnownThenPopulatesArgDescriptorAccordingly) { + using AddressSpace = NEO::KernelArgMetadata::AddressSpaceQualifier; + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::AddrSpace; + std::pair addresSpaces[] = { + {global, AddressSpace::AddrGlobal}, + {local, AddressSpace::AddrLocal}, + {constant, AddressSpace::AddrConstant}, + {"", AddressSpace::AddrUnknown}, + }; + + for (auto addressSpace : addresSpaces) { + std::string zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: 32 + payload_arguments: + - arg_type : arg_bypointer + offset : 16 + size : 8 + arg_index : 0 + addrmode : stateless + )===" + (addressSpace.first.empty() ? "" : ("addrspace : " + addressSpace.first.str())) + + R"===( + access_type : readwrite + )==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + ASSERT_EQ(1U, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs.size()); + EXPECT_EQ(addressSpace.second, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs[0].getTraits().getAddressQualifier()); + } +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenPointerArgWhenAccessQualifierIsKnownThenPopulatesArgDescriptorAccordingly) { + using AccessQualifier = NEO::KernelArgMetadata::AccessQualifier; + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::AccessType; + std::pair accessQualifiers[] = { + {readonly, AccessQualifier::AccessReadOnly}, + {writeonly, AccessQualifier::AccessWriteOnly}, + {readwrite, AccessQualifier::AccessReadWrite}, + {"", AccessQualifier::AccessUnknown}, + }; + + for (auto accessQualifier : accessQualifiers) { + std::string zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: 32 + payload_arguments: + - arg_type : arg_bypointer + offset : 16 + size : 8 + arg_index : 0 + addrmode : stateless + )===" + (accessQualifier.first.empty() ? "" : ("access_type : " + accessQualifier.first.str())) + + R"===( + )==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + ASSERT_EQ(1U, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs.size()); + EXPECT_EQ(accessQualifier.second, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs[0].getTraits().getAccessQualifier()) << accessQualifier.first.str(); + } +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenPointerArgWhenMemoryAcessModeIsUknownThenFail) { + NEO::ConstStringRef zeinfo = R"===( +kernels: + - name : some_kernel + execution_env: + simd_size: 8 + payload_arguments: + - arg_type : arg_bypointer + offset : 16 + size : 8 + arg_index : 0 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("Invalid or missing memory addressing mode for arg idx : 0 in context of : some_kernel.\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenPointerArgWhenMemoryAcessModeIsKnownThenPopulatesArgDescriptorAccordingly) { + using AddressingMode = NEO::Elf::ZebinKernelMetadata::Types::Kernel::PayloadArgument::MemoryAddressingMode; + using namespace NEO::Elf::ZebinKernelMetadata::Tags::Kernel::PayloadArgument::MemoryAddressingMode; + std::pair addressingModes[] = {{stateful, AddressingMode::MemoryAddressingModeStateful}, + {stateless, AddressingMode::MemoryAddressingModeStateless}, + {bindless, AddressingMode::MemoryAddressingModeBindless}, + {sharedLocalMemory, AddressingMode::MemoryAddressingModeSharedLocalMemory}}; + + for (auto addressingMode : addressingModes) { + std::string zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: 32 + payload_arguments: + - arg_type : arg_bypointer + offset : 16 + size : 8 + arg_index : 0 + )===" + (addressingMode.first.empty() ? "" : ("addrmode : " + addressingMode.first.str())) + + R"===( + )==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + ASSERT_EQ(1U, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs.size()); + auto &argAsPointer = programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs[0].as(); + switch (addressingMode.second) { + default: + EXPECT_EQ(AddressingMode::MemoryAddressingModeStateful, addressingMode.second); + break; + case AddressingMode::MemoryAddressingModeStateless: + EXPECT_EQ(16, argAsPointer.stateless); + EXPECT_EQ(8, argAsPointer.pointerSize); + break; + case AddressingMode::MemoryAddressingModeBindless: + EXPECT_EQ(16, argAsPointer.bindless); + break; + case AddressingMode::MemoryAddressingModeSharedLocalMemory: + EXPECT_EQ(16, argAsPointer.slmOffset); + break; + } + } +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenArgTypeLocalSizeWhenArgSizeIsInvalidThenFails) { + NEO::ConstStringRef zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: 32 + payload_arguments: + - arg_type : local_size + offset : 16 + size : 7 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid size for argument of type local_size in context of : some_kernel. Expected 4 or 8 or 12. Got : 7\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenArgTypeLocalSizeWhenArgSizeValidThenPopulatesKernelDescriptor) { + uint32_t vectorSizes[] = {4, 8, 12}; + + for (auto vectorSize : vectorSizes) { + std::string zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: 32 + payload_arguments: + - arg_type : local_size + offset : 16 + size : )===" + + std::to_string(vectorSize) + R"===( + )==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + ASSERT_EQ(1U, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs.size()); + for (uint32_t i = 0; i < vectorSize / sizeof(uint32_t); ++i) { + EXPECT_EQ(16 + sizeof(uint32_t) * i, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.dispatchTraits.localWorkSize[i]) + << " vectorSize : " << vectorSize << ", idx : " << i; + } + } +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenArgTypeGlobaIdOffsetWhenArgSizeIsInvalidThenFails) { + NEO::ConstStringRef zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: 32 + payload_arguments: + - arg_type : global_id_offset + offset : 16 + size : 7 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid size for argument of type global_id_offset in context of : some_kernel. Expected 4 or 8 or 12. Got : 7\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenArgTypeGlobaIdOffsetWhenArgSizeValidThenPopulatesKernelDescriptor) { + uint32_t vectorSizes[] = {4, 8, 12}; + + for (auto vectorSize : vectorSizes) { + std::string zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: 32 + payload_arguments: + - arg_type : global_id_offset + offset : 16 + size : )===" + + std::to_string(vectorSize) + R"===( + )==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + ASSERT_EQ(1U, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs.size()); + for (uint32_t i = 0; i < vectorSize / sizeof(uint32_t); ++i) { + EXPECT_EQ(16 + sizeof(uint32_t) * i, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.dispatchTraits.globalWorkOffset[i]) + << " vectorSize : " << vectorSize << ", idx : " << i; + } + } +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenArgTypeGroupSizeWhenArgSizeIsInvalidThenFails) { + NEO::ConstStringRef zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: 32 + payload_arguments: + - arg_type : group_size + offset : 16 + size : 7 +)==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::InvalidBinary, err); + EXPECT_STREQ("DeviceBinaryFormat::Zebin : Invalid size for argument of type group_size in context of : some_kernel. Expected 4 or 8 or 12. Got : 7\n", errors.c_str()); + EXPECT_TRUE(warnings.empty()) << warnings; +} + +TEST(PopulateArgDescriptorCrossthreadPalyoad, GivenArgTypeGroupSizeWhenArgSizeValidThenPopulatesKernelDescriptor) { + uint32_t vectorSizes[] = {4, 8, 12}; + + for (auto vectorSize : vectorSizes) { + std::string zeinfo = R"===( + kernels: + - name : some_kernel + execution_env: + simd_size: 32 + payload_arguments: + - arg_type : group_size + offset : 16 + size : )===" + + std::to_string(vectorSize) + R"===( + )==="; + NEO::ProgramInfo programInfo; + ZebinTestData::ValidEmptyProgram zebin; + zebin.appendSection(NEO::Elf::SHT_PROGBITS, NEO::Elf::SectionsNamesZebin::textPrefix.str() + "some_kernel", {}); + std::string errors, warnings; + auto elf = NEO::Elf::decodeElf(zebin.storage, errors, warnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << errors << " " << warnings; + + NEO::Yaml::YamlParser parser; + bool parseSuccess = parser.parse(zeinfo, errors, warnings); + ASSERT_TRUE(parseSuccess) << errors << " " << warnings; + + NEO::ZebinSections zebinSections; + auto extractErr = NEO::extractZebinSections(elf, zebinSections, errors, warnings); + ASSERT_EQ(NEO::DecodeError::Success, extractErr) << errors << " " << warnings; + + auto &kernelNode = *parser.createChildrenRange(*parser.findNodeWithKeyDfs("kernels")).begin(); + auto err = NEO::populateKernelDescriptor(programInfo, elf, zebinSections, parser, kernelNode, errors, warnings); + EXPECT_EQ(NEO::DecodeError::Success, err); + EXPECT_TRUE(errors.empty()) << errors; + EXPECT_TRUE(warnings.empty()) << warnings; + ASSERT_EQ(1U, programInfo.kernelInfos.size()); + ASSERT_EQ(1U, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.explicitArgs.size()); + for (uint32_t i = 0; i < vectorSize / sizeof(uint32_t); ++i) { + EXPECT_EQ(16 + sizeof(uint32_t) * i, programInfo.kernelInfos[0]->kernelDescriptor.payloadMappings.dispatchTraits.numWorkGroups[i]) + << " vectorSize : " << vectorSize << ", idx : " << i; + } + } +} diff --git a/shared/test/unit_test/device_binary_format/zebin_tests.h b/shared/test/unit_test/device_binary_format/zebin_tests.h new file mode 100644 index 0000000000..9250af87f8 --- /dev/null +++ b/shared/test/unit_test/device_binary_format/zebin_tests.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#pragma once + +#include "shared/source/device_binary_format/elf/elf_decoder.h" +#include "shared/source/device_binary_format/elf/elf_encoder.h" +#include "shared/source/device_binary_format/elf/zebin_elf.h" + +#include "igfxfmid.h" + +#include + +extern PRODUCT_FAMILY productFamily; + +namespace ZebinTestData { + +struct ValidEmptyProgram { + ValidEmptyProgram() { + NEO::Elf::ElfEncoder<> enc; + enc.getElfFileHeader().type = NEO::Elf::ET_ZEBIN_EXE; + enc.getElfFileHeader().machine = productFamily; + enc.appendSection(NEO::Elf::SHT_ZEBIN_ZEINFO, NEO::Elf::SectionsNamesZebin::zeInfo, std::string{"---\nkernels : \n...\n"}); + storage = enc.encode(); + recalcPtr(); + } + + virtual void recalcPtr() { + elfHeader = reinterpret_cast *>(storage.data()); + } + + template + NEO::Elf::ElfSectionHeader &appendSection(SectionHeaderEnumT sectionType, NEO::ConstStringRef sectionLabel, const ArrayRef sectionData) { + std::string err, warn; + auto decoded = NEO::Elf::decodeElf(storage, err, warn); + NEO::Elf::ElfEncoder enc; + enc.getElfFileHeader() = *decoded.elfFileHeader; + int sectionIt = 0; + auto sectionHeaderNamesData = decoded.sectionHeaders[decoded.elfFileHeader->shStrNdx].data; + NEO::ConstStringRef sectionHeaderNamesString(reinterpret_cast(sectionHeaderNamesData.begin()), sectionHeaderNamesData.size()); + for (const auto §ion : decoded.sectionHeaders) { + switch (section.header->type) { + case NEO::Elf::SHN_UNDEF: + break; + case NEO::Elf::SHT_STRTAB: + if (decoded.elfFileHeader->shStrNdx != sectionIt) { + enc.appendSection(section.header->type, sectionHeaderNamesString.data() + section.header->name, section.data); + } + break; + default: + enc.appendSection(section.header->type, sectionHeaderNamesString.data() + section.header->name, section.data); + break; + } + ++sectionIt; + } + enc.appendSection(static_cast(sectionType), sectionLabel, sectionData); + storage = enc.encode(); + recalcPtr(); + decoded = NEO::Elf::decodeElf(storage, err, warn); + sectionHeaderNamesData = decoded.sectionHeaders[decoded.elfFileHeader->shStrNdx].data; + sectionHeaderNamesString = NEO::ConstStringRef(reinterpret_cast(sectionHeaderNamesData.begin()), sectionHeaderNamesData.size()); + for (const auto §ion : decoded.sectionHeaders) { + if ((sectionType == section.header->type) && (sectionLabel == sectionHeaderNamesString.data() + section.header->name)) { + return const_cast &>(*section.header); + } + } + UNREACHABLE(); + } + + template + void removeSection(SectionHeaderEnumT sectionType, NEO::ConstStringRef sectionLabel) { + std::string err, warn; + auto decoded = NEO::Elf::decodeElf(storage, err, warn); + NEO::Elf::ElfEncoder enc; + enc.getElfFileHeader() = *decoded.elfFileHeader; + int sectionIt = 0; + auto sectionHeaderNamesData = decoded.sectionHeaders[decoded.elfFileHeader->shStrNdx].data; + NEO::ConstStringRef sectionHeaderNamesString(reinterpret_cast(sectionHeaderNamesData.begin()), sectionHeaderNamesData.size()); + for (const auto §ion : decoded.sectionHeaders) { + bool add = true; + switch (section.header->type) { + case NEO::Elf::SHN_UNDEF: + add = false; + break; + case NEO::Elf::SHT_STRTAB: + add = (decoded.elfFileHeader->shStrNdx != sectionIt); + break; + default: + add = ((section.header->type != sectionType) || (sectionHeaderNamesString.data() + section.header->name != sectionLabel)); + break; + } + if (add) { + enc.appendSection(section.header->type, sectionHeaderNamesString.data() + section.header->name, section.data); + } + ++sectionIt; + } + if (decoded.elfFileHeader->shNum <= 3) { + enc.appendSection(NEO::Elf::SHT_STRTAB, "", {}); + } + storage = enc.encode(); + recalcPtr(); + } + + NEO::Elf::ElfFileHeader *elfHeader; + std::vector storage; +}; + +} // namespace ZebinTestData diff --git a/shared/test/unit_test/utilities/const_stringref_tests.cpp b/shared/test/unit_test/utilities/const_stringref_tests.cpp index 634b724626..6aeeac6ffb 100644 --- a/shared/test/unit_test/utilities/const_stringref_tests.cpp +++ b/shared/test/unit_test/utilities/const_stringref_tests.cpp @@ -74,6 +74,20 @@ TEST(ConstStringRef, WhenComparingAgainstContainersThenUsesLexicographicOrdering std::string strTex("Tex"); EXPECT_TRUE(strTex != constStrText); EXPECT_TRUE(constStrText != strTex); + + EXPECT_TRUE(ConstStringRef("Text") == "Text"); + EXPECT_TRUE("Text" == ConstStringRef("Text")); + EXPECT_FALSE(ConstStringRef("Tex") == "Text"); + EXPECT_FALSE("Text" == ConstStringRef("Tex")); + EXPECT_FALSE(ConstStringRef("Tex") == "Text"); + EXPECT_FALSE("Text" == ConstStringRef("Tex")); + + EXPECT_FALSE(ConstStringRef("Text") != "Text"); + EXPECT_FALSE("Text" != ConstStringRef("Text")); + EXPECT_TRUE(ConstStringRef("Tex") != "Text"); + EXPECT_TRUE("Text" != ConstStringRef("Tex")); + EXPECT_TRUE(ConstStringRef("Tex") != "Text"); + EXPECT_TRUE("Text" != ConstStringRef("Tex")); } TEST(ConstStringRef, WhenStrIsCalledThenEmitsProperString) { @@ -189,3 +203,16 @@ TEST(ConstStringRefEqualsCaseInsesitive, WhenStringsIdenticalThenReturnTrue) { TEST(ConstStringRefEqualsCaseInsesitive, WhenStringsDifferOnlyByCaseThenReturnTrue) { EXPECT_TRUE(equalsCaseInsesitive(ConstStringRef("aBc"), ConstStringRef("Abc"))); } + +TEST(ConstStringStartsWith, GivenRightPrefixThenReturnsTrue) { + ConstStringRef str = "some text"; + EXPECT_TRUE(str.startsWith("some")); + EXPECT_TRUE(str.startsWith("some text")); +} + +TEST(ConstStringStartsWith, GivenInvalidPrefixThenReturnsFalse) { + ConstStringRef str = "some text"; + EXPECT_FALSE(str.startsWith("ome")); + EXPECT_FALSE(str.startsWith("some text ")); + EXPECT_FALSE(str.startsWith("substr some text")); +}