// Copyright Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #pragma once #ifdef ROCROLLER_USE_LLVM #include #include #include #include #include #include namespace llvm { namespace yaml { /** * Map the `rocRoller::Serialization` traits classes into `llvm::yaml` traits classes. */ namespace sn = rocRoller::Serialization; /** * The LLVM documentation tells you to put a member: * * static const bool flow = true; * * in order to designate a type to be serialized with the flow style (i.e. to use brackets instead of whitespace) * * What it doesn't clarify is that the value of the bool is not important, so: * * static const bool flow = false; * * will *also* mark a type as using the flow style. The only way to not use flow style is to not have a * member named `flow`. This presents a challenge when trying to specify a traits class that may or may not * use flow style based on a template parameter. * * In the rocRoller::Serialization traits classes, having a `flow` member with a value of `false` will work to * specify the non-flow style. * * The following `FlowBase` can be used as a base class for `llvm::yaml` traits classes and contains a flow * member only for types that evaluate to `true`. This uses a series of concepts declared in HasTraits.hpp */ template struct FlowBase { }; template struct FlowBase { static const bool flow = true; }; template requires(sn::has_SequenceTraits::value) struct SequenceTraits : public FlowBase> { static size_t size(IO& io, T& seq) { return sn::SequenceTraits::size(io, seq); } static auto& element(IO& io, T& seq, size_t index) { return sn::SequenceTraits::element(io, seq, index); } }; template requires(sn::has_EnumTraits::value) struct ScalarEnumerationTraits { static void enumeration(IO& io, T& value) { sn::EnumTraits::enumeration(io, value); } }; template requires(sn::has_MappingTraits::value) struct MappingContextTraits { static void mapping(IO& io, T& obj, Context& ctx) { sn::MappingTraits::mapping(io, obj, ctx); } }; template concept HasContextMappingTraits = requires(IO& io, T& obj, Context& ctx) { {MappingContextTraits::mapping(io, obj, ctx)}; }; template requires(sn::has_EmptyMappingTraits::value) struct MappingTraits : public FlowBase> { static void mapping(IO& io, T& obj) { sn::MappingTraits::mapping(io, obj); } }; template concept HasEmptyMappingTraits = requires(IO& io, T& obj) { {MappingTraits::mapping(io, obj)}; }; template requires(sn::has_CustomMappingTraits::value) struct CustomMappingTraits { using Impl = sn::CustomMappingTraits; static void inputOne(IO& io, StringRef key, T& value) { Impl::inputOne(io, key.str(), value); } static void output(IO& io, T& value) { Impl::output(io, value); } }; template struct Hide { T& value; Hide(T& value) : value(value) { } T& operator*() { return value; } }; } // namespace yaml } // namespace llvm namespace rocRoller { namespace Serialization { template <> struct IOTraits { using IO = llvm::yaml::IO; template static void mapRequired(IO& io, const char* key, T& obj) { io.mapRequired(key, obj); } template static void mapRequired(IO& io, const char* key, T const& obj) { AssertFatal(outputting(io)); T tmp = obj; io.mapRequired(key, tmp); } template static void mapRequired(IO& io, const char* key, T& obj, Context& ctx) { io.mapRequired(key, obj, ctx); } template static void mapOptional(IO& io, const char* key, T& obj) { io.mapOptional(key, obj); } template static void mapOptional(IO& io, const char* key, T& obj, Context& ctx) { io.mapOptional(key, obj, ctx); } static bool outputting(IO& io) { return io.outputting(); } static void setError(IO& io, std::string const& msg) { throw std::runtime_error(msg); } static void setContext(IO& io, void* ctx) { io.setContext(ctx); } static void* getContext(IO& io) { return io.getContext(); } template static void enumCase(IO& io, T& member, const char* key, T value) { io.enumCase(member, key, value); } }; } // namespace Serialization } // namespace rocRoller namespace llvm { namespace yaml { LLVM_YAML_STRONG_TYPEDEF(size_t, FooType); using mysize_t = std::conditional::value, FooType, size_t>::type; template <> struct ScalarTraits { static void output(const mysize_t& value, void* ctx, raw_ostream& stream) { uint64_t tmp = value; ScalarTraits::output(tmp, ctx, stream); } static StringRef input(StringRef str, void* ctx, mysize_t& value) { uint64_t tmp; auto rv = ScalarTraits::input(str, ctx, tmp); value = tmp; return rv; } static bool mustQuote(StringRef) { return false; } }; template struct MappingTraits> { static void mapping(IO& io, Hide& value) { sn::MappingTraits::mapping(io, *value); } static const bool flow = sn::MappingTraits::flow; }; template struct SequenceTraits> { using Impl = sn::SequenceTraits; using Value = typename Impl::Value; static size_t size(IO& io, Hide& t) { return Impl::size(io, *t); } static Value& element(IO& io, Hide& t, size_t index) { return Impl::element(io, *t, index); } static const bool flow = Impl::flow; }; template struct ScalarEnumerationTraits> { static void enumeration(IO& io, Hide& value) { sn::EnumTraits::enumeration(io, *value); } }; template struct CustomMappingTraits> { using Impl = sn::CustomMappingTraits; static void inputOne(IO& io, StringRef key, Hide& value) { Impl::inputOne(io, key.str(), *value); } static void output(IO& io, Hide& value) { Impl::output(io, *value); } }; /** * Add serialization for small floating point types. Defer to built-in fp32 serialization with conversion. */ template requires(rocRoller::CIsAnyOf) struct ScalarTraits { static void output(const T& value, void* ctx, llvm::raw_ostream& out) { float floatVal = value; ScalarTraits::output(floatVal, ctx, out); } static StringRef input(StringRef scalar, void* ctx, T& value) { float floatVal = 0.0f; auto rv = ScalarTraits::input(scalar, ctx, floatVal); value = T(floatVal); return rv; } static QuotingType mustQuote(StringRef ref) { return ScalarTraits::mustQuote(ref); } }; template <> struct ScalarTraits { static void output(const rocRoller::Raw32& value, void* ctx, llvm::raw_ostream& out) { auto outputVal = static_cast(value); ScalarTraits::output(outputVal, ctx, out); } static StringRef input(StringRef scalar, void* ctx, rocRoller::Raw32& value) { uint32_t inputVal = 0u; auto rv = ScalarTraits::input(scalar, ctx, inputVal); value = rocRoller::Raw32(inputVal); return rv; } static QuotingType mustQuote(StringRef ref) { return ScalarTraits::mustQuote(ref); } }; template struct ScalarTraits { using rrTraits = rocRoller::Serialization::ScalarTraits; static void output(const Scalar& value, void* ctx, llvm::raw_ostream& out) { auto string = rrTraits::output(value); ScalarTraits::output(string, ctx, out); } static StringRef input(StringRef scalar, void* ctx, Scalar& value) { std::string string; auto rv = ScalarTraits::input(scalar, ctx, string); rrTraits::input(string, value); return rv; } static QuotingType mustQuote(StringRef ref) { return ScalarTraits::mustQuote(ref); } }; // Explicit vector traits to ensure they're available for LLVM template <> struct SequenceTraits> { static size_t size(IO& io, std::vector& seq) { return seq.size(); } static int& element(IO& io, std::vector& seq, size_t index) { if(index >= seq.size()) seq.resize(index + 1); return seq[index]; } static const bool flow = true; }; template <> struct SequenceTraits> { static size_t size(IO& io, std::vector& seq) { return seq.size(); } static unsigned int& element(IO& io, std::vector& seq, size_t index) { if(index >= seq.size()) seq.resize(index + 1); return seq[index]; } static const bool flow = true; }; } // namespace yaml } // namespace llvm #endif