| //===- 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 <cstdint> |
| #include <optional> |
| |
| namespace llvm { |
| namespace detail { |
| /// Convenience base for all kinds of bitcode abbreviation fields. |
| /// |
| /// This just defines common properties queried by the metaprogramming. |
| template <bool Compound = false> class BCField { |
| public: |
| static const bool IsCompound = Compound; |
| |
| /// Asserts that the given data is a valid value for this field. |
| template <typename T> static void assertValid(const T &data) {} |
| |
| /// Converts a raw numeric representation of this value to its preferred |
| /// type. |
| template <typename T> static T convert(T rawValue) { return rawValue; } |
| }; |
| } // namespace detail |
| |
| /// 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 <uint64_t Value> class BCLiteral : public detail::BCField<> { |
| public: |
| static void emitOp(llvm::BitCodeAbbrev &abbrev) { |
| abbrev.Add(llvm::BitCodeAbbrevOp(Value)); |
| } |
| |
| template <typename T> 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 <unsigned Width> 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<Width>(data) && |
| "data value does not fit in the given bit width"); |
| } |
| |
| template <typename T> static void assertValid(const T &data) { |
| assert(data >= 0 && "cannot encode signed integers"); |
| assert(llvm::isUInt<Width>(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 <unsigned Width> 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 <typename T> 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 <typename T> static void assertValid(const T &data) { |
| assert(llvm::BitCodeAbbrevOp::isChar6(data) && "invalid Char6 data"); |
| } |
| |
| template <typename T> char convert(T rawValue) { |
| return static_cast<char>(rawValue); |
| } |
| }; |
| |
| /// Represents an untyped blob of bytes. |
| /// |
| /// If present, this must be the last field in a record. |
| class BCBlob : public detail::BCField<true> { |
| 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 <typename ElementTy> class BCArray : public detail::BCField<true> { |
| 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 <typename FieldTy> 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 <typename FieldTy, typename Next, typename... Rest> |
| 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<Next, Rest...>(abbrev); |
| } |
| |
| /// Helper class for dealing with a scalar element in the middle of a record. |
| /// |
| /// \sa BCRecordLayout |
| template <typename ElementTy, typename... Fields> class BCRecordCoding { |
| public: |
| template <typename BufferTy, typename ElementDataTy, typename... DataTy> |
| 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<Fields...>::emit(Stream, buffer, code, |
| std::forward<DataTy>(data)...); |
| } |
| |
| template <typename T, typename ElementDataTy, typename... DataTy> |
| static void read(ArrayRef<T> buffer, ElementDataTy &element, |
| DataTy &&...data) { |
| assert(!buffer.empty() && "too few elements in buffer"); |
| element = ElementTy::convert(buffer.front()); |
| BCRecordCoding<Fields...>::read(buffer.slice(1), |
| std::forward<DataTy>(data)...); |
| } |
| |
| template <typename T, typename... DataTy> |
| static void read(ArrayRef<T> buffer, std::nullopt_t, DataTy &&...data) { |
| assert(!buffer.empty() && "too few elements in buffer"); |
| BCRecordCoding<Fields...>::read(buffer.slice(1), |
| std::forward<DataTy>(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 <typename ElementTy> class BCRecordCoding<ElementTy> { |
| public: |
| template <typename BufferTy, typename DataTy> |
| 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 <typename T, typename DataTy> |
| static void read(ArrayRef<T> buffer, DataTy &data) { |
| assert(buffer.size() == 1 && "record data does not match layout"); |
| data = ElementTy::convert(buffer.front()); |
| } |
| |
| template <typename T> static void read(ArrayRef<T> buffer, std::nullopt_t) { |
| assert(buffer.size() == 1 && "record data does not match layout"); |
| (void)buffer; |
| } |
| |
| template <typename T> static void read(ArrayRef<T> buffer) = delete; |
| }; |
| |
| /// Helper class for dealing with an array at the end of a record. |
| /// |
| /// \sa BCRecordLayout::emitRecord |
| template <typename ElementTy> class BCRecordCoding<BCArray<ElementTy>> { |
| public: |
| template <typename BufferTy> |
| static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer, |
| unsigned code, StringRef data) { |
| // TODO: validate array data. |
| Stream.EmitRecordWithArray(code, buffer, data); |
| } |
| |
| template <typename BufferTy, typename ArrayTy> |
| 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 <typename BufferTy, typename ElementDataTy, typename... DataTy> |
| static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer, |
| unsigned code, ElementDataTy element, DataTy... data) { |
| std::array<ElementDataTy, 1 + sizeof...(data)> array{{element, data...}}; |
| emit(Stream, buffer, code, array); |
| } |
| |
| template <typename BufferTy> |
| static void emit(llvm::BitstreamWriter &Stream, BufferTy &Buffer, |
| unsigned code, std::nullopt_t) { |
| Stream.EmitRecordWithAbbrev(code, Buffer); |
| } |
| |
| template <typename T> |
| static void read(ArrayRef<T> Buffer, ArrayRef<T> &rawData) { |
| rawData = Buffer; |
| } |
| |
| template <typename T, typename ArrayTy> |
| static void read(ArrayRef<T> buffer, ArrayTy &array) { |
| array.append(llvm::map_iterator(buffer.begin(), T::convert), |
| llvm::map_iterator(buffer.end(), T::convert)); |
| } |
| |
| template <typename T> static void read(ArrayRef<T> buffer, std::nullopt_t) { |
| (void)buffer; |
| } |
| |
| template <typename T> static void read(ArrayRef<T> buffer) = delete; |
| }; |
| |
| /// Helper class for dealing with a blob at the end of a record. |
| /// |
| /// \sa BCRecordLayout |
| template <> class BCRecordCoding<BCBlob> { |
| public: |
| template <typename BufferTy> |
| static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer, |
| unsigned code, StringRef data) { |
| Stream.EmitRecordWithBlob(code, buffer, data); |
| } |
| |
| template <typename T> static void read(ArrayRef<T> 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 <typename T, typename DataTy> |
| static void read(ArrayRef<T> buffer, DataTy &data) = delete; |
| }; |
| |
| /// A type trait whose \c type field is the last of its template parameters. |
| template <typename Head, typename... Tail> struct last_type { |
| using type = typename last_type<Tail...>::type; |
| }; |
| |
| template <typename Head> struct last_type<Head> { using type = Head; }; |
| |
| /// A type trait whose \c value field is \c true if the last type is BCBlob. |
| template <typename... Types> |
| using has_blob = std::is_same<BCBlob, typename last_type<int, Types...>::type>; |
| |
| /// A type trait whose \c value field is \c true if the given type is a |
| /// BCArray (of any element kind). |
| template <typename T> struct is_array { |
| private: |
| template <typename E> static bool check(BCArray<E> *); |
| static int check(...); |
| |
| public: |
| typedef bool value_type; |
| static constexpr bool value = !std::is_same<decltype(check((T *)nullptr)), |
| decltype(check(false))>::value; |
| }; |
| |
| /// A type trait whose \c value field is \c true if the last type is a |
| /// BCArray (of any element kind). |
| template <typename... Types> |
| using has_array = is_array<typename last_type<int, Types...>::type>; |
| } // namespace detail |
| |
| /// 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 <typename IDField, typename... Fields> 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 <typename BufferTy, typename... Data> |
| void emit(BufferTy &buffer, unsigned id, Data &&...data) const { |
| emitRecord(Stream, buffer, AbbrevCode, id, std::forward<Data>(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<llvm::BitCodeAbbrev>(); |
| detail::emitOps<IDField, Fields...>(*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 <typename BufferTy, typename... Data> |
| static void emitRecord(llvm::BitstreamWriter &Stream, BufferTy &buffer, |
| unsigned abbrCode, unsigned recordID, Data &&...data) { |
| static_assert(sizeof...(data) <= sizeof...(Fields) || |
| detail::has_array<Fields...>::value, |
| "Too many record elements"); |
| static_assert(sizeof...(data) >= sizeof...(Fields), |
| "Too few record elements"); |
| buffer.clear(); |
| detail::BCRecordCoding<IDField, Fields...>::emit( |
| Stream, buffer, abbrCode, recordID, std::forward<Data>(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 <typename ElementTy, typename... Data> |
| static void readRecord(ArrayRef<ElementTy> buffer, Data &&...data) { |
| static_assert(sizeof...(data) <= sizeof...(Fields), |
| "Too many record elements"); |
| static_assert(sizeof...(Fields) <= |
| sizeof...(data) + detail::has_blob<Fields...>::value, |
| "Too few record elements"); |
| return detail::BCRecordCoding<Fields...>::read(buffer, |
| std::forward<Data>(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 <typename BufferTy, typename... Data> |
| static void readRecord(BufferTy &buffer, Data &&...data) { |
| return readRecord(llvm::ArrayRef(buffer), std::forward<Data>(data)...); |
| } |
| }; |
| |
| /// A record with a fixed record code. |
| template <unsigned RecordCode, typename... Fields> |
| class BCRecordLayout |
| : public BCGenericRecordLayout<BCLiteral<RecordCode>, Fields...> { |
| using Base = BCGenericRecordLayout<BCLiteral<RecordCode>, 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 <typename BufferTy, typename... Data> |
| void emit(BufferTy &buffer, Data &&...data) const { |
| Base::emit(buffer, RecordCode, std::forward<Data>(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 <typename BufferTy, typename... Data> |
| static void emitRecord(llvm::BitstreamWriter &Stream, BufferTy &buffer, |
| unsigned abbrCode, Data &&...data) { |
| Base::emitRecord(Stream, buffer, abbrCode, RecordCode, |
| std::forward<Data>(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(); } |
| }; |
| } // namespace llvm |
| |
| #endif |