diff --git a/llvm/include/llvm/Bitcode/BitcodeConvenience.h b/llvm/include/llvm/Bitcode/BitcodeConvenience.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Bitcode/BitcodeConvenience.h @@ -0,0 +1,519 @@ +//===- llvm/Bitcode/BitcodeConvenience.h - Convenience Wrappers -*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file Convenience wrappers for the LLVM bitcode format and bitstream APIs. +/// +/// This allows you to use a sort of DSL to declare and use bitcode +/// abbreviations and records. Example: +/// +/// \code +/// using Metadata = BCRecordLayout< +/// METADATA_ID, // ID +/// BCFixed<16>, // Module format major version +/// BCFixed<16>, // Module format minor version +/// BCBlob // misc. version information +/// >; +/// Metadata metadata(Out); +/// metadata.emit(ScratchRecord, VERSION_MAJOR, VERSION_MINOR, Data); +/// \endcode +/// +/// For details on the bitcode format, see +/// http://llvm.org/docs/BitCodeFormat.html +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BITCODE_BITCODECONVENIENCE_H +#define LLVM_BITCODE_BITCODECONVENIENCE_H + +#include "llvm/Bitstream/BitCodes.h" +#include "llvm/Bitstream/BitstreamWriter.h" +#include + +namespace llvm { +namespace detail { +/// Convenience base for all kinds of bitcode abbreviation fields. +/// +/// This just defines common properties queried by the metaprogramming. +template +class BCField { +public: + static const bool IsCompound = Compound; + + /// Asserts that the given data is a valid value for this field. + template + static void assertValid(const T &data) {} + + /// Converts a raw numeric representation of this value to its preferred + /// type. + template + static T convert(T rawValue) { + return rawValue; + } +}; +} + +/// Represents a literal operand in a bitcode record. +/// +/// The value of a literal operand is the same for all instances of the record, +/// so it is only emitted in the abbreviation definition. +/// +/// Note that because this uses a compile-time template, you cannot have a +/// literal operand that is fixed at run-time without dropping down to the +/// raw LLVM APIs. +template +class BCLiteral : public detail::BCField<> { +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(Value)); + } + + template + static void assertValid(const T &data) { + assert(data == Value && "data value does not match declared literal value"); + } +}; + +/// Represents a fixed-width value in a bitcode record. +/// +/// Note that the LLVM bitcode format only supports unsigned values. +template +class BCFixed : public detail::BCField<> { +public: + static_assert(Width <= 64, "fixed-width field is too large"); + + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, Width)); + } + + static void assertValid(const bool &data) { + assert(llvm::isUInt(data) && + "data value does not fit in the given bit width"); + } + + template + static void assertValid(const T &data) { + assert(data >= 0 && "cannot encode signed integers"); + assert(llvm::isUInt(data) && + "data value does not fit in the given bit width"); + } +}; + +/// Represents a variable-width value in a bitcode record. +/// +/// The \p Width parameter should include the continuation bit. +/// +/// Note that the LLVM bitcode format only supports unsigned values. +template +class BCVBR : public detail::BCField<> { + static_assert(Width >= 2, "width does not have room for continuation bit"); + +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, Width)); + } + + template + static void assertValid(const T &data) { + assert(data >= 0 && "cannot encode signed integers"); + } +}; + +/// Represents a character encoded in LLVM's Char6 encoding. +/// +/// This format is suitable for encoding decimal numbers (without signs or +/// exponents) and C identifiers (without dollar signs), but not much else. +/// +/// \sa http://llvm.org/docs/BitCodeFormat.html#char6-encoded-value +class BCChar6 : public detail::BCField<> { +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Char6)); + } + + template + static void assertValid(const T &data) { + assert(llvm::BitCodeAbbrevOp::isChar6(data) && "invalid Char6 data"); + } + + template + char convert(T rawValue) { + return static_cast(rawValue); + } +}; + +/// Represents an untyped blob of bytes. +/// +/// If present, this must be the last field in a record. +class BCBlob : public detail::BCField { +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)); + } +}; + +/// Represents an array of some other type. +/// +/// If present, this must be the last field in a record. +template +class BCArray : public detail::BCField { + static_assert(!ElementTy::IsCompound, "arrays can only contain scalar types"); +public: + static void emitOp(llvm::BitCodeAbbrev &abbrev) { + abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Array)); + ElementTy::emitOp(abbrev); + } +}; + +namespace detail { +/// Attaches the last field to an abbreviation. +/// +/// This is the base case for \c emitOps. +/// +/// \sa BCRecordLayout::emitAbbrev +template +static void emitOps(llvm::BitCodeAbbrev &abbrev) { + FieldTy::emitOp(abbrev); +} + +/// Attaches fields to an abbreviation. +/// +/// This is the recursive case for \c emitOps. +/// +/// \sa BCRecordLayout::emitAbbrev +template +static void emitOps(llvm::BitCodeAbbrev &abbrev) { + static_assert(!FieldTy::IsCompound, + "arrays and blobs may not appear in the middle of a record"); + FieldTy::emitOp(abbrev); + emitOps(abbrev); +} + +/// Helper class for dealing with a scalar element in the middle of a record. +/// +/// \sa BCRecordLayout +template +class BCRecordCoding { +public: + template + static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer, + unsigned code, ElementDataTy element, DataTy &&...data) { + static_assert(!ElementTy::IsCompound, + "arrays and blobs may not appear in the middle of a record"); + ElementTy::assertValid(element); + buffer.push_back(element); + BCRecordCoding::emit(Stream, buffer, code, + std::forward(data)...); + } + + template + static void read(ArrayRef buffer, ElementDataTy &element, + DataTy &&...data) { + assert(!buffer.empty() && "too few elements in buffer"); + element = ElementTy::convert(buffer.front()); + BCRecordCoding::read(buffer.slice(1), + std::forward(data)...); + } + + template + static void read(ArrayRef buffer, NoneType, DataTy &&...data) { + assert(!buffer.empty() && "too few elements in buffer"); + BCRecordCoding::read(buffer.slice(1), + std::forward(data)...); + } +}; + +/// Helper class for dealing with a scalar element at the end of a record. +/// +/// This has a separate implementation because up until now we've only been +/// \em building the record (into a data buffer), and now we need to hand it +/// off to the BitstreamWriter to be emitted. +/// +/// \sa BCRecordLayout +template +class BCRecordCoding { +public: + template + static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer, + unsigned code, const DataTy &data) { + static_assert(!ElementTy::IsCompound, + "arrays and blobs need special handling"); + ElementTy::assertValid(data); + buffer.push_back(data); + Stream.EmitRecordWithAbbrev(code, buffer); + } + + template + static void read(ArrayRef buffer, DataTy &data) { + assert(buffer.size() == 1 && "record data does not match layout"); + data = ElementTy::convert(buffer.front()); + } + + template + static void read(ArrayRef buffer, NoneType) { + assert(buffer.size() == 1 && "record data does not match layout"); + (void)buffer; + } + + template + static void read(ArrayRef buffer) = delete; +}; + +/// Helper class for dealing with an array at the end of a record. +/// +/// \sa BCRecordLayout::emitRecord +template +class BCRecordCoding> { +public: + template + static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer, + unsigned code, StringRef data) { + // TODO: validate array data. + Stream.EmitRecordWithArray(code, buffer, data); + } + + template + static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer, + unsigned code, const ArrayTy &array) { +#ifndef NDEBUG + for (auto &element : array) + ElementTy::assertValid(element); +#endif + buffer.reserve(buffer.size() + std::distance(array.begin(), array.end())); + std::copy(array.begin(), array.end(), std::back_inserter(buffer)); + Stream.EmitRecordWithAbbrev(code, buffer); + } + + template + static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer, + unsigned code, ElementDataTy element, DataTy... data) { + std::array array{ { element, data... } }; + emit(Stream, buffer, code, array); + } + + template + static void emit(llvm::BitstreamWriter &Stream, BufferTy &Buffer, + unsigned code, NoneType) { + Stream.EmitRecordWithAbbrev(code, Buffer); + } + + template + static void read(ArrayRef Buffer, ArrayRef &rawData) { + rawData = Buffer; + } + + template + static void read(ArrayRef buffer, ArrayTy &array) { + array.append(llvm::map_iterator(buffer.begin(), T::convert), + llvm::map_iterator(buffer.end(), T::convert)); + } + + template + static void read(ArrayRef buffer, NoneType) { + (void)buffer; + } + + template + static void read(ArrayRef buffer) = delete; +}; + +/// Helper class for dealing with a blob at the end of a record. +/// +/// \sa BCRecordLayout +template <> +class BCRecordCoding { +public: + template + static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer, + unsigned code, StringRef data) { + Stream.EmitRecordWithBlob(code, buffer, data); + } + + template + static void read(ArrayRef buffer) { + (void)buffer; + } + + /// Blob data is not stored in the buffer if you are using the correct + /// accessor; this method should not be used. + template + static void read(ArrayRef buffer, DataTy &data) = delete; +}; + +/// A type trait whose \c type field is the last of its template parameters. +template +struct last_type { + using type = typename last_type::type; +}; + +template +struct last_type { + using type = Head; +}; + +/// A type trait whose \c value field is \c true if the last type is BCBlob. +template +using has_blob = std::is_same::type>; + +/// A type trait whose \c value field is \c true if the given type is a +/// BCArray (of any element kind). +template +struct is_array { +private: + template + static bool check(BCArray *); + static int check(...); + +public: + typedef bool value_type; + static constexpr bool value = + !std::is_same::value; +}; + +/// A type trait whose \c value field is \c true if the last type is a +/// BCArray (of any element kind). +template +using has_array = is_array::type>; +} + +/// Represents a single bitcode record type. +/// +/// This class template is meant to be instantiated and then given a name, +/// so that from then on that name can be used. +template +class BCGenericRecordLayout { + llvm::BitstreamWriter &Stream; + +public: + /// The abbreviation code used for this record in the current block. + /// + /// Note that this is not the same as the semantic record code, which is the + /// first field of the record. + const unsigned AbbrevCode; + + /// Create a layout and register it with the given bitstream writer. + explicit BCGenericRecordLayout(llvm::BitstreamWriter &Stream) + : Stream(Stream), AbbrevCode(emitAbbrev(Stream)) {} + + /// Emit a record to the bitstream writer, using the given buffer for scratch + /// space. + /// + /// Note that even fixed arguments must be specified here. + template + void emit(BufferTy &buffer, unsigned id, Data &&... data) const { + emitRecord(Stream, buffer, AbbrevCode, id, std::forward(data)...); + } + + /// Registers this record's layout with the bitstream reader. + /// + /// eturns The abbreviation code for the newly-registered record type. + static unsigned emitAbbrev(llvm::BitstreamWriter &Stream) { + auto Abbrev = std::make_shared(); + detail::emitOps(*Abbrev); + return Stream.EmitAbbrev(std::move(Abbrev)); + } + + /// Emit a record identified by \p abbrCode to bitstream reader \p Stream, + /// using \p buffer for scratch space. + /// + /// Note that even fixed arguments must be specified here. Blobs are passed + /// as StringRefs, while arrays can be passed inline, as aggregates, or as + /// pre-encoded StringRef data. Skipped values and empty arrays should use + /// the special Nothing value. + template + static void emitRecord(llvm::BitstreamWriter &Stream, BufferTy &buffer, + unsigned abbrCode, unsigned recordID, + Data &&... data) { + static_assert(sizeof...(data) <= sizeof...(Fields) || + detail::has_array::value, + "Too many record elements"); + static_assert(sizeof...(data) >= sizeof...(Fields), + "Too few record elements"); + buffer.clear(); + detail::BCRecordCoding::emit( + Stream, buffer, abbrCode, recordID, std::forward(data)...); + } + + /// Extract record data from \p buffer into the given data fields. + /// + /// Note that even fixed arguments must be specified here. Pass \c Nothing + /// if you don't care about a particular parameter. Blob data is not included + /// in the buffer and should be handled separately by the caller. + template + static void readRecord(ArrayRef buffer, Data &&... data) { + static_assert(sizeof...(data) <= sizeof...(Fields), + "Too many record elements"); + static_assert(sizeof...(Fields) <= + sizeof...(data) + detail::has_blob::value, + "Too few record elements"); + return detail::BCRecordCoding::read( + buffer, std::forward(data)...); + } + + /// Extract record data from \p buffer into the given data fields. + /// + /// Note that even fixed arguments must be specified here. Pass \c Nothing + /// if you don't care about a particular parameter. Blob data is not included + /// in the buffer and should be handled separately by the caller. + template + static void readRecord(BufferTy &buffer, Data &&... data) { + return readRecord(llvm::makeArrayRef(buffer), std::forward(data)...); + } +}; + +/// A record with a fixed record code. +template +class BCRecordLayout + : public BCGenericRecordLayout, Fields...> { + using Base = BCGenericRecordLayout, Fields...>; +public: + enum : unsigned { + /// The record code associated with this layout. + Code = RecordCode + }; + + /// Create a layout and register it with the given bitstream writer. + explicit BCRecordLayout(llvm::BitstreamWriter &Stream) : Base(Stream) {} + + /// Emit a record to the bitstream writer, using the given buffer for scratch + /// space. + /// + /// Note that even fixed arguments must be specified here. + template + void emit(BufferTy &buffer, Data &&... data) const { + Base::emit(buffer, RecordCode, std::forward(data)...); + } + + /// Emit a record identified by \p abbrCode to bitstream reader \p Stream, + /// using \p buffer for scratch space. + /// + /// Note that even fixed arguments must be specified here. Currently, arrays + /// and blobs can only be passed as StringRefs. + template + static void emitRecord(llvm::BitstreamWriter &Stream, BufferTy &buffer, + unsigned abbrCode, Data &&... data) { + Base::emitRecord(Stream, buffer, abbrCode, RecordCode, + std::forward(data)...); + } +}; + +/// RAII object to pair entering and exiting a sub-block. +class BCBlockRAII { + llvm::BitstreamWriter &Stream; +public: + BCBlockRAII(llvm::BitstreamWriter &Stream, unsigned block, unsigned abbrev) + : Stream(Stream) { + Stream.EnterSubblock(block, abbrev); + } + + ~BCBlockRAII() { + Stream.ExitBlock(); + } +}; +} + +#endif