From 97a206ed33b8ca056fbe9a4da3fd5f9ca02e1d86 Mon Sep 17 00:00:00 2001 From: Aleksandra Nizio Date: Tue, 7 Oct 2025 16:13:02 +0000 Subject: [PATCH] fix: select target device compatible binary from fatbinary removes recompilation from IR step when fatbinary contains compatible devices binaries Resolves: NEO-14300,GSD-10568 Signed-off-by: Aleksandra Nizio --- .../aot_platforms/neo_aot_platforms.h | 48 ++++++++ .../device_binary_format_ar.cpp | 113 +++++++++++------- shared/source/helpers/product_config_helper.h | 1 + .../device_binary_format_ar_tests.cpp | 96 +++++++++++++-- 4 files changed, 207 insertions(+), 51 deletions(-) diff --git a/shared/source/device_binary_format/aot_platforms/neo_aot_platforms.h b/shared/source/device_binary_format/aot_platforms/neo_aot_platforms.h index e60a1ef8fd..601c4f4ed1 100644 --- a/shared/source/device_binary_format/aot_platforms/neo_aot_platforms.h +++ b/shared/source/device_binary_format/aot_platforms/neo_aot_platforms.h @@ -9,6 +9,8 @@ #include "platforms.h" +#include + namespace AOT { consteval PRODUCT_CONFIG getConfixMaxPlatform() { return CONFIG_MAX_PLATFORM; @@ -22,4 +24,50 @@ inline const auto &getRtlIdAcronyms() { return rtlIdAcronyms; } +inline std::vector getCompatibilityFallbackProductAbbreviations(const std::string &requestedProductAbbreviation) { + std::vector result; + PRODUCT_CONFIG requestedConfig = PRODUCT_CONFIG::UNKNOWN_ISA; + + std::string searchKey = requestedProductAbbreviation; + + for (const auto &acronymEntry : deviceAcronyms) { + if (acronymEntry.first == searchKey || acronymEntry.first.find(searchKey + "-") == 0) { + requestedConfig = acronymEntry.second; + break; + } + } + + if (requestedConfig == PRODUCT_CONFIG::UNKNOWN_ISA) { + return result; + } + + for (const auto &compatibilityEntry : compatibilityMapping) { + bool foundInThisEntry = false; + for (const auto &compatibleConfig : compatibilityEntry.second) { + if (compatibleConfig == requestedConfig) { + foundInThisEntry = true; + break; + } + } + + if (foundInThisEntry) { + for (const auto &acronymEntry : deviceAcronyms) { + if (acronymEntry.second == compatibilityEntry.first) { + std::string compatibleProductName = acronymEntry.first; + size_t dashPos = compatibleProductName.find('-'); + if (dashPos != std::string::npos) { + compatibleProductName = compatibleProductName.substr(0, dashPos); + } + + if (std::find(result.begin(), result.end(), compatibleProductName) == result.end()) { + result.push_back(compatibleProductName); + } + break; + } + } + } + } + + return result; +} } // namespace AOT diff --git a/shared/source/device_binary_format/device_binary_format_ar.cpp b/shared/source/device_binary_format/device_binary_format_ar.cpp index d03c9fe9e2..a5d398cb43 100644 --- a/shared/source/device_binary_format/device_binary_format_ar.cpp +++ b/shared/source/device_binary_format/device_binary_format_ar.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2023 Intel Corporation + * Copyright (C) 2020-2025 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -10,6 +10,12 @@ #include "shared/source/helpers/product_config_helper.h" #include "shared/source/helpers/string.h" +#include "neo_aot_platforms.h" + +#include +#include +#include + namespace NEO { void searchForBinary(Ar::Ar &archiveData, const ConstStringRef filter, Ar::ArFileEntryHeaderAndData *&matched) { for (auto &file : archiveData.files) { @@ -19,7 +25,6 @@ void searchForBinary(Ar::Ar &archiveData, const ConstStringRef filter, Ar::ArFil } } } - template <> bool isDeviceBinaryFormat(const ArrayRef binary) { return NEO::Ar::isAr(binary); @@ -34,49 +39,73 @@ SingleDeviceBinary unpackSingleDeviceBinary(co } std::string pointerSize = ((requestedTargetDevice.maxPointerSizeInBytes == 8) ? "64" : "32"); - std::string filterPointerSizeAndMajorMinorRevision = pointerSize + "." + ProductConfigHelper::parseMajorMinorRevisionValue(requestedTargetDevice.aotConfig); - std::string filterPointerSizeAndMajorMinor = pointerSize + "." + ProductConfigHelper::parseMajorMinorValue(requestedTargetDevice.aotConfig); - std::string filterPointerSizeAndPlatform = pointerSize + "." + requestedProductAbbreviation.str(); - std::string filterPointerSizeAndPlatformAndStepping = filterPointerSizeAndPlatform + "." + std::to_string(requestedTargetDevice.stepping); + + auto searchForProduct = [&](const std::string &productAbbreviation, bool isOriginalProduct) -> SingleDeviceBinary { + std::string filterPointerSizeAndMajorMinorRevision = pointerSize + "." + ProductConfigHelper::parseMajorMinorRevisionValue(requestedTargetDevice.aotConfig); + std::string filterPointerSizeAndMajorMinor = pointerSize + "." + ProductConfigHelper::parseMajorMinorValue(requestedTargetDevice.aotConfig); + std::string filterPointerSizeAndPlatform = pointerSize + "." + productAbbreviation; + std::string filterPointerSizeAndPlatformAndStepping = filterPointerSizeAndPlatform + "." + std::to_string(requestedTargetDevice.stepping); + + Ar::ArFileEntryHeaderAndData *matchedFiles[4] = {}; + Ar::ArFileEntryHeaderAndData *&matchedPointerSizeAndMajorMinorRevision = matchedFiles[0]; + Ar::ArFileEntryHeaderAndData *&matchedPointerSizeAndPlatformAndStepping = matchedFiles[1]; + Ar::ArFileEntryHeaderAndData *&matchedPointerSizeAndMajorMinor = matchedFiles[2]; + Ar::ArFileEntryHeaderAndData *&matchedPointerSizeAndPlatform = matchedFiles[3]; + + searchForBinary(archiveData, ConstStringRef(filterPointerSizeAndMajorMinorRevision), matchedPointerSizeAndMajorMinorRevision); + searchForBinary(archiveData, ConstStringRef(filterPointerSizeAndPlatformAndStepping), matchedPointerSizeAndPlatformAndStepping); + searchForBinary(archiveData, ConstStringRef(filterPointerSizeAndMajorMinor), matchedPointerSizeAndMajorMinor); + searchForBinary(archiveData, ConstStringRef(filterPointerSizeAndPlatform), matchedPointerSizeAndPlatform); + + std::string unpackErrors; + std::string unpackWarnings; + SingleDeviceBinary binaryForRecompilation = {}; + + for (auto matchedFile : matchedFiles) { + if (nullptr == matchedFile) { + continue; + } + auto unpacked = unpackSingleDeviceBinary(matchedFile->fileData, ConstStringRef(productAbbreviation), requestedTargetDevice, unpackErrors, unpackWarnings); + if (false == unpacked.deviceBinary.empty()) { + if ((matchedFile != matchedPointerSizeAndPlatformAndStepping) && (matchedFile != matchedPointerSizeAndMajorMinorRevision)) { + outWarning = "Couldn't find perfectly matched binary in AR, using best usable"; + } + unpacked.packedTargetDeviceBinary = ArrayRef(matchedFile->fileData.begin(), matchedFile->fileData.size()); + return unpacked; + } + if (binaryForRecompilation.intermediateRepresentation.empty() && (false == unpacked.intermediateRepresentation.empty())) { + binaryForRecompilation = unpacked; + } + } + + return binaryForRecompilation; + }; + + auto result = searchForProduct(requestedProductAbbreviation.str(), true); + if (false == result.deviceBinary.empty()) { + return result; + } + + SingleDeviceBinary binaryForRecompilation = result; + + auto compatibleProducts = AOT::getCompatibilityFallbackProductAbbreviations(requestedProductAbbreviation.str()); + for (const auto &compatibleProduct : compatibleProducts) { + auto compatResult = searchForProduct(compatibleProduct, false); + if (false == compatResult.deviceBinary.empty()) { + outWarning = "Couldn't find perfectly matched binary in AR, using best usable"; + return compatResult; + } + if (binaryForRecompilation.intermediateRepresentation.empty() && (false == compatResult.intermediateRepresentation.empty())) { + binaryForRecompilation = compatResult; + } + } + ConstStringRef filterGenericIrFileName{"generic_ir"}; - - Ar::ArFileEntryHeaderAndData *matchedFiles[5] = {}; - Ar::ArFileEntryHeaderAndData *&matchedPointerSizeAndMajorMinorRevision = matchedFiles[0]; - Ar::ArFileEntryHeaderAndData *&matchedPointerSizeAndPlatformAndStepping = matchedFiles[1]; - Ar::ArFileEntryHeaderAndData *&matchedPointerSizeAndMajorMinor = matchedFiles[2]; - Ar::ArFileEntryHeaderAndData *&matchedPointerSizeAndPlatform = matchedFiles[3]; - Ar::ArFileEntryHeaderAndData *&matchedGenericIr = matchedFiles[4]; - - searchForBinary(archiveData, ConstStringRef(filterPointerSizeAndMajorMinorRevision), matchedPointerSizeAndMajorMinorRevision); - searchForBinary(archiveData, ConstStringRef(filterPointerSizeAndPlatformAndStepping), matchedPointerSizeAndPlatformAndStepping); - searchForBinary(archiveData, ConstStringRef(filterPointerSizeAndMajorMinor), matchedPointerSizeAndMajorMinor); - searchForBinary(archiveData, ConstStringRef(filterPointerSizeAndPlatform), matchedPointerSizeAndPlatform); + Ar::ArFileEntryHeaderAndData *matchedGenericIr = nullptr; searchForBinary(archiveData, filterGenericIrFileName, matchedGenericIr); - std::string unpackErrors; - std::string unpackWarnings; - SingleDeviceBinary binaryForRecompilation = {}; - for (auto matchedFile : matchedFiles) { - if (nullptr == matchedFile) { - continue; - } - auto unpacked = unpackSingleDeviceBinary(matchedFile->fileData, requestedProductAbbreviation, requestedTargetDevice, unpackErrors, unpackWarnings); - if (false == unpacked.deviceBinary.empty()) { - if ((matchedFile != matchedPointerSizeAndPlatformAndStepping) && (matchedFile != matchedPointerSizeAndMajorMinorRevision)) { - outWarning = "Couldn't find perfectly matched binary in AR, using best usable"; - } - if (unpacked.intermediateRepresentation.empty() && matchedGenericIr) { - auto unpackedGenericIr = unpackSingleDeviceBinary(matchedGenericIr->fileData, requestedProductAbbreviation, requestedTargetDevice, unpackErrors, unpackWarnings); - if (!unpackedGenericIr.intermediateRepresentation.empty()) { - unpacked.intermediateRepresentation = unpackedGenericIr.intermediateRepresentation; - } - } - unpacked.packedTargetDeviceBinary = ArrayRef(matchedFile->fileData.begin(), matchedFile->fileData.size()); - return unpacked; - } - if (binaryForRecompilation.intermediateRepresentation.empty() && (false == unpacked.intermediateRepresentation.empty())) { - binaryForRecompilation = unpacked; - } + if (matchedGenericIr && binaryForRecompilation.intermediateRepresentation.empty()) { + binaryForRecompilation.intermediateRepresentation = matchedGenericIr->fileData; } if (false == binaryForRecompilation.intermediateRepresentation.empty()) { diff --git a/shared/source/helpers/product_config_helper.h b/shared/source/helpers/product_config_helper.h index 320c8f823b..6ef76e99cf 100644 --- a/shared/source/helpers/product_config_helper.h +++ b/shared/source/helpers/product_config_helper.h @@ -21,6 +21,7 @@ namespace AOT { enum PRODUCT_CONFIG : uint32_t; // NOLINT(readability-identifier-naming) enum RELEASE : uint32_t; enum FAMILY : uint32_t; + } // namespace AOT namespace NEO { diff --git a/shared/test/unit_test/device_binary_format/device_binary_format_ar_tests.cpp b/shared/test/unit_test/device_binary_format/device_binary_format_ar_tests.cpp index 0d4c93b972..ff684845cc 100644 --- a/shared/test/unit_test/device_binary_format/device_binary_format_ar_tests.cpp +++ b/shared/test/unit_test/device_binary_format/device_binary_format_ar_tests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2023 Intel Corporation + * Copyright (C) 2020-2025 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -20,6 +20,8 @@ #include "shared/test/common/test_macros/test.h" #include "shared/test/common/test_macros/test_base.h" +#include "neo_aot_platforms.h" + TEST(IsDeviceBinaryFormatAr, GivenValidBinaryThenReturnTrue) { auto emptyArchive = ArrayRef::fromAny(NEO::Ar::arMagic.begin(), NEO::Ar::arMagic.size()); EXPECT_TRUE(NEO::isDeviceBinaryFormat(emptyArchive)); @@ -447,8 +449,7 @@ TEST(UnpackSingleDeviceBinaryAr, GivenInvalidGenericIrFileWhenDeviceBinaryNotMat EXPECT_TRUE(unpacked.intermediateRepresentation.empty()); } - -TEST(UnpackSingleDeviceBinaryAr, WhenDeviceBinaryMatchedButHasNoIrAndGenericIrFileAvailableThenUseBinaryWithAssignedGenericIr) { +TEST(UnpackSingleDeviceBinaryAr, WhenMatchingDeviceBinaryPresentAndGenericIrExistsThenGenericIrIsIgnored) { PatchTokensTestData::ValidEmptyProgram programTokens; std::string requiredProduct = NEO::hardwarePrefix[productFamily]; std::string requiredStepping = std::to_string(programTokens.header->SteppingId); @@ -457,35 +458,77 @@ TEST(UnpackSingleDeviceBinaryAr, WhenDeviceBinaryMatchedButHasNoIrAndGenericIrFi NEO::Ar::ArEncoder encoder{true}; ASSERT_TRUE(encoder.appendFileEntry(requiredPointerSize + "." + requiredProduct + "." + requiredStepping, programTokens.storage)); + NEO::Elf::ElfEncoder elfEncoderIr; + elfEncoderIr.getElfFileHeader().type = NEO::Elf::ET_OPENCL_OBJECTS; + const std::string ignoredSpirvContent{"\x07\x23\x02\x03Dummy SPIR-V that should be ignored"}; + const auto spirvFile{ArrayRef::fromAny(ignoredSpirvContent.data(), ignoredSpirvContent.size())}; + elfEncoderIr.appendSection(NEO::Elf::SHT_OPENCL_SPIRV, NEO::Elf::SectionNamesOpenCl::spirvObject, spirvFile); + const auto elfIrData = elfEncoderIr.encode(); + ASSERT_FALSE(elfIrData.empty()); + ASSERT_TRUE(encoder.appendFileEntry("generic_ir", ArrayRef(elfIrData))); + + NEO::TargetDevice target{}; + target.coreFamily = static_cast(programTokens.header->Device); + target.stepping = programTokens.header->SteppingId; + target.maxPointerSizeInBytes = programTokens.header->GPUPointerSizeInBytes; + target.aotConfig = 0; + + auto arData = encoder.encode(); + std::string unpackErrors; + std::string unpackWarnings; + + auto unpacked = NEO::unpackSingleDeviceBinary( + arData, requiredProduct, target, unpackErrors, unpackWarnings); + + EXPECT_TRUE(unpackErrors.empty()) << unpackErrors; + EXPECT_TRUE(unpackWarnings.empty()) << unpackWarnings; + + EXPECT_FALSE(unpacked.deviceBinary.empty()); + EXPECT_TRUE(unpacked.intermediateRepresentation.empty()); +} + +TEST(UnpackSingleDeviceBinaryAr, WhenNoDeviceBinaryAndGenericIrAvailableThenGenericIrIsUsed) { + PatchTokensTestData::ValidEmptyProgram programTokens; + std::string requiredProduct = NEO::hardwarePrefix[productFamily]; + std::string requiredPointerSize = (programTokens.header->GPUPointerSizeInBytes == 4) ? "32" : "64"; + + NEO::Ar::ArEncoder encoder{true}; + NEO::Elf::ElfEncoder elfEncoderIr; elfEncoderIr.getElfFileHeader().type = NEO::Elf::ET_OPENCL_OBJECTS; const std::string customSprivContent{"\x07\x23\x02\x03This is a custom file, with SPIR-V magic!"}; const auto spirvFile{ArrayRef::fromAny(customSprivContent.data(), customSprivContent.size())}; elfEncoderIr.appendSection(NEO::Elf::SHT_OPENCL_SPIRV, NEO::Elf::SectionNamesOpenCl::spirvObject, spirvFile); - const auto elfIrData = elfEncoderIr.encode(); ASSERT_TRUE(encoder.appendFileEntry("generic_ir", ArrayRef(elfIrData))); - NEO::TargetDevice target; + NEO::TargetDevice target{}; target.coreFamily = static_cast(programTokens.header->Device); target.stepping = programTokens.header->SteppingId; target.maxPointerSizeInBytes = programTokens.header->GPUPointerSizeInBytes; + target.aotConfig = 0; auto arData = encoder.encode(); std::string unpackErrors; std::string unpackWarnings; auto unpacked = NEO::unpackSingleDeviceBinary(arData, requiredProduct, target, unpackErrors, unpackWarnings); + EXPECT_TRUE(unpackErrors.empty()) << unpackErrors; EXPECT_TRUE(unpackWarnings.empty()) << unpackWarnings; + EXPECT_TRUE(unpacked.deviceBinary.empty()); ASSERT_FALSE(unpacked.intermediateRepresentation.empty()); - ASSERT_EQ(customSprivContent.size(), unpacked.intermediateRepresentation.size()); - const auto isSpirvSameAsInGenericIr = std::memcmp(customSprivContent.data(), unpacked.intermediateRepresentation.begin(), customSprivContent.size()) == 0; - EXPECT_TRUE(isSpirvSameAsInGenericIr); + EXPECT_EQ(elfIrData.size(), unpacked.intermediateRepresentation.size()); - EXPECT_FALSE(unpacked.deviceBinary.empty()); + const uint8_t *irBegin = unpacked.intermediateRepresentation.begin(); + const uint8_t *irEnd = irBegin + unpacked.intermediateRepresentation.size(); + const uint8_t *patternBegin = reinterpret_cast(customSprivContent.data()); + const uint8_t *patternEnd = patternBegin + customSprivContent.size(); + + auto it = std::search(irBegin, irEnd, patternBegin, patternEnd); + EXPECT_NE(it, irEnd) << "SPIR-V section payload not found inside generic_ir ELF"; } TEST(UnpackSingleDeviceBinaryAr, WhenDeviceBinaryMatchedAndHasIrAndGenericIrFileAvailableThenUseBinaryAndItsIr) { @@ -613,3 +656,38 @@ TEST(UnpackSingleDeviceBinaryAr, WhenCouldNotFindBinaryWithRightPointerSizeThenU EXPECT_TRUE(unpackWarnings.empty()) << unpackWarnings; EXPECT_STREQ("Couldn't find matching binary in AR archive", unpackErrors.c_str()); } + +TEST(UnpackSingleDeviceBinaryAr, WhenRequestedDeviceHasCompatibleFallbackThenUseFallbackDevice) { + PatchTokensTestData::ValidEmptyProgram programTokens; + + std::string requestedProduct = "lnl"; + std::string fallbackProduct = "bmg"; + + auto compatibleDevices = AOT::getCompatibilityFallbackProductAbbreviations(requestedProduct); + if (compatibleDevices.empty() || + std::find(compatibleDevices.begin(), compatibleDevices.end(), fallbackProduct) == compatibleDevices.end()) { + GTEST_SKIP(); + } + + NEO::Ar::ArEncoder encoder{true}; + std::string pointerSize = (programTokens.header->GPUPointerSizeInBytes == 4) ? "32" : "64"; + + ASSERT_TRUE(encoder.appendFileEntry(pointerSize + "." + fallbackProduct, programTokens.storage)); + + NEO::TargetDevice target; + target.coreFamily = static_cast(programTokens.header->Device); + target.stepping = programTokens.header->SteppingId; + target.maxPointerSizeInBytes = programTokens.header->GPUPointerSizeInBytes; + + auto arData = encoder.encode(); + std::string unpackErrors; + std::string unpackWarnings; + + auto unpacked = NEO::unpackSingleDeviceBinary(arData, requestedProduct, target, unpackErrors, unpackWarnings); + + EXPECT_TRUE(unpackErrors.empty()); + EXPECT_FALSE(unpacked.deviceBinary.empty()); + EXPECT_EQ(NEO::DeviceBinaryFormat::patchtokens, unpacked.format); + + EXPECT_STREQ("Couldn't find perfectly matched binary in AR, using best usable", unpackWarnings.c_str()); +} \ No newline at end of file