text: /*
   * Copyright (C) 2017-2020 Intel Corporation
   *
   * SPDX-License-Identifier: MIT
   *
   */
  
  #include "shared/source/helpers/string.h"
  #include "shared/source/memory_manager/allocations_list.h"
  #include "shared/source/memory_manager/graphics_allocation.h"
  #include "shared/source/memory_manager/unified_memory_manager.h"
  #include "shared/source/program/program_info_from_patchtokens.h"
  #include "shared/test/unit_test/compiler_interface/linker_mock.h"
  #include "shared/test/unit_test/device_binary_format/patchtokens_tests.h"
  
  #include "opencl/source/platform/platform.h"
  #include "opencl/source/program/program.h"
  #include "opencl/test/unit_test/mocks/mock_buffer.h"
  #include "opencl/test/unit_test/mocks/mock_cl_device.h"
  #include "opencl/test/unit_test/mocks/mock_csr.h"
  #include "opencl/test/unit_test/mocks/mock_execution_environment.h"
  #include "opencl/test/unit_test/mocks/mock_memory_manager.h"
  #include "opencl/test/unit_test/mocks/mock_program.h"
  #include "opencl/test/unit_test/program/program_with_source.h"
  #include "test.h"
  
  using namespace NEO;
  
  using namespace iOpenCL;
  static const char constValue[] = "11223344";
  static const char globalValue[] = "55667788";
  
  class ProgramDataTestBase : public testing::Test,
                              public ContextFixture,
                              public PlatformFixture,
                              public ProgramFixture {
  
      using ContextFixture::SetUp;
      using PlatformFixture::SetUp;
  
    public:
      ProgramDataTestBase() {
          memset(&programBinaryHeader, 0x00, sizeof(SProgramBinaryHeader));
          pCurPtr = nullptr;
          pProgramPatchList = nullptr;
          programPatchListSize = 0;
      }
  
      void buildAndDecodeProgramPatchList();
  
      void SetUp() override {
          PlatformFixture::SetUp();
          cl_device_id device = pPlatform->getClDevice(0);
          ContextFixture::SetUp(1, &device);
          ProgramFixture::SetUp();
  
          CreateProgramWithSource(
              pContext,
              &device,
              "CopyBuffer_simd16.cl");
      }
  
      void TearDown() override {
          knownSource.reset();
          ProgramFixture::TearDown();
          ContextFixture::TearDown();
          PlatformFixture::TearDown();
      }
  
      size_t setupConstantAllocation() {
          size_t constSize = strlen(constValue) + 1;
  
          EXPECT_EQ(nullptr, pProgram->getConstantSurface());
  
          SPatchAllocateConstantMemorySurfaceProgramBinaryInfo allocateConstMemorySurface;
          allocateConstMemorySurface.Token = PATCH_TOKEN_ALLOCATE_CONSTANT_MEMORY_SURFACE_PROGRAM_BINARY_INFO;
          allocateConstMemorySurface.Size = static_cast<uint32_t>(sizeof(SPatchAllocateConstantMemorySurfaceProgramBinaryInfo));
  
          allocateConstMemorySurface.ConstantBufferIndex = 0;
          allocateConstMemorySurface.InlineDataSize = static_cast<uint32_t>(constSize);
  
          pAllocateConstMemorySurface.reset(new cl_char[allocateConstMemorySurface.Size + constSize]);
  
          memcpy_s(pAllocateConstMemorySurface.get(),
                   sizeof(SPatchAllocateConstantMemorySurfaceProgramBinaryInfo),
                   &allocateConstMemorySurface,
                   sizeof(SPatchAllocateConstantMemorySurfaceProgramBinaryInfo));
  
          memcpy_s((cl_char *)pAllocateConstMemorySurface.get() + sizeof(allocateConstMemorySurface), constSize, constValue, constSize);
  
          pProgramPatchList = (void *)pAllocateConstMemorySurface.get();
          programPatchListSize = static_cast<uint32_t>(allocateConstMemorySurface.Size + constSize);
          return constSize;
      }
  
      size_t setupGlobalAllocation() {
          size_t globalSize = strlen(globalValue) + 1;
  
          EXPECT_EQ(nullptr, pProgram->getGlobalSurface());
  
          SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo allocateGlobalMemorySurface;
          allocateGlobalMemorySurface.Token = PATCH_TOKEN_ALLOCATE_GLOBAL_MEMORY_SURFACE_PROGRAM_BINARY_INFO;
          allocateGlobalMemorySurface.Size = static_cast<uint32_t>(sizeof(SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo));
  
          allocateGlobalMemorySurface.GlobalBufferIndex = 0;
          allocateGlobalMemorySurface.InlineDataSize = static_cast<uint32_t>(globalSize);
  
          pAllocateGlobalMemorySurface.reset(new cl_char[allocateGlobalMemorySurface.Size + globalSize]);
  
          memcpy_s(pAllocateGlobalMemorySurface.get(),
                   sizeof(SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo),
                   &allocateGlobalMemorySurface,
                   sizeof(SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo));
  
          memcpy_s((cl_char *)pAllocateGlobalMemorySurface.get() + sizeof(allocateGlobalMemorySurface), globalSize, globalValue, globalSize);
  
          pProgramPatchList = pAllocateGlobalMemorySurface.get();
          programPatchListSize = static_cast<uint32_t>(allocateGlobalMemorySurface.Size + globalSize);
          return globalSize;
      }
      std::unique_ptr<cl_char[]> pAllocateConstMemorySurface;
      std::unique_ptr<cl_char[]> pAllocateGlobalMemorySurface;
      char *pCurPtr;
      SProgramBinaryHeader programBinaryHeader;
      void *pProgramPatchList;
      uint32_t programPatchListSize;
      cl_int patchlistDecodeErrorCode = 0;
      bool allowDecodeFailure = false;
  };
  
  void ProgramDataTestBase::buildAndDecodeProgramPatchList() {
      size_t headerSize = sizeof(SProgramBinaryHeader);
  
      cl_int error = CL_SUCCESS;
      programBinaryHeader.Magic = 0x494E5443;
      programBinaryHeader.Version = CURRENT_ICBE_VERSION;
      programBinaryHeader.Device = defaultHwInfo->platform.eRenderCoreFamily;
      programBinaryHeader.GPUPointerSizeInBytes = 8;
      programBinaryHeader.NumberOfKernels = 0;
      programBinaryHeader.PatchListSize = programPatchListSize;
  
      char *pProgramData = new char[headerSize + programBinaryHeader.PatchListSize];
      ASSERT_NE(nullptr, pProgramData);
  
      pCurPtr = pProgramData;
  
      // program header
      memset(pCurPtr, 0, sizeof(SProgramBinaryHeader));
      *(SProgramBinaryHeader *)pCurPtr = programBinaryHeader;
  
      pCurPtr += sizeof(SProgramBinaryHeader);
  
      // patch list
      memcpy_s(pCurPtr, programPatchListSize, pProgramPatchList, programPatchListSize);
      pCurPtr += programPatchListSize;
  
      //as we use mock compiler in unit test, replace the genBinary here.
      pProgram->unpackedDeviceBinary = makeCopy(pProgramData, headerSize + programBinaryHeader.PatchListSize);
      pProgram->unpackedDeviceBinarySize = headerSize + programBinaryHeader.PatchListSize;
  
      error = pProgram->processGenBinary();
      patchlistDecodeErrorCode = error;
      if (allowDecodeFailure == false) {
          EXPECT_EQ(CL_SUCCESS, error);
      }
      delete[] pProgramData;
  }
  
  using ProgramDataTest = ProgramDataTestBase;
  
  TEST_F(ProgramDataTest, EmptyProgramBinaryHeader) {
      buildAndDecodeProgramPatchList();
  }
  
  TEST_F(ProgramDataTest, AllocateConstantMemorySurfaceProgramBinaryInfo) {
  
      auto constSize = setupConstantAllocation();
  
      buildAndDecodeProgramPatchList();
  
      EXPECT_NE(nullptr, pProgram->getConstantSurface());
      EXPECT_EQ(0, memcmp(constValue, pProgram->getConstantSurface()->getUnderlyingBuffer(), constSize));
  }
  
  TEST_F(ProgramDataTest, whenGlobalConstantsAreExportedThenAllocateSurfacesAsSvm) {
      if (this->pContext->getSVMAllocsManager() == nullptr) {
          return;
      }
  
      char constantData[128] = {};
      ProgramInfo programInfo;
      programInfo.globalConstants.initData = constantData;
      programInfo.globalConstants.size = sizeof(constantData);
      std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
      mockLinkerInput->traits.exportsGlobalConstants = true;
      programInfo.linkerInput = std::move(mockLinkerInput);
      this->pProgram->processProgramInfo(programInfo);
  
      ASSERT_NE(nullptr, pProgram->getConstantSurface());
      EXPECT_NE(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getConstantSurface()->getGpuAddress())));
  }
  
  TEST_F(ProgramDataTest, whenGlobalConstantsAreNotExportedThenAllocateSurfacesAsNonSvm) {
      if (this->pContext->getSVMAllocsManager() == nullptr) {
          return;
      }
  
      char constantData[128] = {};
      ProgramInfo programInfo;
      programInfo.globalConstants.initData = constantData;
      programInfo.globalConstants.size = sizeof(constantData);
      std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
      mockLinkerInput->traits.exportsGlobalConstants = false;
      programInfo.linkerInput = std::move(mockLinkerInput);
      this->pProgram->processProgramInfo(programInfo);
  
      ASSERT_NE(nullptr, pProgram->getConstantSurface());
      EXPECT_EQ(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getConstantSurface()->getGpuAddress())));
  }
  
  TEST_F(ProgramDataTest, whenGlobalConstantsAreExportedButContextUnavailableThenAllocateSurfacesAsNonSvm) {
      if (this->pContext->getSVMAllocsManager() == nullptr) {
          return;
      }
  
      char constantData[128] = {};
      ProgramInfo programInfo;
      programInfo.globalConstants.initData = constantData;
      programInfo.globalConstants.size = sizeof(constantData);
      std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
      mockLinkerInput->traits.exportsGlobalConstants = true;
      programInfo.linkerInput = std::move(mockLinkerInput);
  
      pProgram->context = nullptr;
  
      this->pProgram->processProgramInfo(programInfo);
  
      pProgram->context = pContext;
  
      ASSERT_NE(nullptr, pProgram->getConstantSurface());
      EXPECT_EQ(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getConstantSurface()->getGpuAddress())));
  }
  
  TEST_F(ProgramDataTest, whenGlobalVariablesAreExportedThenAllocateSurfacesAsSvm) {
      if (this->pContext->getSVMAllocsManager() == nullptr) {
          return;
      }
      char globalData[128] = {};
      ProgramInfo programInfo;
      programInfo.globalVariables.initData = globalData;
      programInfo.globalVariables.size = sizeof(globalData);
      std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
      mockLinkerInput->traits.exportsGlobalVariables = true;
      programInfo.linkerInput = std::move(mockLinkerInput);
      this->pProgram->processProgramInfo(programInfo);
  
      ASSERT_NE(nullptr, pProgram->getGlobalSurface());
      EXPECT_NE(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getGlobalSurface()->getGpuAddress())));
  }
  
  TEST_F(ProgramDataTest, whenGlobalVariablesAreExportedButContextUnavailableThenAllocateSurfacesAsNonSvm) {
      if (this->pContext->getSVMAllocsManager() == nullptr) {
          return;
      }
  
      char globalData[128] = {};
      ProgramInfo programInfo;
      programInfo.globalVariables.initData = globalData;
      programInfo.globalVariables.size = sizeof(globalData);
      std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
      mockLinkerInput->traits.exportsGlobalVariables = true;
      programInfo.linkerInput = std::move(mockLinkerInput);
  
      pProgram->context = nullptr;
  
      this->pProgram->processProgramInfo(programInfo);
  
      pProgram->context = pContext;
  
      ASSERT_NE(nullptr, pProgram->getGlobalSurface());
      EXPECT_EQ(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getGlobalSurface()->getGpuAddress())));
  }
  
  TEST_F(ProgramDataTest, whenGlobalVariablesAreNotExportedThenAllocateSurfacesAsNonSvm) {
      if (this->pContext->getSVMAllocsManager() == nullptr) {
          return;
      }
  
      char globalData[128] = {};
      ProgramInfo programInfo;
      programInfo.globalVariables.initData = globalData;
      programInfo.globalVariables.size = sizeof(globalData);
      std::unique_ptr<WhiteBox<NEO::LinkerInput>> mockLinkerInput = std::make_unique<WhiteBox<NEO::LinkerInput>>();
      mockLinkerInput->traits.exportsGlobalVariables = false;
      programInfo.linkerInput = std::move(mockLinkerInput);
      this->pProgram->processProgramInfo(programInfo);
  
      ASSERT_NE(nullptr, pProgram->getGlobalSurface());
      EXPECT_EQ(nullptr, this->pContext->getSVMAllocsManager()->getSVMAlloc(reinterpret_cast<const void *>(pProgram->getGlobalSurface()->getGpuAddress())));
  }
  
  TEST_F(ProgramDataTest, givenConstantAllocationThatIsInUseByGpuWhenProgramIsBeingDestroyedThenItIsAddedToTemporaryAllocationList) {
  
      setupConstantAllocation();
  
      buildAndDecodeProgramPatchList();
  
      auto &csr = *pPlatform->getClDevice(0)->getDefaultEngine().commandStreamReceiver;
      auto tagAddress = csr.getTagAddress();
      auto constantSurface = pProgram->getConstantSurface();
      constantSurface->updateTaskCount(*tagAddress + 1, csr.getOsContext().getContextId());
  
      EXPECT_TRUE(csr.getTemporaryAllocations().peekIsEmpty());
      delete pProgram;
      pProgram = nullptr;
      EXPECT_FALSE(csr.getTemporaryAllocations().peekIsEmpty());
      EXPECT_EQ(constantSurface, csr.getTemporaryAllocations().peekHead());
  }
  
  TEST_F(ProgramDataTest, givenGlobalAllocationThatIsInUseByGpuWhenProgramIsBeingDestroyedThenItIsAddedToTemporaryAllocationList) {
      setupGlobalAllocation();
  
      buildAndDecodeProgramPatchList();
  
      auto &csr = *pPlatform->getClDevice(0)->getDefaultEngine().commandStreamReceiver;
      auto tagAddress = csr.getTagAddress();
      auto globalSurface = pProgram->getGlobalSurface();
      globalSurface->updateTaskCount(*tagAddress + 1, csr.getOsContext().getContextId());
  
      EXPECT_TRUE(csr.getTemporaryAllocations().peekIsEmpty());
      delete pProgram;
      pProgram = nullptr;
      EXPECT_FALSE(csr.getTemporaryAllocations().peekIsEmpty());
      EXPECT_EQ(globalSurface, csr.getTemporaryAllocations().peekHead());
  }
  
  TEST_F(ProgramDataTest, GivenDeviceForcing32BitMessagesWhenConstAllocationIsPresentInProgramBinariesThen32BitStorageIsAllocated) {
      auto constSize = setupConstantAllocation();
      this->pContext->getDevice(0)->getMemoryManager()->setForce32BitAllocations(true);
  
      buildAndDecodeProgramPatchList();
  
      EXPECT_NE(nullptr, pProgram->getConstantSurface());
      EXPECT_EQ(0, memcmp(constValue, pProgram->getConstantSurface()->getUnderlyingBuffer(), constSize));
  
      if (is64bit) {
          EXPECT_TRUE(pProgram->getConstantSurface()->is32BitAllocation());
      }
  }
  
  TEST_F(ProgramDataTest, AllocateGlobalMemorySurfaceProgramBinaryInfo) {
      auto globalSize = setupGlobalAllocation();
      buildAndDecodeProgramPatchList();
      EXPECT_NE(nullptr, pProgram->getGlobalSurface());
      EXPECT_EQ(0, memcmp(globalValue, pProgram->getGlobalSurface()->getUnderlyingBuffer(), globalSize));
  }
  
  TEST_F(ProgramDataTest, Given32BitDeviceWhenGlobalMemorySurfaceIsPresentThenItHas32BitStorage) {
      char globalValue[] = "55667788";
      size_t globalSize = strlen(globalValue) + 1;
      this->pContext->getDevice(0)->getMemoryManager()->setForce32BitAllocations(true);
      EXPECT_EQ(nullptr, pProgram->getGlobalSurface());
  
      SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo allocateGlobalMemorySurface;
      allocateGlobalMemorySurface.Token = PATCH_TOKEN_ALLOCATE_GLOBAL_MEMORY_SURFACE_PROGRAM_BINARY_INFO;
      allocateGlobalMemorySurface.Size = static_cast<uint32_t>(sizeof(SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo));
  
      allocateGlobalMemorySurface.GlobalBufferIndex = 0;
      allocateGlobalMemorySurface.InlineDataSize = static_cast<uint32_t>(globalSize);
  
      cl_char *pAllocateGlobalMemorySurface = new cl_char[allocateGlobalMemorySurface.Size + globalSize];
  
      memcpy_s(pAllocateGlobalMemorySurface,
               sizeof(SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo),
               &allocateGlobalMemorySurface,
               sizeof(SPatchAllocateGlobalMemorySurfaceProgramBinaryInfo));
  
      memcpy_s((cl_char *)pAllocateGlobalMemorySurface + sizeof(allocateGlobalMemorySurface), globalSize, globalValue, globalSize);
  
      pProgramPatchList = (void *)pAllocateGlobalMemorySurface;
      programPatchListSize = static_cast<uint32_t>(allocateGlobalMemorySurface.Size + globalSize);
  
      buildAndDecodeProgramPatchList();
  
      EXPECT_NE(nullptr, pProgram->getGlobalSurface());
      EXPECT_EQ(0, memcmp(globalValue, pProgram->getGlobalSurface()->getUnderlyingBuffer(), globalSize));
      if (is64bit) {
          EXPECT_TRUE(pProgram->getGlobalSurface()->is32BitAllocation());
      }
  
      delete[] pAllocateGlobalMemorySurface;
  }
  
  TEST(ProgramScopeMetadataTest, WhenPatchingGlobalSurfaceThenPickProperSourceBuffer) {
      MockExecutionEnvironment execEnv;
      MockClDevice device{new MockDevice};
      execEnv.memoryManager = std::make_unique<MockMemoryManager>();
      PatchTokensTestData::ValidProgramWithMixedGlobalVarAndConstSurfacesAndPointers decodedProgram;
      decodedProgram.globalPointerMutable->GlobalPointerOffset = 0U;
      decodedProgram.constantPointerMutable->ConstantPointerOffset = 0U;
      memset(decodedProgram.globalSurfMutable + 1, 0U, sizeof(uintptr_t));
      memset(decodedProgram.constSurfMutable + 1, 0U, sizeof(uintptr_t));
      ProgramInfo programInfo;
      MockProgram program(execEnv);
      program.pDevice = &device.getDevice();
      NEO::populateProgramInfo(programInfo, decodedProgram);
      program.processProgramInfo(programInfo);
      ASSERT_NE(nullptr, program.globalSurface);
      ASSERT_NE(nullptr, program.constantSurface);
      ASSERT_NE(nullptr, program.globalSurface->getUnderlyingBuffer());
      ASSERT_NE(nullptr, program.constantSurface->getUnderlyingBuffer());
      EXPECT_EQ(static_cast<uintptr_t>(program.globalSurface->getGpuAddressToPatch()), *reinterpret_cast<uintptr_t *>(program.constantSurface->getUnderlyingBuffer()));
      EXPECT_EQ(static_cast<uintptr_t>(program.constantSurface->getGpuAddressToPatch()), *reinterpret_cast<uintptr_t *>(program.globalSurface->getUnderlyingBuffer()));
  }
  
  TEST_F(ProgramDataTest, GivenProgramWith32bitPointerOptWhenProgramScopeConstantBufferPatchTokensAreReadThenConstantPointerOffsetIsPatchedWith32bitPointer) {
      cl_device_id device = pPlatform->getClDevice(0);
      CreateProgramWithSource(pContext, &device, "CopyBuffer_simd16.cl");
      ASSERT_NE(nullptr, pProgram);
  
      MockProgram *prog = pProgram;
  
      // simulate case when constant surface was not allocated
      EXPECT_EQ(nullptr, prog->getConstantSurface());
      ProgramInfo programInfo;
      programInfo.prepareLinkerInputStorage();
      NEO::LinkerInput::RelocationInfo relocInfo;
      relocInfo.relocationSegment = NEO::SegmentType::GlobalConstants;
      relocInfo.symbolSegment = NEO::SegmentType::GlobalConstants;
      relocInfo.offset = 0U;
      relocInfo.type = NEO::LinkerInput::RelocationInfo::Type::Address;
      programInfo.linkerInput->addDataRelocationInfo(relocInfo);
      programInfo.linkerInput->setPointerSize(LinkerInput::Traits::PointerSize::Ptr32bit);
  
      MockBuffer constantSurface;
      ASSERT_LT(8U, constantSurface.getSize());
      prog->setConstantSurface(&constantSurface.mockGfxAllocation);
      constantSurface.mockGfxAllocation.set32BitAllocation(true);
      uint32_t *constantSurfaceStorage = reinterpret_cast<uint32_t *>(constantSurface.getCpuAddress());
      uint32_t sentinel = 0x17192329U;
      constantSurfaceStorage[0] = 0U;
      constantSurfaceStorage[1] = sentinel;
  
      pProgram->linkerInput = std::move(programInfo.linkerInput);
      pProgram->linkBinary();
      uint32_t expectedAddr = static_cast<uint32_t>(constantSurface.getGraphicsAllocation()->getGpuAddressToPatch());
      EXPECT_EQ(expectedAddr, constantSurfaceStorage[0]);
      EXPECT_EQ(sentinel, constantSurfaceStorage[1]);
      constantSurface.mockGfxAllocation.set32BitAllocation(false);
      prog->setConstantSurface(nullptr);
  }
  
  TEST_F(ProgramDataTest, GivenProgramWith32bitPointerOptWhenProgramScopeGlobalPointerPatchTokensAreReadThenGlobalPointerOffsetIsPatchedWith32bitPointer) {
      cl_device_id device = pPlatform->getClDevice(0);
      CreateProgramWithSource(pContext, &device, "CopyBuffer_simd16.cl");
      ASSERT_NE(nullptr, pProgram);
  
      MockProgram *prog = pProgram;
  
      // simulate case when constant surface was not allocated
      EXPECT_EQ(nullptr, prog->getConstantSurface());
  
      ProgramInfo programInfo;
      programInfo.prepareLinkerInputStorage();
      NEO::LinkerInput::RelocationInfo relocInfo;
      relocInfo.relocationSegment = NEO::SegmentType::GlobalVariables;
      relocInfo.symbolSegment = NEO::SegmentType::GlobalVariables;
      relocInfo.offset = 0U;
      relocInfo.type = NEO::LinkerInput::RelocationInfo::Type::Address;
      programInfo.linkerInput->addDataRelocationInfo(relocInfo);
      programInfo.linkerInput->setPointerSize(LinkerInput::Traits::PointerSize::Ptr32bit);
  
      MockBuffer globalSurface;
      ASSERT_LT(8U, globalSurface.getSize());
      prog->setGlobalSurface(&globalSurface.mockGfxAllocation);
      globalSurface.mockGfxAllocation.set32BitAllocation(true);
      uint32_t *globalSurfaceStorage = reinterpret_cast<uint32_t *>(globalSurface.getCpuAddress());
      uint32_t sentinel = 0x17192329U;
      globalSurfaceStorage[0] = 0U;
      globalSurfaceStorage[1] = sentinel;
  
      pProgram->linkerInput = std::move(programInfo.linkerInput);
      pProgram->linkBinary();
      uint32_t expectedAddr = static_cast<uint32_t>(globalSurface.getGraphicsAllocation()->getGpuAddressToPatch());
      EXPECT_EQ(expectedAddr, globalSurfaceStorage[0]);
      EXPECT_EQ(sentinel, globalSurfaceStorage[1]);
      globalSurface.mockGfxAllocation.set32BitAllocation(false);
      prog->setGlobalSurface(nullptr);
  }
  
  TEST_F(ProgramDataTest, givenSymbolTablePatchTokenThenLinkerInputIsCreated) {
      SPatchFunctionTableInfo token;
      token.Token = PATCH_TOKEN_PROGRAM_SYMBOL_TABLE;
      token.Size = static_cast<uint32_t>(sizeof(SPatchFunctionTableInfo));
      token.NumEntries = 0;
  
      pProgramPatchList = &token;
      programPatchListSize = token.Size;
  
      buildAndDecodeProgramPatchList();
  
      EXPECT_NE(nullptr, pProgram->getLinkerInput());
  }
  
  TEST(ProgramLinkBinaryTest, whenLinkerInputEmptyThenLinkSuccessful) {
      auto linkerInput = std::make_unique<WhiteBox<LinkerInput>>();
      NEO::ExecutionEnvironment env;
      MockProgram program{env};
      program.linkerInput = std::move(linkerInput);
      auto ret = program.linkBinary();
      EXPECT_EQ(CL_SUCCESS, ret);
  }
  
  TEST(ProgramLinkBinaryTest, whenLinkerUnresolvedExternalThenLinkFailedAndBuildLogAvailable) {
      auto linkerInput = std::make_unique<WhiteBox<LinkerInput>>();
      NEO::LinkerInput::RelocationInfo relocation = {};
      relocation.symbolName = "A";
      relocation.offset = 0;
      linkerInput->relocations.push_back(NEO::LinkerInput::Relocations{relocation});
      linkerInput->traits.requiresPatchingOfInstructionSegments = true;
      NEO::ExecutionEnvironment env;
      MockProgram program{env};
      KernelInfo kernelInfo = {};
      kernelInfo.name = "onlyKernel";
      std::vector<char> kernelHeap;
      kernelHeap.resize(32, 7);
      kernelInfo.heapInfo.pKernelHeap = kernelHeap.data();
      iOpenCL::SKernelBinaryHeaderCommon kernelHeader = {};
      kernelHeader.KernelHeapSize = static_cast<uint32_t>(kernelHeap.size());
      kernelInfo.heapInfo.pKernelHeader = &kernelHeader;
      program.getKernelInfoArray().push_back(&kernelInfo);
      program.linkerInput = std::move(linkerInput);
  
      EXPECT_EQ(nullptr, program.getBuildLog(nullptr));
      auto ret = program.linkBinary();
      EXPECT_NE(CL_SUCCESS, ret);
      program.getKernelInfoArray().clear();
      auto buildLog = program.getBuildLog(nullptr);
      ASSERT_NE(nullptr, buildLog);
      Linker::UnresolvedExternals expectedUnresolvedExternals;
      expectedUnresolvedExternals.push_back(Linker::UnresolvedExternal{relocation, 0, false});
      auto expectedError = constructLinkerErrorMessage(expectedUnresolvedExternals, std::vector<std::string>{"kernel : " + kernelInfo.name});
      EXPECT_THAT(buildLog, ::testing::HasSubstr(expectedError));
  }
  
  TEST_F(ProgramDataTest, whenLinkerInputValidThenIsaIsProperlyPatched) {
      auto linkerInput = std::make_unique<WhiteBox<LinkerInput>>();
      linkerInput->symbols["A"] = NEO::SymbolInfo{4U, 4U, NEO::SegmentType::GlobalVariables};
      linkerInput->symbols["B"] = NEO::SymbolInfo{8U, 4U, NEO::SegmentType::GlobalConstants};
      linkerInput->symbols["C"] = NEO::SymbolInfo{16U, 4U, NEO::SegmentType::Instructions};
  
      auto relocationType = NEO::LinkerInput::RelocationInfo::Type::Address;
      linkerInput->relocations.push_back({NEO::LinkerInput::RelocationInfo{"A", 8U, relocationType},
                                          NEO::LinkerInput::RelocationInfo{"B", 16U, relocationType},
                                          NEO::LinkerInput::RelocationInfo{"C", 24U, relocationType}});
      linkerInput->traits.requiresPatchingOfInstructionSegments = true;
      linkerInput->exportedFunctionsSegmentId = 0;
      NEO::ExecutionEnvironment env;
      MockProgram program{env};
      KernelInfo kernelInfo = {};
      kernelInfo.name = "onlyKernel";
      std::vector<char> kernelHeap;
      kernelHeap.resize(32, 7);
      kernelInfo.heapInfo.pKernelHeap = kernelHeap.data();
      iOpenCL::SKernelBinaryHeaderCommon kernelHeader = {};
      kernelHeader.KernelHeapSize = static_cast<uint32_t>(kernelHeap.size());
      kernelInfo.heapInfo.pKernelHeader = &kernelHeader;
      MockGraphicsAllocation kernelIsa(kernelHeap.data(), kernelHeap.size());
      kernelInfo.kernelAllocation = &kernelIsa;
      program.getKernelInfoArray().push_back(&kernelInfo);
      program.linkerInput = std::move(linkerInput);
  
      program.exportedFunctionsSurface = kernelInfo.kernelAllocation;
      std::vector<char> globalVariablesBuffer;
      globalVariablesBuffer.resize(32, 7);
      std::vector<char> globalConstantsBuffer;
      globalConstantsBuffer.resize(32, 7);
      program.globalSurface = new MockGraphicsAllocation(globalVariablesBuffer.data(), globalVariablesBuffer.size());
      program.constantSurface = new MockGraphicsAllocation(globalConstantsBuffer.data(), globalConstantsBuffer.size());
  
      program.pDevice = &this->pContext->getDevice(0)->getDevice();
  
      auto ret = program.linkBinary();
      EXPECT_EQ(CL_SUCCESS, ret);
  
      linkerInput.reset(static_cast<WhiteBox<LinkerInput> *>(program.linkerInput.release()));
  
      for (size_t i = 0; i < linkerInput->relocations.size(); ++i) {
          auto expectedPatch = program.globalSurface->getGpuAddress() + linkerInput->symbols[linkerInput->relocations[0][0].symbolName].offset;
          auto relocationAddress = kernelHeap.data() + linkerInput->relocations[0][0].offset;
  
          EXPECT_EQ(static_cast<uintptr_t>(expectedPatch), *reinterpret_cast<uintptr_t *>(relocationAddress)) << i;
      }
  
      program.getKernelInfoArray().clear();
      delete program.globalSurface;
      program.globalSurface = nullptr;
      delete program.constantSurface;
      program.constantSurface = nullptr;
  }
  
  TEST_F(ProgramDataTest, whenRelocationsAreNotNeededThenIsaIsPreserved) {
      auto linkerInput = std::make_unique<WhiteBox<LinkerInput>>();
      linkerInput->symbols["A"] = NEO::SymbolInfo{4U, 4U, NEO::SegmentType::GlobalVariables};
      linkerInput->symbols["B"] = NEO::SymbolInfo{8U, 4U, NEO::SegmentType::GlobalConstants};
  
      NEO::ExecutionEnvironment env;
      MockProgram program{env};
      KernelInfo kernelInfo = {};
      kernelInfo.name = "onlyKernel";
      std::vector<char> kernelHeapData;
      kernelHeapData.resize(32, 7);
      std::vector<char> kernelHeap(kernelHeapData.begin(), kernelHeapData.end());
      kernelInfo.heapInfo.pKernelHeap = kernelHeap.data();
      iOpenCL::SKernelBinaryHeaderCommon kernelHeader = {};
      kernelHeader.KernelHeapSize = static_cast<uint32_t>(kernelHeap.size());
      kernelInfo.heapInfo.pKernelHeader = &kernelHeader;
      MockGraphicsAllocation kernelIsa(kernelHeap.data(), kernelHeap.size());
      kernelInfo.kernelAllocation = &kernelIsa;
      program.getKernelInfoArray().push_back(&kernelInfo);
      program.linkerInput = std::move(linkerInput);
  
      std::vector<char> globalVariablesBuffer;
      globalVariablesBuffer.resize(32, 7);
      std::vector<char> globalConstantsBuffer;
      globalConstantsBuffer.resize(32, 7);
      program.globalSurface = new MockGraphicsAllocation(globalVariablesBuffer.data(), globalVariablesBuffer.size());
      program.constantSurface = new MockGraphicsAllocation(globalConstantsBuffer.data(), globalConstantsBuffer.size());
  
      program.pDevice = &this->pContext->getDevice(0)->getDevice();
  
      auto ret = program.linkBinary();
      EXPECT_EQ(CL_SUCCESS, ret);
      EXPECT_EQ(kernelHeapData, kernelHeap);
  
      program.getKernelInfoArray().clear();
      delete program.globalSurface;
      program.globalSurface = nullptr;
      delete program.constantSurface;
      program.constantSurface = nullptr;
  }
  