diff --git a/llvm/include/llvm/Support/PropertySetIO.h b/llvm/include/llvm/Support/PropertySetIO.h new file mode 100644 index 0000000000000..a8b329f02a62d --- /dev/null +++ b/llvm/include/llvm/Support/PropertySetIO.h @@ -0,0 +1,163 @@ +//==-- PropertySetIO.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. +// TODO use Yaml as I/O engine. +// PropertyValue 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_PROPERTYSETIO_H +#define LLVM_SUPPORT_PROPERTYSETIO_H + +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include + +namespace llvm { +namespace util { + +// Represents a property value. PropertyValue name is stored in the encompassing +// container. +class PropertyValue { +public: + // Defines supported property types + enum Type { first = 0, NONE = first, UINT32, last = UINT32 }; + + // 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); + } + + PropertyValue() = default; + PropertyValue(Type T) : Ty(T) {} + + PropertyValue(uint32_t Val) : Ty(UINT32), Val({Val}) {} + PropertyValue(const PropertyValue &P) = default; + PropertyValue(PropertyValue &&P) = default; + + PropertyValue &operator=(PropertyValue &&P) = default; + + PropertyValue &operator=(const PropertyValue &P) = default; + + // get property value as unsigned 32-bit integer + uint32_t asUint32() const { + assert(Ty == UINT32); + return Val.UInt32Val; + } + + bool isValid() const { return getType() != NONE; } + + // set property value; the 'T' type must be convertible to a property type tag + template void set(T V) { + assert(getTypeTag() == Ty); + getValueRef() = V; + } + + Type getType() const { return Ty; } + + size_t size() const { + switch (Ty) { + case UINT32: + return sizeof(Val.UInt32Val); + default: + llvm_unreachable_internal("unsupported property type"); + } + } + +private: + template T &getValueRef(); + + Type Ty = NONE; + union { + uint32_t UInt32Val; + } Val; +}; + +std::ostream &operator<<(std::ostream &Out, const PropertyValue &V); + +// A property set. Preserves insertion order when iterating elements. +using PropertySet = MapVector; + +// A "registry" of multiple property sets. Maps a property set name to its +// contents. Can be read/written. +class PropertySetRegistry { +public: + using MapTy = MapVector; + + // Specific property category names used by tools. + static constexpr char SYCL_SPECIALIZATION_CONSTANTS[] = + "SYCL/specialization constants"; + + // Function for bulk addition of an entire property set under given category + // (property set name). + template + void add(StringRef Category, const std::map &Props) { + assert(PropSetMap.find(Category) == PropSetMap.end() && + "category already added"); + auto &PropSet = PropSetMap[Category]; + + for (const auto &Prop : Props) + PropSet.insert(std::make_pair(Prop.first, PropertyValue(Prop.second))); + } + + // Parses and creates a property set registry. + static Expected> + read(const MemoryBuffer *Buf); + + // Dumps a property set registry to a stream. + void write(raw_ostream &Out) const; + + // Start iterator of all preperty sets in the registry. + MapTy::const_iterator begin() const { return PropSetMap.begin(); } + // End iterator of all preperty sets in the registry. + MapTy::const_iterator end() const { return PropSetMap.end(); } + + // Retrieves a property set with given name. + PropertySet &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_PROPERTYSETIO_H \ No newline at end of file diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index 75a62f45da366..0ac8fe6875e64 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -119,6 +119,7 @@ add_llvm_component_library(LLVMSupport Parallel.cpp PluginLoader.cpp PrettyStackTrace.cpp + PropertySetIO.cpp RandomNumberGenerator.cpp Regex.cpp ScaledNumber.cpp diff --git a/llvm/lib/Support/PropertySetIO.cpp b/llvm/lib/Support/PropertySetIO.cpp new file mode 100644 index 0000000000000..a70983faeda59 --- /dev/null +++ b/llvm/lib/Support/PropertySetIO.cpp @@ -0,0 +1,116 @@ +//==- PropertySetIO.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/PropertySetIO.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/LineIterator.h" + +using namespace llvm::util; +using namespace llvm; + +Expected> +PropertySetRegistry::read(const MemoryBuffer *Buf) { + auto Res = std::make_unique(); + PropertySet *CurPropSet = nullptr; + std::error_code EC; + + for (line_iterator LI(*Buf); !LI.is_at_end(); LI++) { + // see if this line starts a new property set + if (LI->startswith("[")) { + // yes - parse the category (property name) + auto EndPos = LI->rfind(']'); + if (EndPos == StringRef::npos) + return createStringError(EC, "invalid line: " + *LI); + StringRef Category = LI->substr(1, EndPos - 1); + CurPropSet = &(*Res)[Category]; + continue; + } + if (!CurPropSet) + return createStringError(EC, "property category missing"); + // parse name and type+value + auto Parts = LI->split('='); + + if (Parts.first.empty() || Parts.second.empty()) + return createStringError(EC, "invalid property line: " + *LI); + auto TypeVal = Parts.second.split('|'); + + if (TypeVal.first.empty() || TypeVal.second.empty()) + return createStringError(EC, "invalid property value: " + Parts.second); + APInt Tint; + + // parse type + if (TypeVal.first.getAsInteger(10, Tint)) + return createStringError(EC, "invalid property type: " + TypeVal.first); + Expected Ttag = + PropertyValue::getTypeTag(static_cast(Tint.getSExtValue())); + StringRef Val = TypeVal.second; + + if (!Ttag) + return Ttag.takeError(); + PropertyValue Prop(Ttag.get()); + + // parse value depending on its type + switch (Ttag.get()) { + case PropertyValue::Type::UINT32: { + APInt ValV; + if (Val.getAsInteger(10, ValV)) + return createStringError(EC, "invalid property value: " + Val); + Prop.set(static_cast(ValV.getZExtValue())); + break; + } + default: + return createStringError(EC, "unsupported property type: " + Ttag.get()); + } + (*CurPropSet)[Parts.first] = Prop; + } + if (!CurPropSet) + return createStringError(EC, "invalid property set registry"); + + return Expected>(std::move(Res)); +} + +namespace llvm { +// output a property to a stream +raw_ostream &operator<<(raw_ostream &Out, const PropertyValue &Prop) { + Out << static_cast(Prop.getType()) << "|"; + switch (Prop.getType()) { + case PropertyValue::Type::UINT32: + Out << Prop.asUint32(); + break; + default: + llvm_unreachable_internal("unsupported property type: " + Prop.getType()); + } + return Out; +} +} // namespace llvm + +void PropertySetRegistry::write(raw_ostream &Out) const { + for (const auto &PropSet : PropSetMap) { + Out << "[" << PropSet.first << "]\n"; + + for (const auto &Props : PropSet.second) { + Out << std::string(Props.first) << "=" << Props.second << "\n"; + } + } +} + +namespace llvm { +namespace util { + +template <> uint32_t &PropertyValue::getValueRef() { + return Val.UInt32Val; +} +template <> PropertyValue::Type PropertyValue::getTypeTag() { + return UINT32; +} + +constexpr char PropertySetRegistry::SYCL_SPECIALIZATION_CONSTANTS[]; +} // namespace util +} // namespace llvm diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index ebb7aaa3ca753..201bcaa834192 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -55,6 +55,7 @@ add_llvm_unittest(SupportTests Path.cpp ProcessTest.cpp ProgramTest.cpp + PropertySetIOTest.cpp RegexTest.cpp ReverseIterationTest.cpp ReplaceFileTest.cpp diff --git a/llvm/unittests/Support/PropertySetIOTest.cpp b/llvm/unittests/Support/PropertySetIOTest.cpp new file mode 100644 index 0000000000000..329c7692fe3f1 --- /dev/null +++ b/llvm/unittests/Support/PropertySetIOTest.cpp @@ -0,0 +1,45 @@ +//===- llvm/unittest/Support/PropertySetIO.cpp - 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/PropertySetIO.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::util; + +namespace { + +TEST(PropertySet, 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 = PropertySetRegistry::read(MemBuf.get()); + + if (!PropSetsPtr) + FAIL() << "PropertySetRegistry::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 + ASSERT_EQ(Serialized, Content); +} +} // namespace \ No newline at end of file