diff --git a/sycl/source/detail/persistent_device_code_cache.cpp b/sycl/source/detail/persistent_device_code_cache.cpp index da72e49d0a213..53d6ba20246d6 100644 --- a/sycl/source/detail/persistent_device_code_cache.cpp +++ b/sycl/source/detail/persistent_device_code_cache.cpp @@ -47,11 +47,17 @@ LockCacheItem::~LockCacheItem() { FileName); } +// Returns true if the specified format is either SPIRV or a native binary. +static bool IsSupportedImageFormat(RT::PiDeviceBinaryType Format) { + return Format == PI_DEVICE_BINARY_TYPE_SPIRV || + Format == PI_DEVICE_BINARY_TYPE_NATIVE; +} + /* Returns true if specified image should be cached on disk. It checks if - * cache is enabled, image has SPIRV type and matches thresholds. */ + * cache is enabled, image has supported format and matches thresholds. */ bool PersistentDeviceCodeCache::isImageCached(const RTDeviceBinaryImage &Img) { - // Cache shoould be enabled and image type should be SPIR-V - if (!isEnabled() || Img.getFormat() != PI_DEVICE_BINARY_TYPE_SPIRV) + // Cache should be enabled and image type is one of the supported formats. + if (!isEnabled() || !IsSupportedImageFormat(Img.getFormat())) return false; // Disable cache for ITT-profiled images. diff --git a/sycl/source/detail/pi.cpp b/sycl/source/detail/pi.cpp index 34f830de3cacc..bd199c384e1ba 100644 --- a/sycl/source/detail/pi.cpp +++ b/sycl/source/detail/pi.cpp @@ -684,22 +684,54 @@ DeviceBinaryImage::getProperty(const char *PropName) const { return *It; } +// Returns the e_type field from an ELF image. +static uint16_t getELFHeaderType(const unsigned char *ImgData, size_t ImgSize) { + bool IsBigEndian = ImgData[5] == 2; + if (IsBigEndian) + return (static_cast(ImgData[16]) << 8) | + static_cast(ImgData[17]); + uint16_t HdrType = 0; + std::copy(ImgData + 16, ImgData + 16 + sizeof(HdrType), + reinterpret_cast(&HdrType)); + return HdrType; +} + RT::PiDeviceBinaryType getBinaryImageFormat(const unsigned char *ImgData, size_t ImgSize) { + // Top-level magic numbers for the recognized binary image formats. struct { RT::PiDeviceBinaryType Fmt; const uint32_t Magic; } Fmts[] = {{PI_DEVICE_BINARY_TYPE_SPIRV, 0x07230203}, - {PI_DEVICE_BINARY_TYPE_LLVMIR_BITCODE, 0xDEC04342}}; + {PI_DEVICE_BINARY_TYPE_LLVMIR_BITCODE, 0xDEC04342}, + // 'I', 'N', 'T', 'C' ; Intel native + {PI_DEVICE_BINARY_TYPE_NATIVE, 0x43544E49}}; if (ImgSize >= sizeof(Fmts[0].Magic)) { detail::remove_const_t Hdr = 0; std::copy(ImgData, ImgData + sizeof(Hdr), reinterpret_cast(&Hdr)); + // Check headers for direct formats. for (const auto &Fmt : Fmts) { if (Hdr == Fmt.Magic) return Fmt.Fmt; } + + // ELF e_type for recognized binary image formats. + struct { + RT::PiDeviceBinaryType Fmt; + const uint16_t Magic; + } ELFFmts[] = {{PI_DEVICE_BINARY_TYPE_NATIVE, 0xFF04}}; // OpenCL executable + + // ELF files need to be parsed separately. The header type ends after 18 + // bytes. + if (Hdr == 0x464c457F && ImgSize >= 18) { + uint16_t HdrType = getELFHeaderType(ImgData, ImgSize); + for (const auto &ELFFmt : ELFFmts) { + if (HdrType == ELFFmt.Magic) + return ELFFmt.Fmt; + } + } } return PI_DEVICE_BINARY_TYPE_NONE; } diff --git a/sycl/unittests/kernel-and-program/PersistentDeviceCodeCache.cpp b/sycl/unittests/kernel-and-program/PersistentDeviceCodeCache.cpp index 08ecec3b3344d..83298f7923758 100644 --- a/sycl/unittests/kernel-and-program/PersistentDeviceCodeCache.cpp +++ b/sycl/unittests/kernel-and-program/PersistentDeviceCodeCache.cpp @@ -1,4 +1,4 @@ -//==----- PersistenDeviceCodeCache.cpp --- Persistent cache tests ----------==// +//==----- PersistentDeviceCodeCache.cpp --- Persistent cache tests ---------==// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -78,7 +78,8 @@ static pi_result redefinedProgramGetInfo(pi_program program, return PI_SUCCESS; } -class PersistenDeviceCodeCache : public ::testing::Test { +class PersistentDeviceCodeCache + : public ::testing::TestWithParam { public: #ifdef _WIN32 int setenv(const char *name, const char *value, int overwrite) { @@ -123,6 +124,8 @@ class PersistenDeviceCodeCache : public ::testing::Test { EXPECT_NE(getenv("SYCL_CACHE_DIR"), nullptr) << "Please set SYCL_CACHE_DIR environment variable pointing to cache " "location."; + // Set binary format from parameter. + BinStruct.Format = GetParam(); } virtual void TearDown() { @@ -133,7 +136,7 @@ class PersistenDeviceCodeCache : public ::testing::Test { : nullptr); } - PersistenDeviceCodeCache() : Plt{default_selector()} { + PersistentDeviceCodeCache() : Plt{default_selector()} { if (Plt.is_host() || Plt.get_backend() != backend::opencl) { std::clog << "This test is only supported on OpenCL devices\n"; @@ -199,9 +202,10 @@ class PersistenDeviceCodeCache : public ::testing::Test { detail::OSModuleHandle ModuleHandle = detail::OSUtil::ExeModuleHandle; platform Plt; device Dev; + // NOTE: Format is a parameter of the test so use none and set in SetUp. pi_device_binary_struct BinStruct{/*Version*/ 1, /*Kind*/ 4, - /*Format*/ PI_DEVICE_BINARY_TYPE_SPIRV, + /*Format*/ PI_DEVICE_BINARY_TYPE_NONE, /*DeviceTargetSpec*/ nullptr, /*CompileOptions*/ nullptr, /*LinkOptions*/ nullptr, @@ -221,7 +225,7 @@ class PersistenDeviceCodeCache : public ::testing::Test { /* Checks that key values with \0 symbols are processed correctly */ -TEST_F(PersistenDeviceCodeCache, KeysWithNullTermSymbol) { +TEST_P(PersistentDeviceCodeCache, KeysWithNullTermSymbol) { if (Plt.is_host() || Plt.get_backend() != backend::opencl) { return; } @@ -254,21 +258,21 @@ TEST_F(PersistenDeviceCodeCache, KeysWithNullTermSymbol) { /* Do read/write for the same cache item to/from 300 threads for small device * code size. Make sure that there is no data corruption or crashes. */ -TEST_F(PersistenDeviceCodeCache, ConcurentReadWriteSmallItem) { +TEST_P(PersistentDeviceCodeCache, ConcurentReadWriteSmallItem) { ConcurentReadWriteCache(0, 300); } /* Do read/write for the same cache item to/from 100 threads for medium device * code size. Make sure that there is no data corruption or crashes. */ -TEST_F(PersistenDeviceCodeCache, ConcurentReadWriteCacheMediumItem) { +TEST_P(PersistentDeviceCodeCache, ConcurentReadWriteCacheMediumItem) { ConcurentReadWriteCache(1, 100); } /* Do read/write for the same cache item to/from 20 threads from big device * code size. Make sure that there is no data corruption or crashes. */ -TEST_F(PersistenDeviceCodeCache, ConcurentReadWriteCacheBigItem) { +TEST_P(PersistentDeviceCodeCache, ConcurentReadWriteCacheBigItem) { ConcurentReadWriteCache(2, 20); } @@ -279,7 +283,7 @@ TEST_F(PersistenDeviceCodeCache, ConcurentReadWriteCacheBigItem) { * - source file is corrupted; * - binary file is corrupted. */ -TEST_F(PersistenDeviceCodeCache, CorruptedCacheFiles) { +TEST_P(PersistentDeviceCodeCache, CorruptedCacheFiles) { if (Plt.is_host() || Plt.get_backend() != backend::opencl) { return; } @@ -349,7 +353,7 @@ TEST_F(PersistenDeviceCodeCache, CorruptedCacheFiles) { * - new cache item is created if existing one is locked on write operation; * - cache miss happens on read operation. */ -TEST_F(PersistenDeviceCodeCache, LockFile) { +TEST_P(PersistentDeviceCodeCache, LockFile) { if (Plt.is_host() || Plt.get_backend() != backend::opencl) { return; } @@ -405,7 +409,7 @@ TEST_F(PersistenDeviceCodeCache, LockFile) { // llvm::sys::fs::setPermissions does not make effect on Windows /* Checks cache behavior when filesystem read/write operations fail */ -TEST_F(PersistenDeviceCodeCache, AccessDeniedForCacheDir) { +TEST_P(PersistentDeviceCodeCache, AccessDeniedForCacheDir) { if (Plt.is_host() || Plt.get_backend() != backend::opencl) { return; } @@ -452,4 +456,9 @@ TEST_F(PersistenDeviceCodeCache, AccessDeniedForCacheDir) { ASSERT_NO_ERROR(llvm::sys::fs::remove_directories(ItemDir)); } #endif //_WIN32 + +INSTANTIATE_TEST_SUITE_P(PersistentDeviceCodeCacheImpl, + PersistentDeviceCodeCache, + ::testing::Values(PI_DEVICE_BINARY_TYPE_SPIRV, + PI_DEVICE_BINARY_TYPE_NATIVE)); } // namespace