//===- 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