#ifndef LLVM_BITCODE_BITCODECONVENIENCE_H
#define LLVM_BITCODE_BITCODECONVENIENCE_H
#include "llvm/Bitstream/BitCodes.h"
#include "llvm/Bitstream/BitstreamWriter.h"
#include <cstdint>
namespace llvm {
namespace detail {
template <bool Compound = false> class BCField {
public:
static const bool IsCompound = Compound;
template <typename T> static void assertValid(const T &data) {}
template <typename T> static T convert(T rawValue) { return rawValue; }
};
}
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");
}
};
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");
}
};
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");
}
};
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);
}
};
class BCBlob : public detail::BCField<true> {
public:
static void emitOp(llvm::BitCodeAbbrev &abbrev) {
abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
}
};
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 {
template <typename FieldTy> static void emitOps(llvm::BitCodeAbbrev &abbrev) {
FieldTy::emitOp(abbrev);
}
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);
}
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, NoneType, DataTy &&...data) {
assert(!buffer.empty() && "too few elements in buffer");
BCRecordCoding<Fields...>::read(buffer.slice(1),
std::forward<DataTy>(data)...);
}
};
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, NoneType) {
assert(buffer.size() == 1 && "record data does not match layout");
(void)buffer;
}
template <typename T> static void read(ArrayRef<T> buffer) = delete;
};
template <typename ElementTy> class BCRecordCoding<BCArray<ElementTy>> {
public:
template <typename BufferTy>
static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer,
unsigned code, StringRef 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, NoneType) {
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, NoneType) {
(void)buffer;
}
template <typename T> static void read(ArrayRef<T> buffer) = delete;
};
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; }
template <typename T, typename DataTy>
static void read(ArrayRef<T> buffer, DataTy &data) = delete;
};
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; };
template <typename... Types>
using has_blob = std::is_same<BCBlob, typename last_type<int, Types...>::type>;
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;
};
template <typename... Types>
using has_array = is_array<typename last_type<int, Types...>::type>;
}
template <typename IDField, typename... Fields> class BCGenericRecordLayout {
llvm::BitstreamWriter &Stream;
public:
const unsigned AbbrevCode;
explicit BCGenericRecordLayout(llvm::BitstreamWriter &Stream)
: Stream(Stream), AbbrevCode(emitAbbrev(Stream)) {}
template <typename BufferTy, typename... Data>
void emit(BufferTy &buffer, unsigned id, Data &&...data) const {
emitRecord(Stream, buffer, AbbrevCode, id, std::forward<Data>(data)...);
}
static unsigned emitAbbrev(llvm::BitstreamWriter &Stream) {
auto Abbrev = std::make_shared<llvm::BitCodeAbbrev>();
detail::emitOps<IDField, Fields...>(*Abbrev);
return Stream.EmitAbbrev(std::move(Abbrev));
}
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)...);
}
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)...);
}
template <typename BufferTy, typename... Data>
static void readRecord(BufferTy &buffer, Data &&...data) {
return readRecord(llvm::makeArrayRef(buffer), std::forward<Data>(data)...);
}
};
template <unsigned RecordCode, typename... Fields>
class BCRecordLayout
: public BCGenericRecordLayout<BCLiteral<RecordCode>, Fields...> {
using Base = BCGenericRecordLayout<BCLiteral<RecordCode>, Fields...>;
public:
enum : unsigned {
Code = RecordCode
};
explicit BCRecordLayout(llvm::BitstreamWriter &Stream) : Base(Stream) {}
template <typename BufferTy, typename... Data>
void emit(BufferTy &buffer, Data &&...data) const {
Base::emit(buffer, RecordCode, std::forward<Data>(data)...);
}
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)...);
}
};
class BCBlockRAII {
llvm::BitstreamWriter &Stream;
public:
BCBlockRAII(llvm::BitstreamWriter &Stream, unsigned block, unsigned abbrev)
: Stream(Stream) {
Stream.EnterSubblock(block, abbrev);
}
~BCBlockRAII() { Stream.ExitBlock(); }
};
}
#endif