Index: lib/Target/AMDGPU/Utils/CMakeLists.txt =================================================================== --- lib/Target/AMDGPU/Utils/CMakeLists.txt +++ lib/Target/AMDGPU/Utils/CMakeLists.txt @@ -2,4 +2,6 @@ AMDGPUBaseInfo.cpp AMDKernelCodeTUtils.cpp AMDGPUAsmUtils.cpp + MsgPackReader.cpp + MsgPackWriter.cpp ) Index: lib/Target/AMDGPU/Utils/MsgPack.h =================================================================== --- /dev/null +++ lib/Target/AMDGPU/Utils/MsgPack.h @@ -0,0 +1,95 @@ +//===-- MsgPack.h -- MessagePack Constants ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains constants used for implementing MessagePack support. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AMDGPU_UTILS_MSGPACK_H +#define LLVM_LIB_TARGET_AMDGPU_UTILS_MSGPACK_H + +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Endian.h" + +namespace llvm { +namespace AMDGPU { +namespace MsgPack { + +/// The endianness of all multi-byte encoded values in MessagePack. +constexpr support::endianness Endianness = support::big; + +/// The first byte identifiers of MessagePack object formats. +namespace FirstByte { +#define HANDLE_MP_FIRST_BYTE(ID, NAME) constexpr uint8_t NAME = ID; +#include "MsgPack.def" +} + +/// Most significant bits used to identify "Fix" variants in MessagePack. +/// +/// For example, FixStr objects encode their size in the five least significant +/// bits of their first byte, which is identified by the bit pattern "101" in +/// the three most significant bits. So FixBits::String contains 0b10100000. +/// +/// A corresponding mask of the bit pattern is found in \c FixBitsMask. +namespace FixBits { +#define HANDLE_MP_FIX_BITS(ID, NAME) constexpr uint8_t NAME = ID; +#include "MsgPack.def" +} + +/// Mask of bits used to identify "Fix" variants in MessagePack. +/// +/// For example, FixStr objects encode their size in the five least significant +/// bits of their first byte, which is identified by the bit pattern "101" in +/// the three most significant bytes. So FixBitsMask::String contains +/// 0b11100000. +/// +/// The corresponding bit pattern to mask for is found in FixBits. +namespace FixBitsMask { +#define HANDLE_MP_FIX_BITS_MASK(ID, NAME) constexpr uint8_t NAME = ID; +#include "MsgPack.def" +} + +/// The maximum value or size encodable in "Fix" variants of formats. +/// +/// For example, FixStr objects encode their size in the five least significant +/// bits of their first byte, so the largest encodable size is 0b00011111. +namespace FixMax { +#define HANDLE_MP_FIX_MAX(ID, NAME) constexpr uint8_t NAME = ID; +#include "MsgPack.def" +} + +/// The exact size encodable in "Fix" variants of formats. +/// +/// The only objects for which an exact size makes sense are of Extension type. +/// +/// For example, FixExt4 stores an extension type containing exactly four bytes. +namespace FixLen { +#define HANDLE_MP_FIX_LEN(ID, NAME) constexpr uint8_t NAME = ID; +#include "MsgPack.def" +} + +/// The minimum value or size encodable in "Fix" variants of formats. +/// +/// The only object for which a minimum makes sense is a negative FixNum. +/// +/// Negative FixNum objects encode their signed integer value in one byte, but +/// they must have the pattern "111" as their three most significant bits. This +/// means all values are negative, and the smallest representable value is +/// 0b11100000. +namespace FixMin { +#define HANDLE_MP_FIX_MIN(ID, NAME) constexpr int8_t NAME = ID; +#include "MsgPack.def" +} + +} // end namespace MsgPack +} // end namespace AMDGPU +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_AMDGPU_UTILS_MSGPACK_H Index: lib/Target/AMDGPU/Utils/MsgPack.def =================================================================== --- /dev/null +++ lib/Target/AMDGPU/Utils/MsgPack.def @@ -0,0 +1,108 @@ +//===- MsgPack.def - MessagePack definitions --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Macros for running through MessagePack enumerators. +/// +//===----------------------------------------------------------------------===// + +#if !( \ + defined HANDLE_MP_FIRST_BYTE || defined HANDLE_MP_FIX_BITS || \ + defined HANDLE_MP_FIX_BITS_MASK || defined HANDLE_MP_FIX_MAX || \ + defined HANDLE_MP_FIX_LEN || defined HANDLE_MP_FIX_MIN) +#error "Missing macro definition of HANDLE_MP*" +#endif + +#ifndef HANDLE_MP_FIRST_BYTE +#define HANDLE_MP_FIRST_BYTE(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_BITS +#define HANDLE_MP_FIX_BITS(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_BITS_MASK +#define HANDLE_MP_FIX_BITS_MASK(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_MAX +#define HANDLE_MP_FIX_MAX(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_LEN +#define HANDLE_MP_FIX_LEN(ID, NAME) +#endif + +#ifndef HANDLE_MP_FIX_MIN +#define HANDLE_MP_FIX_MIN(ID, NAME) +#endif + +HANDLE_MP_FIRST_BYTE(0xc0, Nil) +HANDLE_MP_FIRST_BYTE(0xc2, False) +HANDLE_MP_FIRST_BYTE(0xc3, True) +HANDLE_MP_FIRST_BYTE(0xc4, Bin8) +HANDLE_MP_FIRST_BYTE(0xc5, Bin16) +HANDLE_MP_FIRST_BYTE(0xc6, Bin32) +HANDLE_MP_FIRST_BYTE(0xc7, Ext8) +HANDLE_MP_FIRST_BYTE(0xc8, Ext16) +HANDLE_MP_FIRST_BYTE(0xc9, Ext32) +HANDLE_MP_FIRST_BYTE(0xca, Float32) +HANDLE_MP_FIRST_BYTE(0xcb, Float64) +HANDLE_MP_FIRST_BYTE(0xcc, UInt8) +HANDLE_MP_FIRST_BYTE(0xcd, UInt16) +HANDLE_MP_FIRST_BYTE(0xce, UInt32) +HANDLE_MP_FIRST_BYTE(0xcf, UInt64) +HANDLE_MP_FIRST_BYTE(0xd0, Int8) +HANDLE_MP_FIRST_BYTE(0xd1, Int16) +HANDLE_MP_FIRST_BYTE(0xd2, Int32) +HANDLE_MP_FIRST_BYTE(0xd3, Int64) +HANDLE_MP_FIRST_BYTE(0xd4, FixExt1) +HANDLE_MP_FIRST_BYTE(0xd5, FixExt2) +HANDLE_MP_FIRST_BYTE(0xd6, FixExt4) +HANDLE_MP_FIRST_BYTE(0xd7, FixExt8) +HANDLE_MP_FIRST_BYTE(0xd8, FixExt16) +HANDLE_MP_FIRST_BYTE(0xd9, Str8) +HANDLE_MP_FIRST_BYTE(0xda, Str16) +HANDLE_MP_FIRST_BYTE(0xdb, Str32) +HANDLE_MP_FIRST_BYTE(0xdc, Array16) +HANDLE_MP_FIRST_BYTE(0xdd, Array32) +HANDLE_MP_FIRST_BYTE(0xde, Map16) +HANDLE_MP_FIRST_BYTE(0xdf, Map32) + +HANDLE_MP_FIX_BITS(0x00, PositiveInt) +HANDLE_MP_FIX_BITS(0x80, Map) +HANDLE_MP_FIX_BITS(0x90, Array) +HANDLE_MP_FIX_BITS(0xa0, String) +HANDLE_MP_FIX_BITS(0xe0, NegativeInt) + +HANDLE_MP_FIX_BITS_MASK(0x80, PositiveInt) +HANDLE_MP_FIX_BITS_MASK(0xf0, Map) +HANDLE_MP_FIX_BITS_MASK(0xf0, Array) +HANDLE_MP_FIX_BITS_MASK(0xe0, String) +HANDLE_MP_FIX_BITS_MASK(0xe0, NegativeInt) + +HANDLE_MP_FIX_MAX(0x7f, PositiveInt) +HANDLE_MP_FIX_MAX(0x0f, Map) +HANDLE_MP_FIX_MAX(0x0f, Array) +HANDLE_MP_FIX_MAX(0x1f, String) + +HANDLE_MP_FIX_LEN(0x01, Ext1) +HANDLE_MP_FIX_LEN(0x02, Ext2) +HANDLE_MP_FIX_LEN(0x04, Ext4) +HANDLE_MP_FIX_LEN(0x08, Ext8) +HANDLE_MP_FIX_LEN(0x10, Ext16) + +HANDLE_MP_FIX_MIN(-0x20, NegativeInt) + +#undef HANDLE_MP_FIRST_BYTE +#undef HANDLE_MP_FIX_BITS +#undef HANDLE_MP_FIX_BITS_MASK +#undef HANDLE_MP_FIX_MAX +#undef HANDLE_MP_FIX_LEN +#undef HANDLE_MP_FIX_MIN Index: lib/Target/AMDGPU/Utils/MsgPackReader.h =================================================================== --- /dev/null +++ lib/Target/AMDGPU/Utils/MsgPackReader.h @@ -0,0 +1,149 @@ +//===- MsgPackReader.h - Simple MsgPack reader ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This is a MessagePack reader. +/// +/// See https://github.com/msgpack/msgpack/blob/master/spec.md for the full +/// standard. +/// +/// Typical usage: +/// \code +/// StringRef input = GetInput(); +/// msgpack::Reader MPReader(input); +/// msgpack::Object Obj; +/// +/// while (MPReader.Read(Obj)) { +/// switch (Obj.Kind) { +/// case msgpack::Type::Int: +// // Use Obj.Int +/// break; +/// // ... +/// } +/// } +/// \endcode +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AMDGPU_UTILS_MSGPACKREADER_H +#define LLVM_LIB_TARGET_AMDGPU_UTILS_MSGPACKREADER_H + +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { +namespace AMDGPU { +namespace MsgPack { + +/// MessagePack types as defined in the standard, with the exception of Int +/// being divided into a signed and unsigned variant in order to map directly +/// to C++ types. +/// +/// The types map onto corresponding union members of the \c Object struct. +enum class Type : uint8_t { + Int, + UInt, + Nil, + Boolean, + Float, + String, + Binary, + Array, + Map, + Extension, +}; + +/// MessagePack object, represented as a tagged union of C++ types. +/// +/// All types except \c Type::Nil (which has only one value, and so is +/// completely represented by the \c Kind itself) map to a exactly one union +/// member. +struct Object { + Type Kind; + union { + /// Value for \c Type::Int. + int64_t Int; + /// Value for \c Type::Uint. + uint64_t UInt; + /// Value for \c Type::Boolean. + bool Bool; + /// Value for \c Type::Float. + double Float; + /// Value for \c Type::String and \c Type::Binary. + StringRef Raw; + /// Value for \c Type::Array and \c Type::Map. + size_t Length; + /// Value for \c Type::Extension. + struct { + /// User-defined extension type. + int8_t Type; + /// Raw bytes of the extension object. + StringRef Bytes; + } Extension; + }; + + Object() : Kind(Type::Int), Int(0) {} +}; + +/// Reads MessagePack objects from memory, one at a time. +class Reader { +public: + /// Construct a reader, keeping a reference to the \p InputBuffer. + Reader(MemoryBufferRef InputBuffer); + /// Construct a reader, keeping a reference to the \p Input. + Reader(StringRef Input); + ~Reader(); + + Reader(const Reader &) = delete; + Reader &operator=(const Reader &) = delete; + + /// Read one object from the input buffer, advancing past it. + /// + /// The \p Obj is updated with the kind of the object read, and the + /// corresponding union member is updated. + /// + /// For the collection objects (Array and Map), only the length is read, and + /// the caller must make and additional \c N calls (in the case of Array) or + /// \c N*2 calls (in the case of Map) to \c Read to retrieve the collection + /// elements. + /// + /// \param [out] Obj filled with next object on success. + /// + /// \returns true when object successfully read, otherwise false. + bool Read(Object &Obj); + +private: + MemoryBufferRef InputBuffer; + StringRef::iterator Current; + StringRef::iterator End; + bool Failed = false; + + void SetError(const Twine &Message) { + if (Current >= End) + Current = End - 1; + if (!Failed) + errs() << Message << '\n'; + Failed = true; + } + + template bool ReadRaw(Object &Obj); + template bool ReadInt(Object &Obj); + template bool ReadUInt(Object &Obj); + template bool ReadLength(Object &Obj); + template bool ReadExt(Object &Obj); + bool CreateRaw(Object &Obj, uint32_t Size); + bool CreateExt(Object &Obj, uint32_t Size); +}; + +} // end namespace MsgPack +} // end namespace AMDGPU +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_AMDGPU_UTILS_MSGPACKREADER_H Index: lib/Target/AMDGPU/Utils/MsgPackReader.cpp =================================================================== --- /dev/null +++ lib/Target/AMDGPU/Utils/MsgPackReader.cpp @@ -0,0 +1,258 @@ +//===- MsgPackReader.cpp - Simple MsgPack reader ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a MessagePack reader. +/// +//===----------------------------------------------------------------------===// + +#include "MsgPackReader.h" +#include "MsgPack.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::support; +using namespace AMDGPU; +using namespace MsgPack; + +Reader::Reader(MemoryBufferRef InputBuffer) + : InputBuffer(InputBuffer), Current(InputBuffer.getBufferStart()), + End(InputBuffer.getBufferEnd()) {} + +Reader::Reader(StringRef Input) : Reader({Input, "MsgPack"}) {} + +Reader::~Reader() = default; + +bool Reader::Read(Object &Obj) { + if (Current == End) { + SetError("Attempted to read at end of stream"); + return false; + } + + uint8_t FB = static_cast(*Current++); + + switch (FB) { + case FirstByte::Nil: + Obj.Kind = Type::Nil; + return true; + case FirstByte::True: + Obj.Kind = Type::Boolean; + Obj.Bool = true; + return true; + case FirstByte::False: + Obj.Kind = Type::Boolean; + Obj.Bool = false; + return true; + case FirstByte::Int8: + Obj.Kind = Type::Int; + return ReadInt(Obj); + case FirstByte::Int16: + Obj.Kind = Type::Int; + return ReadInt(Obj); + case FirstByte::Int32: + Obj.Kind = Type::Int; + return ReadInt(Obj); + case FirstByte::Int64: + Obj.Kind = Type::Int; + return ReadInt(Obj); + case FirstByte::UInt8: + Obj.Kind = Type::UInt; + return ReadUInt(Obj); + case FirstByte::UInt16: + Obj.Kind = Type::UInt; + return ReadUInt(Obj); + case FirstByte::UInt32: + Obj.Kind = Type::UInt; + return ReadUInt(Obj); + case FirstByte::UInt64: + Obj.Kind = Type::UInt; + return ReadUInt(Obj); + case FirstByte::Float32: + Obj.Kind = Type::Float; + if (Current + sizeof(float) > End) { + SetError("Invalid Float32 with insufficient payload"); + return false; + } + Obj.Float = BitsToFloat(endian::read(Current)); + Current += sizeof(float); + return true; + case FirstByte::Float64: + Obj.Kind = Type::Float; + if (Current + sizeof(double) > End) { + SetError("Invalid Float64 with insufficient payload"); + return false; + } + Obj.Float = BitsToDouble(endian::read(Current)); + Current += sizeof(double); + return true; + case FirstByte::Str8: + Obj.Kind = Type::String; + return ReadRaw(Obj); + case FirstByte::Str16: + Obj.Kind = Type::String; + return ReadRaw(Obj); + case FirstByte::Str32: + Obj.Kind = Type::String; + return ReadRaw(Obj); + case FirstByte::Bin8: + Obj.Kind = Type::Binary; + return ReadRaw(Obj); + case FirstByte::Bin16: + Obj.Kind = Type::Binary; + return ReadRaw(Obj); + case FirstByte::Bin32: + Obj.Kind = Type::Binary; + return ReadRaw(Obj); + case FirstByte::Array16: + Obj.Kind = Type::Array; + return ReadLength(Obj); + case FirstByte::Array32: + Obj.Kind = Type::Array; + return ReadLength(Obj); + case FirstByte::Map16: + Obj.Kind = Type::Map; + return ReadLength(Obj); + case FirstByte::Map32: + Obj.Kind = Type::Map; + return ReadLength(Obj); + case FirstByte::FixExt1: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext1); + case FirstByte::FixExt2: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext2); + case FirstByte::FixExt4: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext4); + case FirstByte::FixExt8: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext8); + case FirstByte::FixExt16: + Obj.Kind = Type::Extension; + return CreateExt(Obj, FixLen::Ext16); + case FirstByte::Ext8: + Obj.Kind = Type::Extension; + return ReadExt(Obj); + case FirstByte::Ext16: + Obj.Kind = Type::Extension; + return ReadExt(Obj); + case FirstByte::Ext32: + Obj.Kind = Type::Extension; + return ReadExt(Obj); + } + + if ((FB & FixBitsMask::NegativeInt) == FixBits::NegativeInt) { + Obj.Kind = Type::Int; + // TODO: Better way to reinterpret bits in FB? + Obj.Int = *reinterpret_cast(&FB); + return true; + } + + if ((FB & FixBitsMask::PositiveInt) == FixBits::PositiveInt) { + Obj.Kind = Type::UInt; + Obj.UInt = static_cast(FB); + return true; + } + + if ((FB & FixBitsMask::String) == FixBits::String) { + Obj.Kind = Type::String; + uint8_t Size = FB & ~FixBitsMask::String; + return CreateRaw(Obj, Size); + } + + if ((FB & FixBitsMask::Array) == FixBits::Array) { + Obj.Kind = Type::Array; + Obj.Length = FB & ~FixBitsMask::Array; + return true; + } + + if ((FB & FixBitsMask::Map) == FixBits::Map) { + Obj.Kind = Type::Map; + Obj.Length = FB & ~FixBitsMask::Map; + return true; + } + + SetError("Invalid first byte"); + return false; +} + +template bool Reader::ReadRaw(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Raw with invalid size"); + return false; + } + T Size = endian::read(Current); + Current += sizeof(T); + return CreateRaw(Obj, Size); +} + +template bool Reader::ReadInt(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Int with insufficient payload"); + return false; + } + Obj.Int = static_cast(endian::read(Current)); + Current += sizeof(T); + return true; +} + +template bool Reader::ReadUInt(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Int with insufficient payload"); + return false; + } + Obj.UInt = static_cast(endian::read(Current)); + Current += sizeof(T); + return true; +} + +template bool Reader::ReadLength(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Map/Array with invalid length"); + return false; + } + Obj.Length = static_cast(endian::read(Current)); + Current += sizeof(T); + return true; +} + +template bool Reader::ReadExt(Object &Obj) { + if (Current + sizeof(T) > End) { + SetError("Invalid Ext with invalid length"); + return false; + } + T Size = endian::read(Current); + Current += sizeof(T); + return CreateExt(Obj, Size); +} + +bool Reader::CreateRaw(Object &Obj, uint32_t Size) { + Obj.Raw = StringRef(Current, Size); + Current += Size; + if (Current > End) { + SetError("Invalid Raw with insufficient payload"); + return false; + } + return true; +} + +bool Reader::CreateExt(Object &Obj, uint32_t Size) { + if (Current == End) { + SetError("Invalid Ext with no type"); + return false; + } + Obj.Extension.Type = *Current++; + Obj.Extension.Bytes = StringRef(Current, Size); + Current += Size; + if (Current > End) { + SetError("Invalid Ext with insufficient payload"); + return false; + } + return true; +} Index: lib/Target/AMDGPU/Utils/MsgPackWriter.h =================================================================== --- /dev/null +++ lib/Target/AMDGPU/Utils/MsgPackWriter.h @@ -0,0 +1,134 @@ +//===- MsgPackWriter.h - Simple MsgPack writer ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains a MessagePack writer. +/// +/// See https://github.com/msgpack/msgpack/blob/master/spec.md for the full +/// specification. +/// +/// Typical usage: +/// \code +/// raw_ostream output = GetOutputStream(); +/// msgpack::Writer MPWriter(output); +/// MPWriter.WriteNil(); +/// MPWriter.Write(false); +/// MPWriter.Write("string"); +/// // ... +/// \endcode +/// +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AMDGPU_UTILS_MSGPACKWRITER_H +#define LLVM_LIB_TARGET_AMDGPU_UTILS_MSGPACKWRITER_H + +#include "MsgPack.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace AMDGPU { +namespace MsgPack { + +/// Writes MessagePack objects to an output stream, one at a time. +class Writer { +public: + /// Construct a writer, optionally enabling "Compatibility Mode" as defined + /// in the MessagePack specification. + /// + /// When in \p Compatible mode, the writer will write \c Str16 formats + /// instead of \c Str8 formats, and will refuse to write any \c Bin formats. + /// + /// \param OS stream to output MessagePack objects to. + /// \param Compatible when set, write in "Compatibility Mode". + Writer(raw_ostream &OS, bool Compatible = false); + ~Writer(); + + Writer(const Writer &) = delete; + Writer &operator=(const Writer &) = delete; + + /// Write a \em Nil to the output stream. + /// + /// The output will be the \em nil format. + void WriteNil(); + + /// Write a \em Boolean to the output stream. + /// + /// The output will be a \em bool format. + void Write(bool b); + + /// Write a signed integer to the output stream. + /// + /// The output will be in the smallest possible \em int format. + /// + /// The format chosen may be for an unsigned integer. + void Write(int64_t i); + + /// Write an unsigned integer to the output stream. + /// + /// The output will be in the smallest possible \em int format. + void Write(uint64_t u); + + /// Write a floating point number to the output stream. + /// + /// The output will be in the smallest possible \em float format. + void Write(double d); + + /// Write a string to the output stream. + /// + /// The output will be in the smallest possible \em str format. + void Write(StringRef s); + + /// Write a memory buffer to the output stream. + /// + /// The output will be in the smallest possible \em bin format. + /// + /// \warning Do not use this overload if in \c Compatible mode. + void Write(MemoryBufferRef Buffer); + + /// Write the header for an \em Array of the given size. + /// + /// The output will be in the smallest possible \em array format. + // + /// The header contains an identifier for the \em array format used, as well + /// as an encoding of the size of the array. + /// + /// N.B. The caller must subsequently call \c Write an additional \p Size + /// times to complete the array. + void WriteArraySize(uint32_t Size); + + /// Write the header for a \em Map of the given size. + /// + /// The output will be in the smallest possible \em map format. + // + /// The header contains an identifier for the \em map format used, as well + /// as an encoding of the size of the map. + /// + /// N.B. The caller must subsequently call \c Write and additional \c Size*2 + /// times to complete the map. Each even numbered call to \c Write defines a + /// new key, and each odd numbered call defines the previous key's value. + void WriteMapSize(uint32_t Size); + + /// Write a typed memory buffer (an extension type) to the output stream. + /// + /// The output will be in the smallest possible \em ext format. + void WriteExt(int8_t Type, MemoryBufferRef Buffer); + +private: + support::endian::Writer EW; + bool Compatible; +}; + +} // end namespace MsgPack +} // end namespace AMDGPU +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_AMDGPU_UTILS_MSGPACKWRITER_H Index: lib/Target/AMDGPU/Utils/MsgPackWriter.cpp =================================================================== --- /dev/null +++ lib/Target/AMDGPU/Utils/MsgPackWriter.cpp @@ -0,0 +1,210 @@ +//===- MsgPackWriter.cpp - Simple MsgPack writer ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a MessagePack writer. +/// +//===----------------------------------------------------------------------===// + +#include "MsgPackWriter.h" + +using namespace llvm; +using namespace AMDGPU; +using namespace MsgPack; + +Writer::Writer(raw_ostream &OS, bool Compatible) + : EW(OS), Compatible(Compatible) {} + +Writer::~Writer() = default; + +void Writer::WriteNil() { EW.write(FirstByte::Nil); } + +void Writer::Write(bool b) { EW.write(b ? FirstByte::True : FirstByte::False); } + +void Writer::Write(int64_t i) { + if (i >= 0) { + Write(static_cast(i)); + return; + } + + if (i >= FixMin::NegativeInt) { + EW.write(static_cast(i)); + return; + } + + if (i >= INT8_MIN) { + EW.write(FirstByte::Int8); + EW.write(static_cast(i)); + return; + } + + if (i >= INT16_MIN) { + EW.write(FirstByte::Int16); + EW.write(static_cast(i)); + return; + } + + if (i >= INT32_MIN) { + EW.write(FirstByte::Int32); + EW.write(static_cast(i)); + return; + } + + EW.write(FirstByte::Int64); + EW.write(i); +} + +void Writer::Write(uint64_t u) { + if (u <= FixMax::PositiveInt) { + EW.write(static_cast(u)); + return; + } + + if (u <= UINT8_MAX) { + EW.write(FirstByte::UInt8); + EW.write(static_cast(u)); + return; + } + + if (u <= UINT16_MAX) { + EW.write(FirstByte::UInt16); + EW.write(static_cast(u)); + return; + } + + if (u <= UINT32_MAX) { + EW.write(FirstByte::UInt32); + EW.write(static_cast(u)); + return; + } + + EW.write(FirstByte::UInt64); + EW.write(u); +} + +void Writer::Write(double d) { + // If no loss of precision, encode as a Float32. + float f = static_cast(d); + if (static_cast(f) == d) { + EW.write(FirstByte::Float32); + EW.write(f); + } else { + EW.write(FirstByte::Float64); + EW.write(d); + } +} + +void Writer::Write(StringRef s) { + size_t Size = s.size(); + + if (Size <= FixMax::String) + EW.write(static_cast(FixBits::String | Size)); + else if (!Compatible && Size <= UINT8_MAX) { + EW.write(FirstByte::Str8); + EW.write(static_cast(Size)); + } else if (Size <= UINT16_MAX) { + EW.write(FirstByte::Str16); + EW.write(static_cast(Size)); + } else { + assert(Size <= UINT32_MAX && "String object too long to be encoded"); + EW.write(FirstByte::Str32); + EW.write(static_cast(Size)); + } + + EW.OS << s; +} + +void Writer::Write(MemoryBufferRef Buffer) { + assert(!Compatible && "Attempt to write Bin format in compatible mode"); + + size_t Size = Buffer.getBufferSize(); + + if (Size <= UINT8_MAX) { + EW.write(FirstByte::Bin8); + EW.write(static_cast(Size)); + } else if (Size <= UINT16_MAX) { + EW.write(FirstByte::Bin16); + EW.write(static_cast(Size)); + } else { + assert(Size <= UINT32_MAX && "Binary object too long to be encoded"); + EW.write(FirstByte::Bin32); + EW.write(static_cast(Size)); + } + + EW.OS.write(Buffer.getBufferStart(), Size); +} + +void Writer::WriteArraySize(uint32_t Size) { + if (Size <= FixMax::Array) { + EW.write(static_cast(FixBits::Array | Size)); + return; + } + + if (Size <= UINT16_MAX) { + EW.write(FirstByte::Array16); + EW.write(static_cast(Size)); + return; + } + + EW.write(FirstByte::Array32); + EW.write(Size); +} + +void Writer::WriteMapSize(uint32_t Size) { + if (Size <= FixMax::Map) { + EW.write(static_cast(FixBits::Map | Size)); + return; + } + + if (Size <= UINT16_MAX) { + EW.write(FirstByte::Map16); + EW.write(static_cast(Size)); + return; + } + + EW.write(FirstByte::Map32); + EW.write(Size); +} + +void Writer::WriteExt(int8_t Type, MemoryBufferRef Buffer) { + size_t Size = Buffer.getBufferSize(); + + switch (Size) { + case FixLen::Ext1: + EW.write(FirstByte::FixExt1); + break; + case FixLen::Ext2: + EW.write(FirstByte::FixExt2); + break; + case FixLen::Ext4: + EW.write(FirstByte::FixExt4); + break; + case FixLen::Ext8: + EW.write(FirstByte::FixExt8); + break; + case FixLen::Ext16: + EW.write(FirstByte::FixExt16); + break; + default: + if (Size <= UINT8_MAX) { + EW.write(FirstByte::Ext8); + EW.write(static_cast(Size)); + } else if (Size <= UINT16_MAX) { + EW.write(FirstByte::Ext16); + EW.write(static_cast(Size)); + } else { + assert(Size <= UINT32_MAX && "Ext size too large to be encoded"); + EW.write(FirstByte::Ext32); + EW.write(static_cast(Size)); + } + } + + EW.write(Type); + EW.OS.write(Buffer.getBufferStart(), Size); +}