#ifndef LLVM_BITCODE_RECORDLAYOUT_H
#define LLVM_BITCODE_RECORDLAYOUT_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Fixnum.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/BitCodes.h"
#include "llvm/Bitcode/BitstreamWriter.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MathExtras.h"
namespace llvm {
namespace impl {
template<bool COMPOUND = false>
class BCField {
public:
static const bool IS_COMPOUND = 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 impl::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 impl::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));
}
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");
}
using value_type = Fixnum<Width>;
template<typename T>
static value_type convert(T rawValue) {
return static_cast<value_type>(rawValue);
}
};
template<unsigned Width>
class BCVBR : public impl::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 impl::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 impl::BCField<true> {
public:
static void emitOp(llvm::BitCodeAbbrev &abbrev) {
abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
}
};
template<typename Element>
class BCArray : public impl::BCField<true> {
static_assert(!Element::IS_COMPOUND, "arrays can only contain scalar types");
public:
static void emitOp(llvm::BitCodeAbbrev &abbrev) {
abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Array));
Element::emitOp(abbrev);
}
};
namespace impl {
template<typename Last>
static void emitOps(llvm::BitCodeAbbrev &abbrev) {
Last::emitOp(abbrev);
}
template<typename First, typename Next, typename ...Rest>
static void emitOps(llvm::BitCodeAbbrev &abbrev) {
static_assert(!First::IS_COMPOUND,
"arrays and blobs may not appear in the middle of a record");
First::emitOp(abbrev);
emitOps<Next, Rest...>(abbrev);
}
template<typename First, typename... Fields>
class BCRecordCoding {
public:
template <typename BufferTy, typename FirstData, typename... Data>
static void emit(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, FirstData data, Data... rest) {
static_assert(!First::IS_COMPOUND,
"arrays and blobs may not appear in the middle of a record");
First::assertValid(data);
buffer.push_back(data);
BCRecordCoding<Fields...>::emit(out, buffer, abbrCode, rest...);
}
template <typename ElementTy, typename FirstData, typename... Data>
static void read(ArrayRef<ElementTy> buffer,
FirstData &data, Data &&...rest) {
assert(!buffer.empty() && "too few elements in buffer");
data = First::convert(buffer.front());
BCRecordCoding<Fields...>::read(buffer.slice(1),
std::forward<Data>(rest)...);
}
template <typename ElementTy, typename... Data>
static void read(ArrayRef<ElementTy> buffer,
NoneType, Data &&...rest) {
assert(!buffer.empty() && "too few elements in buffer");
BCRecordCoding<Fields...>::read(buffer.slice(1),
std::forward<Data>(rest)...);
}
};
template<typename Last>
class BCRecordCoding<Last> {
public:
template <typename BufferTy, typename LastData>
static void emit(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, LastData data) {
static_assert(!Last::IS_COMPOUND,
"arrays and blobs need special handling");
Last::assertValid(data);
buffer.push_back(data);
out.EmitRecordWithAbbrev(abbrCode, buffer);
}
template <typename ElementTy, typename LastData>
static void read(ArrayRef<ElementTy> buffer, LastData &data) {
assert(buffer.size() == 1 && "record data does not match layout");
data = Last::convert(buffer.front());
}
template <typename ElementTy>
static void read(ArrayRef<ElementTy> buffer, NoneType) {
assert(buffer.size() == 1 && "record data does not match layout");
(void)buffer;
}
template <typename ElementTy>
static void read(ArrayRef<ElementTy> buffer) = delete;
};
template<typename EleTy>
class BCRecordCoding<BCArray<EleTy>> {
public:
template <typename BufferTy>
static void emit(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, StringRef arrayData) {
out.EmitRecordWithArray(abbrCode, buffer, arrayData);
}
template <typename BufferTy, typename ArrayTy>
static void emit(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, const ArrayTy &arrayData) {
#ifndef NDEBUG
for (auto &item : arrayData)
EleTy::assertValid(item);
#endif
buffer.reserve(buffer.size() + arrayData.size());
std::copy(arrayData.begin(), arrayData.end(),
std::back_inserter(buffer));
out.EmitRecordWithAbbrev(abbrCode, buffer);
}
template <typename BufferTy, typename FirstData, typename ...RestData>
static void emit(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, FirstData firstData,
RestData... restData) {
std::array<FirstData, 1+sizeof...(restData)> arrayData{ {
firstData,
restData...
} };
emit(out, buffer, abbrCode, arrayData);
}
template <typename BufferTy>
static void emit(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, NoneType) {
out.EmitRecordWithAbbrev(abbrCode, buffer);
}
template <typename ElementTy>
static void read(ArrayRef<ElementTy> buffer, ArrayRef<ElementTy> &rawData) {
rawData = buffer;
}
template <typename ElementTy, typename ArrayTy>
static void read(ArrayRef<ElementTy> buffer, ArrayTy &array) {
array.append(llvm::map_iterator(buffer.begin(), ElementTy::convert),
llvm::map_iterator(buffer.end(), ElementTy::convert));
}
template <typename ElementTy>
static void read(ArrayRef<ElementTy> buffer, NoneType) {
(void)buffer;
}
template <typename ElementTy>
static void read(ArrayRef<ElementTy> buffer) = delete;
};
template<>
class BCRecordCoding<BCBlob> {
public:
template <typename BufferTy>
static void emit(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, StringRef blobData) {
out.EmitRecordWithBlob(abbrCode, buffer, blobData);
}
template <typename ElementTy>
static void read(ArrayRef<ElementTy> buffer) {
(void)buffer;
}
template <typename ElementTy, typename DataTy>
static void read(ArrayRef<ElementTy> buffer, DataTy &data) = delete;
};
template<typename First, typename ...Rest>
struct last_type {
using type = typename last_type<Rest...>::type;
};
template<typename Last>
struct last_type<Last> {
using type = Last;
};
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 &Out;
public:
const unsigned AbbrevCode;
explicit BCGenericRecordLayout(llvm::BitstreamWriter &out)
: Out(out), AbbrevCode(emitAbbrev(out)) {}
template <typename BufferTy, typename... Data>
void emit(BufferTy &buffer, unsigned recordID, Data... data) const {
emitRecord(Out, buffer, AbbrevCode, recordID, data...);
}
static unsigned emitAbbrev(llvm::BitstreamWriter &out) {
auto *abbrev = new llvm::BitCodeAbbrev();
impl::emitOps<IDField, Fields...>(*abbrev);
return out.EmitAbbrev(abbrev);
}
template <typename BufferTy, typename... Data>
static void emitRecord(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, unsigned recordID, Data... data) {
static_assert(sizeof...(data) <= sizeof...(Fields) ||
impl::has_array<Fields...>::value,
"Too many record elements");
static_assert(sizeof...(data) >= sizeof...(Fields),
"Too few record elements");
buffer.clear();
impl::BCRecordCoding<IDField, Fields...>::emit(out, buffer, abbrCode,
recordID, 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) + impl::has_blob<Fields...>::value,
"Too few record elements");
return impl::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 &out) : Base(out) {}
template <typename BufferTy, typename... Data>
void emit(BufferTy &buffer, Data... data) const {
Base::emit(buffer, RecordCode, data...);
}
template <typename BufferTy, typename... Data>
static void emitRecord(llvm::BitstreamWriter &out, BufferTy &buffer,
unsigned abbrCode, Data... data) {
Base::emitRecord(out, buffer, abbrCode, RecordCode, data...);
}
};
class BCBlockRAII {
llvm::BitstreamWriter &Writer;
public:
BCBlockRAII(llvm::BitstreamWriter &writer, unsigned blockID,
unsigned abbrevLen)
: Writer(writer) {
writer.EnterSubblock(blockID, abbrevLen);
}
~BCBlockRAII() {
Writer.ExitBlock();
}
};
}
#endif