diff --git a/llvm/include/llvm/Support/SYCLPropertySetIO.h b/llvm/include/llvm/Support/SYCLPropertySetIO.h new file mode 100644 index 0000000000000..85abde9a025ba --- /dev/null +++ b/llvm/include/llvm/Support/SYCLPropertySetIO.h @@ -0,0 +1,288 @@ +//=- SYCLPropertySetIO.h -- models a sequence of property sets and their I/O =// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Models a sequence of property sets and their input and output operations. +// SYCLPropertyValue set format: +// '['']' +// ='|' +// ='|' +// ... +// '['']' +// ='|' +// where +// , are strings +// - string representation of the property type +// - string representation of the property value. +// +// For example: +// [Staff/Ages] +// person1=1|20 +// person2=1|25 +// [Staff/Experience] +// person1=1|1 +// person2=1|2 +// person3=1|12 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_SYCLPROPERTYSETIO_H +#define LLVM_SUPPORT_SYCLPROPERTYSETIO_H + +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/xxhash.h" +#include + +namespace llvm { +namespace util { + +typedef struct Deleter { + void operator()(std::byte *ptr) const { delete[] ptr; } +} Deleter; + +// Represents a SYCL property value. SYCLPropertyValue name is stored in the +// encompassing container. +class SYCLPropertyValue { +public: + // Type of the size of the value. Value size gets serialized along with the + // value data in some cases for later reading at runtime, so size_t is not + // suitable as its size varies. + using SizeTy = uint64_t; + + // Defines supported property types + enum Type { first = 0, None = first, UInt32, ByteArray, last = ByteArray }; + + // Translates C++ type to the corresponding type tag. + template static Type getTypeTag(); + + // Casts from int value to a type tag. + static Expected getTypeTag(int T) { + if (T < first || T > last) + return createStringError(std::error_code(), "bad property type ", T); + return static_cast(T); + } + + ~SYCLPropertyValue() {} + SYCLPropertyValue() = default; + + SYCLPropertyValue(Type Ty) { + switch (Ty) { + case UInt32: + Val = (uint32_t)0; + break; + case ByteArray: + Val = std::unique_ptr(new std::byte[1], Deleter{}); + break; + default: + llvm_unreachable_internal("unsupported SYCL property type"); + } + } + SYCLPropertyValue(uint32_t Val) : Val({Val}) {} + SYCLPropertyValue(const std::byte *Data, SizeTy DataBitSize); + template + SYCLPropertyValue(const C &Data) + : SYCLPropertyValue(reinterpret_cast(Data.data()), + Data.size() * sizeof(T) * CHAR_BIT) {} + SYCLPropertyValue(const llvm::StringRef &Str) + : SYCLPropertyValue(reinterpret_cast(Str.data()), + Str.size() * sizeof(char) * CHAR_BIT) {} + SYCLPropertyValue(const SYCLPropertyValue &P); + SYCLPropertyValue(SYCLPropertyValue &&P); + + SYCLPropertyValue &operator=(SYCLPropertyValue &&P); + + SYCLPropertyValue &operator=(const SYCLPropertyValue &P); + + // Get property value as unsigned 32-bit integer + uint32_t asUint32() const { + assert(std::holds_alternative(Val) && "must be a uint32_t value"); + return std::get(Val); + } + + // Get raw data size in bits. + SizeTy getByteArraySizeInBits() const { + SizeTy Res = 0; + if (auto ByteArrayVal = + std::get_if>(&Val)) { + for (size_t I = 0; I < sizeof(SizeTy); ++I) + Res |= (SizeTy)(*ByteArrayVal).get()[I] << (8 * I); + } else + llvm_unreachable("must be a byte array value"); + return Res; + } + + // Get byte array data size in bytes. + SizeTy getByteArraySize() const { + SizeTy SizeInBits = getByteArraySizeInBits(); + constexpr unsigned int MASK = 0x7; + return ((SizeInBits + MASK) & ~MASK) / 8; + } + + // Get byte array data size in bytes, including the leading bytes encoding the + // size. + SizeTy getRawByteArraySize() const { + return getByteArraySize() + sizeof(SizeTy); + } + + // Get byte array data including the leading bytes encoding the size. + const std::byte *asRawByteArray() const { + if (auto ByteArrayVal = + std::get_if>(&Val)) + return (*ByteArrayVal).get(); + else + llvm_unreachable("must be a byte array value"); + } + + // Get byte array data excluding the leading bytes encoding the size. + const std::byte *asByteArray() const { + if (auto ByteArrayVal = + std::get_if>(&Val)) + return (*ByteArrayVal).get() + sizeof(SizeTy); + else + llvm_unreachable("must be a byte array value"); + } + + bool isValid() const { return getType() != None; } + + // Set property value when data type is uint32_t + void set(uint32_t V) { + assert(std::holds_alternative(Val) && "must be a uint32_t value"); + Val = V; + } + + // Set property value when data type is 'std::byte *' + void set(std::byte *V, int DataSize) { + size_t DataBitSize = DataSize * CHAR_BIT; + constexpr size_t SizeFieldSize = sizeof(SizeTy); + // Allocate space for size and data. + Val = std::unique_ptr( + new std::byte[SizeFieldSize + DataSize], Deleter{}); + + // Write the size into first bytes. + if (auto ByteArrayVal = + std::get_if>(&Val)) { + for (size_t I = 0; I < SizeFieldSize; ++I) { + (*ByteArrayVal).get()[I] = (std::byte)DataBitSize; + DataBitSize >>= CHAR_BIT; + } + // Append data. + std::memcpy((*ByteArrayVal).get() + SizeFieldSize, V, DataSize); + } else + llvm_unreachable("must be a byte array value"); + } + + Type getType() const { + if (std::holds_alternative(Val)) + return UInt32; + if (std::holds_alternative>(Val)) + return ByteArray; + return None; + } + + SizeTy size() const { + if (std::holds_alternative(Val)) + return sizeof(uint32_t); + if (std::holds_alternative>(Val)) + return getRawByteArraySize(); + llvm_unreachable_internal("unsupported SYCL property type"); + } + +private: + void copy(const SYCLPropertyValue &P); + + std::variant> Val; +}; + +/// Structure for specialization of DenseMap in SYCLPropertySetRegistry. +struct SYCLPropertySetKeyInfo { + static unsigned getHashValue(const SmallString<16> &K) { return xxHash64(K); } + + static SmallString<16> getEmptyKey() { return SmallString<16>(""); } + + static SmallString<16> getTombstoneKey() { return SmallString<16>("_"); } + + static bool isEqual(StringRef L, StringRef R) { return L == R; } +}; + +using SYCLPropertyMapTy = + DenseMap, unsigned, SYCLPropertySetKeyInfo>; +/// A property set. Preserves insertion order when iterating elements. +using SYCLPropertySet = + MapVector, SYCLPropertyValue, SYCLPropertyMapTy>; + +/// A registry of property sets. Maps a property set name to its +/// content. +/// +/// The order of keys is preserved and corresponds to the order of insertion. +class SYCLPropertySetRegistry { +public: + using MapTy = MapVector, SYCLPropertySet, SYCLPropertyMapTy>; + + // Specific property category names used by tools. + static constexpr char SYCLSpecializationConstants[] = + "SYCL/specialization constants"; + static constexpr char SYCLSpecConstantsDefaultValues[] = + "SYCL/specialization constants default values"; + static constexpr char SYCLDeviceLibReqMask[] = "SYCL/devicelib req mask"; + static constexpr char SYCLKernelParamOptInfo[] = "SYCL/kernel param opt"; + static constexpr char SYCLProgramMetadata[] = "SYCL/program metadata"; + static constexpr char SYCLMiscProp[] = "SYCL/misc properties"; + static constexpr char SYCLAssertUsed[] = "SYCL/assert used"; + static constexpr char SYCLExportedSymbols[] = "SYCL/exported symbols"; + static constexpr char SYCLImportedSymbols[] = "SYCL/imported symbols"; + static constexpr char SYCLDeviceGlobals[] = "SYCL/device globals"; + static constexpr char SYCLDeviceRequirements[] = "SYCL/device requirements"; + static constexpr char SYCLHostPipes[] = "SYCL/host pipes"; + static constexpr char SYCLVirtualFunctions[] = "SYCL/virtual functions"; + + /// Function for bulk addition of an entire property set in the given + /// \p Category . + template void add(StringRef Category, const MapTy &Props) { + assert(PropSetMap.find(Category) == PropSetMap.end() && + "category already added"); + auto &PropSet = PropSetMap[Category]; + + for (const auto &[PropName, PropVal] : Props) + PropSet.insert_or_assign(PropName, SYCLPropertyValue(PropVal)); + } + + /// Adds the given \p PropVal with the given \p PropName into the given \p + /// Category . + template + void add(StringRef Category, StringRef PropName, const T &PropVal) { + auto &PropSet = PropSetMap[Category]; + PropSet.insert({PropName, SYCLPropertyValue(PropVal)}); + } + + /// Parses from the given \p Buf a property set registry. + static Expected> + read(const MemoryBuffer *Buf); + + /// Dumps the property set registry to the given \p Out stream. + void write(raw_ostream &Out) const; + + MapTy::const_iterator begin() const { return PropSetMap.begin(); } + MapTy::const_iterator end() const { return PropSetMap.end(); } + + /// Retrieves a property set with given \p Name . + SYCLPropertySet &operator[](StringRef Name) { return PropSetMap[Name]; } + /// Constant access to the underlying map. + const MapTy &getPropSets() const { return PropSetMap; } + +private: + MapTy PropSetMap; +}; + +} // namespace util + +} // namespace llvm + +#endif // #define LLVM_SUPPORT_SYCLPROPERTYSETIO_H diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index 97188b0672f03..2e277ba0a9818 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -217,6 +217,7 @@ add_llvm_component_library(LLVMSupport Parallel.cpp PluginLoader.cpp PrettyStackTrace.cpp + SYCLPropertySetIO.cpp RandomNumberGenerator.cpp Regex.cpp RewriteBuffer.cpp diff --git a/llvm/lib/Support/SYCLPropertySetIO.cpp b/llvm/lib/Support/SYCLPropertySetIO.cpp new file mode 100644 index 0000000000000..996cbd73e41ad --- /dev/null +++ b/llvm/lib/Support/SYCLPropertySetIO.cpp @@ -0,0 +1,179 @@ +//= SYCLPropertySetIO.cpp - models a sequence of property sets and their I/O =// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/SYCLPropertySetIO.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Base64.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/LineIterator.h" + +#include +#include + +using namespace llvm::util; +using namespace llvm; + +static llvm::Error makeError(const Twine &Msg) { + return createStringError(std::error_code{}, Msg); +} + +Expected> +SYCLPropertySetRegistry::read(const MemoryBuffer *Buf) { + auto Res = std::make_unique(); + SYCLPropertySet *CurPropSet = nullptr; + + for (line_iterator LI(*Buf); !LI.is_at_end(); LI++) { + // See if this line starts a new property set + if (LI->starts_with("[")) { + // Parse the category (property name) + size_t EndPos = LI->rfind(']'); + if (EndPos == StringRef::npos) + return makeError("invalid line: " + *LI); + StringRef Category = LI->substr(1, EndPos - 1); + CurPropSet = &(*Res)[Category]; + continue; + } + if (!CurPropSet) + return makeError("property category missing"); + // Parse name and type+value + auto [PropName, PropTypeAndValue] = LI->split('='); + + if (PropName.empty() || PropTypeAndValue.empty()) + return makeError("invalid property line: " + *LI); + auto [PropType, PropVal] = PropTypeAndValue.split('|'); + + if (PropType.empty() || PropVal.empty()) + return makeError("invalid property value: " + PropTypeAndValue); + APInt Tint; + + // Parse type + if (PropType.getAsInteger(10, Tint)) + return makeError("invalid property type: " + PropType); + Expected Ttag = + SYCLPropertyValue::getTypeTag(static_cast(Tint.getSExtValue())); + StringRef Val = PropVal; + + if (!Ttag) + return Ttag.takeError(); + SYCLPropertyValue Prop(Ttag.get()); + + // Parse value depending on its type + if (Prop.getType() == SYCLPropertyValue::Type::UInt32) { + APInt ValV; + if (Val.getAsInteger(10, ValV)) + return createStringError(std::error_code{}, + "invalid property value: ", Val.data()); + Prop.set(static_cast(ValV.getZExtValue())); + } else if (Prop.getType() == SYCLPropertyValue::Type::ByteArray) { + std::vector Output; + // Output resized to maximum output size for base64 decoding + Output.resize(((Val.size() + 3) / 4) * 3); + if (Error Err = decodeBase64(Val, Output)) + return std::move(Err); + Prop.set(reinterpret_cast(Output.data()), Output.size()); + } else { + return createStringError(std::error_code{}, + "unsupported property type\n"); + } + (*CurPropSet)[PropName] = std::move(Prop); + } + if (!CurPropSet) + return makeError("invalid property set registry"); + + return Expected>(std::move(Res)); +} + +namespace llvm { +// Output a property to a stream +raw_ostream &operator<<(raw_ostream &Out, const SYCLPropertyValue &Prop) { + Out << static_cast(Prop.getType()) << '|'; + if (Prop.getType() == SYCLPropertyValue::Type::UInt32) { + Out << Prop.asUint32(); + return Out; + } + if (Prop.getType() == SYCLPropertyValue::Type::ByteArray) { + const std::byte *PropArr = Prop.asByteArray(); + std::vector V(PropArr, PropArr + Prop.getByteArraySize() / + sizeof(std::byte)); + Out << encodeBase64(V); + return Out; + } + llvm_unreachable("unsupported property type"); +} +} // namespace llvm + +void SYCLPropertySetRegistry::write(raw_ostream &Out) const { + for (const auto &[PropCategory, Props] : PropSetMap) { + Out << '[' << PropCategory << "]\n"; + + for (const auto &[PropName, PropVal] : Props) + Out << PropName << '=' << PropVal << '\n'; + } +} + +namespace llvm { +namespace util { + +SYCLPropertyValue::SYCLPropertyValue(const std::byte *Data, + SizeTy DataBitSize) { + SizeTy DataSize = (DataBitSize + (CHAR_BIT - 1)) / CHAR_BIT; + constexpr size_t SizeFieldSize = sizeof(SizeTy); + + // Allocate space for size and data. + Val = std::unique_ptr( + new std::byte[SizeFieldSize + DataSize], Deleter{}); + + // Write the size into first bytes. + if (auto ByteArrayVal = + std::get_if>(&Val)) { + for (size_t I = 0; I < SizeFieldSize; ++I) { + (*ByteArrayVal).get()[I] = (std::byte)DataBitSize; + DataBitSize >>= CHAR_BIT; + } + // Append data. + std::memcpy((*ByteArrayVal).get() + SizeFieldSize, Data, DataSize); + } else + llvm_unreachable("must be a byte array value"); +} + +SYCLPropertyValue::SYCLPropertyValue(const SYCLPropertyValue &P) { *this = P; } + +SYCLPropertyValue::SYCLPropertyValue(SYCLPropertyValue &&P) { + *this = std::move(P); +} + +SYCLPropertyValue &SYCLPropertyValue::operator=(SYCLPropertyValue &&P) { + copy(P); + + if (std::holds_alternative>(Val)) + P.Val = nullptr; + return *this; +} + +SYCLPropertyValue &SYCLPropertyValue::operator=(const SYCLPropertyValue &P) { + copy(P); + return *this; +} + +void SYCLPropertyValue::copy(const SYCLPropertyValue &P) { + if (std::holds_alternative>(P.Val)) { + // Allocate space for size and data. + Val = std::unique_ptr( + new std::byte[P.getRawByteArraySize()], Deleter{}); + if (auto ByteArrayVal = + std::get_if>(&Val)) + std::memcpy((*ByteArrayVal).get(), P.asRawByteArray(), + P.getRawByteArraySize()); + } else + Val = P.asUint32(); +} + +} // namespace util +} // namespace llvm diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index d64f89847aa8e..61789ef39a679 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -69,6 +69,7 @@ add_llvm_unittest(SupportTests PerThreadBumpPtrAllocatorTest.cpp ProcessTest.cpp ProgramTest.cpp + SYCLPropertySetIOTest.cpp RegexTest.cpp ReverseIterationTest.cpp ReplaceFileTest.cpp diff --git a/llvm/unittests/Support/SYCLPropertySetIOTest.cpp b/llvm/unittests/Support/SYCLPropertySetIOTest.cpp new file mode 100644 index 0000000000000..ce09bacaaa792 --- /dev/null +++ b/llvm/unittests/Support/SYCLPropertySetIOTest.cpp @@ -0,0 +1,309 @@ +// llvm/unittest/Support/SYCLPropertySetIOTest.cpp - SYCL Property set I/O tests +// // +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/SYCLPropertySetIO.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::util; + +namespace { + +TEST(SYCLPropertySet, IncorrectValuesReadIO) { + auto Content = "Staff/Ages]\n"; + auto MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + auto PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR( + std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr("property category missing"))); + + Content = "[Staff/Ages\n"; + MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR(std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr("invalid line"))); + + Content = "[Staff/Ages]\n" + "person1=\n"; + MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR( + std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr("invalid property line"))); + + Content = "[Staff/Ages]\n" + "person1=|10\n"; + MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR( + std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr("invalid property value"))); + + Content = "[Staff/Ages]\n" + "person1=abc|10\n"; + MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR( + std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr("invalid property type"))); + + Content = "[Staff/Ages]\n" + "person1=1|IAQ\n"; + MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR( + std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr("invalid property value"))); + + Content = "[Staff/Ages]\n" + "person1=2|IAQ\n"; + MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR( + std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr( + "Base64 encoded strings must be a multiple of 4 bytes in length"))); + + Content = "[Staff/Ages]\n" + "person1=100|10\n"; + MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR(std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr("bad property type"))); + + Content = "[Opt/Param]\n" + "kernel1=2|IAAAAAAAAAQA\tIAAAAAAAAAQ\n"; + MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR( + std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr("Invalid Base64 character"))); + + Content = "[Opt/Param]\n" + "kernel1=2|IAAAAAAAAAQA\nIAAAAAAAAAQ\n"; + MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + EXPECT_THAT_ERROR( + std::move(PropSetsPtr.takeError()), + FailedWithMessage(testing::HasSubstr("invalid property line"))); +} + +TEST(SYCLPropertySet, DuplicateKeysRead) { + // '1' in '1|20' means 'integer property' + auto Content = "[Staff/Ages]\n" + "person1=1|20\n" + "person1=1|25\n"; + // For duplicate keys, the latest key is selected + auto ExpectedContent = "[Staff/Ages]\n" + "person1=1|25\n"; + auto MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + auto PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + + if (!PropSetsPtr) + FAIL() << "SYCLPropertySetRegistry::read failed\n"; + + std::string Serialized; + { + llvm::raw_string_ostream OS(Serialized); + // Serialize + PropSetsPtr->get()->write(OS); + } + // Check that the original and the serialized version are equal + EXPECT_EQ(Serialized, ExpectedContent); +} + +// Test read-then-write of a list of integer values +TEST(SYCLPropertySet, IntValuesIO) { + // '1' in '1|20' means 'integer property' + auto Content = "[Staff/Ages]\n" + "person1=1|20\n" + "person2=1|25\n" + "[Staff/Experience]\n" + "person1=1|1\n" + "person2=1|2\n" + "person3=1|12\n"; + auto MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + auto PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + + if (!PropSetsPtr) + FAIL() << "SYCLPropertySetRegistry::read failed\n"; + + std::string Serialized; + { + llvm::raw_string_ostream OS(Serialized); + // Serialize + PropSetsPtr->get()->write(OS); + } + // Check that the original and the serialized version are equal + EXPECT_EQ(Serialized, Content); +} + +// Test read-then-write of a list of byte arrays +TEST(SYCLPropertySet, ByteArrayValuesIO) { + // '2' in '2|...' means 'byte array property', Base64-encoded + // encodes the following byte arrays: + // { 8, 0, 0, 0, 0, 0, 0, 0, 0x1 }; + // { 40, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x7F, 0xFF, 0x70 }; + // first 8 bytes are the size in bits (40) of what follows (5 bytes). + + auto Content = "[Opt/Param]\n" + "kernel1=2|IAAAAAAAAAQA\n" + "kernel2=2|oAAAAAAAAAAAw///3/wB\n"; + auto MemBuf = MemoryBuffer::getMemBuffer(Content); + // Parse a property set registry + auto PropSetsPtr = SYCLPropertySetRegistry::read(MemBuf.get()); + + if (!PropSetsPtr) + FAIL() << "SYCLPropertySetRegistry::read failed\n"; + + std::string Serialized; + { + llvm::raw_string_ostream OS(Serialized); + // Serialize + PropSetsPtr->get()->write(OS); + } + // Check that the original and the serialized version are equal + EXPECT_EQ(Serialized, Content); +} + +// Test write-then-read of boolean values +TEST(SYCLPropertySet, Mask) { + SYCLPropertySetRegistry PropSet; + uint32_t Mask = 0; + std::map DevMask = {{"Mask", Mask}}; + PropSet.add("MaskCategory", DevMask); + std::string Serialized; + std::string Expected("[MaskCategory]\nMask=1|0\n"); + { + llvm::raw_string_ostream OS(Serialized); + // Serialize + PropSet.write(OS); + } + EXPECT_EQ(Serialized, Expected); +} + +// Test write-then-read of std::map +// SYClPropertyValue is a class which contains one member, which is +// std::variant> Val; +TEST(SYCLPropertySet, SYCLPropertyValues) { + std::map PropValues; + std::vector Values = {1, 2, 3, 4}; + uint32_t Size = 4; + PropValues["Values"] = std::move(Values); + PropValues["Size"] = Size; + SYCLPropertySetRegistry PropSet; + PropSet.add("Property Values", PropValues); + std::string Serialized; + std::string Expected( + "[Property Values]\nSize=1|4\nValues=2|AQAAAAIAAAADAAAABAAAAA==\n"); + { + llvm::raw_string_ostream OS(Serialized); + // Serialize + PropSet.write(OS); + } + EXPECT_EQ(Serialized, Expected); +} + +// Test write-then-read of MapVector of StringRef to a simple struct datatype. +// Example of simple data structure: +// struct SimpleDS { +// unsigned ID; +// unsigned Offset; +// unsigned Size; +// }; +struct SimpleDS { + unsigned ID; + unsigned Offset; + unsigned Size; +}; +using MapTy = MapVector>; +TEST(SYCLPropertySet, MapToStruct) { + MapTy Values; + std::vector SimpleDSVal(2); + unsigned Start = 0; + for (unsigned I = Start; I < Start + 2; ++I) { + SimpleDSVal[I - Start].ID = I; + SimpleDSVal[I - Start].Offset = I * 4; + SimpleDSVal[I - Start].Size = 4; + } + Values["Value0"] = SimpleDSVal; + Start += 8; + for (unsigned I = Start; I < Start + 2; ++I) { + SimpleDSVal[I - Start].ID = I; + SimpleDSVal[I - Start].Offset = I * 4; + SimpleDSVal[I - Start].Size = 4; + } + Values["Value1"] = SimpleDSVal; + SYCLPropertySetRegistry PropSet; + PropSet.add("Values", Values); + std::string Serialized; + std::string Expected( + "[Values]\nValue0=2|AAAAAAAAAAAEAAAAAQAAAAQAAAAEAAAA\nValue1=2|" + "CAAAACAAAAAEAAAACQAAACQAAAAEAAAA\n"); + { + llvm::raw_string_ostream OS(Serialized); + // Serialize + PropSet.write(OS); + } + EXPECT_EQ(Serialized, Expected); +} + +// Test write-then-read of vector of chars +TEST(SYCLPropertySet, VectorOfChars) { + std::vector Values; + for (unsigned I = 0; I < 8; ++I) + Values.push_back((char)(I + '0')); + SYCLPropertySetRegistry PropSet; + PropSet.add("Values", "all", Values); + std::string Serialized; + std::string Expected("[Values]\nall=2|MDEyMzQ1Njc=\n"); + { + llvm::raw_string_ostream OS(Serialized); + // Serialize + PropSet.write(OS); + } + EXPECT_EQ(Serialized, Expected); +} + +// Test write-then-read of a list of Name-Value pairs +TEST(SYCLPropertySet, ListOfNameValuePairs) { + SYCLPropertySetRegistry PropSet; + std::vector Names = {"Name0", "Name1", "Name2", "Name3"}; + for (unsigned I = 0; I < 4; ++I) { + auto Value = I * 8; + PropSet.add("Values", Names[I], Value); + } + std::string Serialized; + std::string Expected( + "[Values]\nName0=1|0\nName1=1|8\nName2=1|16\nName3=1|24\n"); + { + llvm::raw_string_ostream OS(Serialized); + // Serialize + PropSet.write(OS); + } + EXPECT_EQ(Serialized, Expected); +} +} // namespace