From 7fc44aa60e4d220025b1f6bb2e5b2600f1a7be51 Mon Sep 17 00:00:00 2001 From: Jaroslaw Chodor Date: Thu, 2 Apr 2020 15:00:23 +0200 Subject: [PATCH] Support for clLink with spec constants Resolves: NEO-4537 Change-Id: Ic9a9ea8a7d4a83e6a308735b653bdd334cb88bf9 Signed-off-by: Jaroslaw Chodor --- opencl/source/program/link.cpp | 17 ++++ .../unit_test/api/cl_link_program_tests.inl | 97 +++++++++++++++++++ shared/source/device_binary_format/elf/elf.h | 2 +- .../source/device_binary_format/elf/ocl_elf.h | 28 +++--- 4 files changed, 131 insertions(+), 13 deletions(-) diff --git a/opencl/source/program/link.cpp b/opencl/source/program/link.cpp index 112fb17d94..3439e52826 100644 --- a/opencl/source/program/link.cpp +++ b/opencl/source/program/link.cpp @@ -80,6 +80,8 @@ cl_int Program::link( elfEncoder.getElfFileHeader().type = NEO::Elf::ET_OPENCL_OBJECTS; StackVec inputProgramsInternal; + StackVec specConstIds; + StackVec specConstValues; for (cl_uint i = 0; i < numInputPrograms; i++) { auto program = inputPrograms[i]; if (program == nullptr) { @@ -97,6 +99,21 @@ cl_int Program::link( break; } + if (pInputProgObj->areSpecializationConstantsInitialized) { + specConstIds.clear(); + specConstValues.clear(); + specConstIds.reserve(pInputProgObj->specConstantsValues.size()); + specConstValues.reserve(pInputProgObj->specConstantsValues.size()); + for (const auto &specConst : pInputProgObj->specConstantsValues) { + specConstIds.push_back(specConst.first); + specConstValues.push_back(specConst.second); + } + elfEncoder.appendSection(NEO::Elf::SHT_OPENCL_SPIRV_SC_IDS, NEO::Elf::SectionNamesOpenCl::spirvSpecConstIds, + ArrayRef::fromAny(specConstIds.begin(), specConstIds.size())); + elfEncoder.appendSection(NEO::Elf::SHT_OPENCL_SPIRV_SC_VALUES, NEO::Elf::SectionNamesOpenCl::spirvSpecConstValues, + ArrayRef::fromAny(specConstValues.begin(), specConstValues.size())); + } + auto sectionType = pInputProgObj->getIsSpirV() ? NEO::Elf::SHT_OPENCL_SPIRV : NEO::Elf::SHT_OPENCL_LLVM_BINARY; ConstStringRef sectionName = pInputProgObj->getIsSpirV() ? NEO::Elf::SectionNamesOpenCl::spirvObject : NEO::Elf::SectionNamesOpenCl::llvmObject; elfEncoder.appendSection(sectionType, sectionName, ArrayRef(reinterpret_cast(pInputProgObj->irBinary.get()), pInputProgObj->irBinarySize)); diff --git a/opencl/test/unit_test/api/cl_link_program_tests.inl b/opencl/test/unit_test/api/cl_link_program_tests.inl index bb3ac013db..c6c1665c4e 100644 --- a/opencl/test/unit_test/api/cl_link_program_tests.inl +++ b/opencl/test/unit_test/api/cl_link_program_tests.inl @@ -6,9 +6,11 @@ */ #include "shared/source/compiler_interface/compiler_interface.h" +#include "shared/source/device_binary_format/elf/elf_decoder.h" #include "shared/source/helpers/file_io.h" #include "opencl/source/context/context.h" +#include "opencl/test/unit_test/global_environment.h" #include "opencl/test/unit_test/helpers/test_files.h" #include "cl_api_tests.h" @@ -156,4 +158,99 @@ TEST_F(clLinkProgramTests, GivenNullContextWhenLinkingProgramThenClInvalidContex EXPECT_EQ(CL_INVALID_CONTEXT, retVal); EXPECT_EQ(nullptr, oprog); } + +template +std::vector asVec(const uint8_t *src, size_t size) { + auto beg = reinterpret_cast(src); + auto end = beg + size / sizeof(T); + return std::vector(beg, end); +} + +TEST_F(clLinkProgramTests, GivenProgramsWithSpecConstantsThenSpecConstantsAreEmbeddedIntoElf) { + uint8_t ir1[] = {15, 17, 19, 23}; + uint8_t ir2[] = {29, 31, 37, 41}; + uint8_t ir3[] = {43, 47, 53, 59}; + uint32_t prog1Keys[2] = {2, 3}; + uint64_t prog1Values[2] = {5, 7}; + uint32_t prog2Keys[1] = {11}; + uint64_t prog2Values[1] = {13}; + + auto progSrc1 = clUniquePtr(new MockProgram(*pProgram->getDevice().getExecutionEnvironment())); + progSrc1->pDevice = pProgram->pDevice; + progSrc1->specConstantsValues[prog1Keys[0]] = prog1Values[0]; + progSrc1->specConstantsValues[prog1Keys[1]] = prog1Values[1]; + progSrc1->areSpecializationConstantsInitialized = true; + progSrc1->irBinary = makeCopy(ir1, sizeof(ir1)); + progSrc1->irBinarySize = sizeof(ir1); + progSrc1->isSpirV = true; + + auto progSrc2 = clUniquePtr(new MockProgram(*pProgram->getDevice().getExecutionEnvironment())); + progSrc2->pDevice = pProgram->pDevice; + progSrc2->specConstantsValues[prog2Keys[0]] = prog2Values[0]; + progSrc2->areSpecializationConstantsInitialized = true; + progSrc2->irBinary = makeCopy(ir2, sizeof(ir2)); + progSrc2->irBinarySize = sizeof(ir2); + progSrc2->isSpirV = true; + + auto progSrc3 = clUniquePtr(new MockProgram(*pProgram->getDevice().getExecutionEnvironment())); + progSrc3->pDevice = pProgram->pDevice; + progSrc3->irBinary = makeCopy(ir3, sizeof(ir3)); + progSrc3->irBinarySize = sizeof(ir3); + progSrc3->isSpirV = true; + + auto progDst = clUniquePtr(new MockProgram(*pProgram->getDevice().getExecutionEnvironment())); + progDst->pDevice = pProgram->pDevice; + cl_program inputPrograms[3] = {progSrc1.get(), progSrc2.get(), progSrc3.get()}; + + std::string receivedInput; + MockCompilerDebugVars igcDebugVars; + igcDebugVars.receivedInput = &receivedInput; + gEnvironment->igcPushDebugVars(igcDebugVars); + progDst->link(0U, nullptr, "", 3, inputPrograms, nullptr, nullptr); + gEnvironment->igcPopDebugVars(); + + std::string elfDecodeError; + std::string elfDecoceWarnings; + auto elf = NEO::Elf::decodeElf(ArrayRef::fromAny(receivedInput.data(), receivedInput.size()), + elfDecodeError, elfDecoceWarnings); + ASSERT_NE(nullptr, elf.elfFileHeader) << elfDecodeError; + + ASSERT_EQ(8U, elf.sectionHeaders.size()); + EXPECT_EQ(NEO::Elf::SHT_OPENCL_SPIRV_SC_IDS, elf.sectionHeaders[1].header->type); + EXPECT_EQ(NEO::Elf::SHT_OPENCL_SPIRV_SC_VALUES, elf.sectionHeaders[2].header->type); + EXPECT_EQ(NEO::Elf::SHT_OPENCL_SPIRV, elf.sectionHeaders[3].header->type); + EXPECT_EQ(NEO::Elf::SHT_OPENCL_SPIRV_SC_IDS, elf.sectionHeaders[4].header->type); + EXPECT_EQ(NEO::Elf::SHT_OPENCL_SPIRV_SC_VALUES, elf.sectionHeaders[5].header->type); + EXPECT_EQ(NEO::Elf::SHT_OPENCL_SPIRV, elf.sectionHeaders[6].header->type); + EXPECT_EQ(NEO::Elf::SHT_OPENCL_SPIRV, elf.sectionHeaders[7].header->type); + + ASSERT_EQ(sizeof(uint32_t) * progSrc1->specConstantsValues.size(), elf.sectionHeaders[1].data.size()); + ASSERT_EQ(sizeof(uint64_t) * progSrc1->specConstantsValues.size(), elf.sectionHeaders[2].data.size()); + ASSERT_EQ(sizeof(ir1), elf.sectionHeaders[3].data.size()); + ASSERT_EQ(sizeof(uint32_t) * progSrc2->specConstantsValues.size(), elf.sectionHeaders[4].data.size()); + ASSERT_EQ(sizeof(uint64_t) * progSrc2->specConstantsValues.size(), elf.sectionHeaders[5].data.size()); + ASSERT_EQ(sizeof(ir2), elf.sectionHeaders[6].data.size()); + ASSERT_EQ(sizeof(ir3), elf.sectionHeaders[7].data.size()); + + auto readSpecConstId = [](NEO::Elf::Elf::SectionHeaderAndData §ion, uint32_t offset) { + return *(reinterpret_cast(section.data.begin()) + offset); + }; + + auto readSpecConstValue = [](NEO::Elf::Elf::SectionHeaderAndData §ion, uint32_t offset) { + return *(reinterpret_cast(section.data.begin()) + offset); + }; + + ASSERT_EQ(1U, progSrc1->specConstantsValues.count(readSpecConstId(elf.sectionHeaders[1], 0))); + EXPECT_EQ(progSrc1->specConstantsValues[readSpecConstId(elf.sectionHeaders[1], 0)], readSpecConstValue(elf.sectionHeaders[2], 0)); + ASSERT_EQ(1U, progSrc1->specConstantsValues.count(readSpecConstId(elf.sectionHeaders[1], 1))); + EXPECT_EQ(progSrc1->specConstantsValues[readSpecConstId(elf.sectionHeaders[1], 1)], readSpecConstValue(elf.sectionHeaders[2], 1)); + EXPECT_EQ(0, memcmp(ir1, elf.sectionHeaders[3].data.begin(), sizeof(ir1))); + + ASSERT_EQ(1U, progSrc2->specConstantsValues.count(readSpecConstId(elf.sectionHeaders[4], 0))); + EXPECT_EQ(progSrc2->specConstantsValues[readSpecConstId(elf.sectionHeaders[4], 0)], readSpecConstValue(elf.sectionHeaders[5], 0)); + EXPECT_EQ(0, memcmp(ir2, elf.sectionHeaders[6].data.begin(), sizeof(ir2))); + + EXPECT_EQ(0, memcmp(ir3, elf.sectionHeaders[7].data.begin(), sizeof(ir3))); +} + } // namespace ULT diff --git a/shared/source/device_binary_format/elf/elf.h b/shared/source/device_binary_format/elf/elf.h index 55cb5ed7b9..333b29810b 100644 --- a/shared/source/device_binary_format/elf/elf.h +++ b/shared/source/device_binary_format/elf/elf.h @@ -76,7 +76,7 @@ enum SECTION_HEADER_TYPE : uint32_t { SHT_NUM = 19, // number of defined types SHT_LOOS = 0x60000000, // start of os-specifc SHT_OPENCL_RESERVED_START = 0xff000000, // start of Intel OCL SHT_TYPES - SHT_OPENCL_RESERVED_END = 0xff00000a // end of Intel OCL SHT_TYPES + SHT_OPENCL_RESERVED_END = 0xff00000c // end of Intel OCL SHT_TYPES }; enum SPECIAL_SECTION_HEADER_NUMBER : uint16_t { diff --git a/shared/source/device_binary_format/elf/ocl_elf.h b/shared/source/device_binary_format/elf/ocl_elf.h index 368a13f20e..0cda25521a 100644 --- a/shared/source/device_binary_format/elf/ocl_elf.h +++ b/shared/source/device_binary_format/elf/ocl_elf.h @@ -30,21 +30,23 @@ static_assert(static_cast(ET_OPENCL_SOURCE) == static_cast(E static_assert(static_cast(ET_OPENCL_DEBUG) == static_cast(ET_OPENCL_RESERVED_END), ""); enum SHT_OPENCL : uint32_t { - SHT_OPENCL_SOURCE = 0xff000000, // CL source to link into LLVM binary - SHT_OPENCL_HEADER = 0xff000001, // CL header to link into LLVM binary - SHT_OPENCL_LLVM_TEXT = 0xff000002, // LLVM text - SHT_OPENCL_LLVM_BINARY = 0xff000003, // LLVM byte code - SHT_OPENCL_LLVM_ARCHIVE = 0xff000004, // LLVM archives(s) - SHT_OPENCL_DEV_BINARY = 0xff000005, // Device binary (coherent by default) - SHT_OPENCL_OPTIONS = 0xff000006, // CL Options - SHT_OPENCL_PCH = 0xff000007, // PCH (pre-compiled headers) - SHT_OPENCL_DEV_DEBUG = 0xff000008, // Device debug - SHT_OPENCL_SPIRV = 0xff000009, // SPIRV - SHT_NON_COHERENT_DEV_BINARY = 0xff00000a, // Non-coherent Device binary + SHT_OPENCL_SOURCE = 0xff000000, // CL source to link into LLVM binary + SHT_OPENCL_HEADER = 0xff000001, // CL header to link into LLVM binary + SHT_OPENCL_LLVM_TEXT = 0xff000002, // LLVM text + SHT_OPENCL_LLVM_BINARY = 0xff000003, // LLVM byte code + SHT_OPENCL_LLVM_ARCHIVE = 0xff000004, // LLVM archives(s) + SHT_OPENCL_DEV_BINARY = 0xff000005, // Device binary (coherent by default) + SHT_OPENCL_OPTIONS = 0xff000006, // CL Options + SHT_OPENCL_PCH = 0xff000007, // PCH (pre-compiled headers) + SHT_OPENCL_DEV_DEBUG = 0xff000008, // Device debug + SHT_OPENCL_SPIRV = 0xff000009, // SPIRV + SHT_OPENCL_NON_COHERENT_DEV_BINARY = 0xff00000a, // Non-coherent Device binary + SHT_OPENCL_SPIRV_SC_IDS = 0xff00000b, // Specialization Constants IDs + SHT_OPENCL_SPIRV_SC_VALUES = 0xff00000c // Specialization Constants values }; static_assert(sizeof(SHT_OPENCL) == sizeof(SECTION_HEADER_TYPE), ""); static_assert(static_cast(SHT_OPENCL_SOURCE) == static_cast(SHT_OPENCL_RESERVED_START), ""); -static_assert(static_cast(SHT_NON_COHERENT_DEV_BINARY) == static_cast(SHT_OPENCL_RESERVED_END), ""); +static_assert(static_cast(SHT_OPENCL_SPIRV_SC_VALUES) == static_cast(SHT_OPENCL_RESERVED_END), ""); namespace SectionNamesOpenCl { static constexpr ConstStringRef buildOptions = "BuildOptions"; @@ -52,6 +54,8 @@ static constexpr ConstStringRef spirvObject = "SPIRV Object"; static constexpr ConstStringRef llvmObject = "Intel(R) OpenCL LLVM Object"; static constexpr ConstStringRef deviceDebug = "Intel(R) OpenCL Device Debug"; static constexpr ConstStringRef deviceBinary = "Intel(R) OpenCL Device Binary"; +static constexpr ConstStringRef spirvSpecConstIds = "SPIRV Specialization Constants Ids"; +static constexpr ConstStringRef spirvSpecConstValues = "SPIRV Specialization Constants Values"; } // namespace SectionNamesOpenCl } // namespace Elf