blob: 0060d014ba829f942615992c8132d2f8adbe61de [file] [log] [blame]
Olivier Deprezf4ef2d02021-04-20 13:36:24 +02001//===- llvm/Bitcode/BitcodeConvenience.h - Convenience Wrappers -*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file Convenience wrappers for the LLVM bitcode format and bitstream APIs.
10///
11/// This allows you to use a sort of DSL to declare and use bitcode
12/// abbreviations and records. Example:
13///
14/// \code
15/// using Metadata = BCRecordLayout<
16/// METADATA_ID, // ID
17/// BCFixed<16>, // Module format major version
18/// BCFixed<16>, // Module format minor version
19/// BCBlob // misc. version information
20/// >;
21/// Metadata metadata(Out);
22/// metadata.emit(ScratchRecord, VERSION_MAJOR, VERSION_MINOR, Data);
23/// \endcode
24///
25/// For details on the bitcode format, see
26/// http://llvm.org/docs/BitCodeFormat.html
27///
28//===----------------------------------------------------------------------===//
29
30#ifndef LLVM_BITCODE_BITCODECONVENIENCE_H
31#define LLVM_BITCODE_BITCODECONVENIENCE_H
32
33#include "llvm/Bitstream/BitCodes.h"
34#include "llvm/Bitstream/BitstreamWriter.h"
35#include <cstdint>
36
37namespace llvm {
38namespace detail {
39/// Convenience base for all kinds of bitcode abbreviation fields.
40///
41/// This just defines common properties queried by the metaprogramming.
42template <bool Compound = false> class BCField {
43public:
44 static const bool IsCompound = Compound;
45
46 /// Asserts that the given data is a valid value for this field.
47 template <typename T> static void assertValid(const T &data) {}
48
49 /// Converts a raw numeric representation of this value to its preferred
50 /// type.
51 template <typename T> static T convert(T rawValue) { return rawValue; }
52};
53} // namespace detail
54
55/// Represents a literal operand in a bitcode record.
56///
57/// The value of a literal operand is the same for all instances of the record,
58/// so it is only emitted in the abbreviation definition.
59///
60/// Note that because this uses a compile-time template, you cannot have a
61/// literal operand that is fixed at run-time without dropping down to the
62/// raw LLVM APIs.
63template <uint64_t Value> class BCLiteral : public detail::BCField<> {
64public:
65 static void emitOp(llvm::BitCodeAbbrev &abbrev) {
66 abbrev.Add(llvm::BitCodeAbbrevOp(Value));
67 }
68
69 template <typename T> static void assertValid(const T &data) {
70 assert(data == Value && "data value does not match declared literal value");
71 }
72};
73
74/// Represents a fixed-width value in a bitcode record.
75///
76/// Note that the LLVM bitcode format only supports unsigned values.
77template <unsigned Width> class BCFixed : public detail::BCField<> {
78public:
79 static_assert(Width <= 64, "fixed-width field is too large");
80
81 static void emitOp(llvm::BitCodeAbbrev &abbrev) {
82 abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, Width));
83 }
84
85 static void assertValid(const bool &data) {
86 assert(llvm::isUInt<Width>(data) &&
87 "data value does not fit in the given bit width");
88 }
89
90 template <typename T> static void assertValid(const T &data) {
91 assert(data >= 0 && "cannot encode signed integers");
92 assert(llvm::isUInt<Width>(data) &&
93 "data value does not fit in the given bit width");
94 }
95};
96
97/// Represents a variable-width value in a bitcode record.
98///
99/// The \p Width parameter should include the continuation bit.
100///
101/// Note that the LLVM bitcode format only supports unsigned values.
102template <unsigned Width> class BCVBR : public detail::BCField<> {
103 static_assert(Width >= 2, "width does not have room for continuation bit");
104
105public:
106 static void emitOp(llvm::BitCodeAbbrev &abbrev) {
107 abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, Width));
108 }
109
110 template <typename T> static void assertValid(const T &data) {
111 assert(data >= 0 && "cannot encode signed integers");
112 }
113};
114
115/// Represents a character encoded in LLVM's Char6 encoding.
116///
117/// This format is suitable for encoding decimal numbers (without signs or
118/// exponents) and C identifiers (without dollar signs), but not much else.
119///
120/// \sa http://llvm.org/docs/BitCodeFormat.html#char6-encoded-value
121class BCChar6 : public detail::BCField<> {
122public:
123 static void emitOp(llvm::BitCodeAbbrev &abbrev) {
124 abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Char6));
125 }
126
127 template <typename T> static void assertValid(const T &data) {
128 assert(llvm::BitCodeAbbrevOp::isChar6(data) && "invalid Char6 data");
129 }
130
131 template <typename T> char convert(T rawValue) {
132 return static_cast<char>(rawValue);
133 }
134};
135
136/// Represents an untyped blob of bytes.
137///
138/// If present, this must be the last field in a record.
139class BCBlob : public detail::BCField<true> {
140public:
141 static void emitOp(llvm::BitCodeAbbrev &abbrev) {
142 abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
143 }
144};
145
146/// Represents an array of some other type.
147///
148/// If present, this must be the last field in a record.
149template <typename ElementTy> class BCArray : public detail::BCField<true> {
150 static_assert(!ElementTy::IsCompound, "arrays can only contain scalar types");
151
152public:
153 static void emitOp(llvm::BitCodeAbbrev &abbrev) {
154 abbrev.Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Array));
155 ElementTy::emitOp(abbrev);
156 }
157};
158
159namespace detail {
160/// Attaches the last field to an abbreviation.
161///
162/// This is the base case for \c emitOps.
163///
164/// \sa BCRecordLayout::emitAbbrev
165template <typename FieldTy> static void emitOps(llvm::BitCodeAbbrev &abbrev) {
166 FieldTy::emitOp(abbrev);
167}
168
169/// Attaches fields to an abbreviation.
170///
171/// This is the recursive case for \c emitOps.
172///
173/// \sa BCRecordLayout::emitAbbrev
174template <typename FieldTy, typename Next, typename... Rest>
175static void emitOps(llvm::BitCodeAbbrev &abbrev) {
176 static_assert(!FieldTy::IsCompound,
177 "arrays and blobs may not appear in the middle of a record");
178 FieldTy::emitOp(abbrev);
179 emitOps<Next, Rest...>(abbrev);
180}
181
182/// Helper class for dealing with a scalar element in the middle of a record.
183///
184/// \sa BCRecordLayout
185template <typename ElementTy, typename... Fields> class BCRecordCoding {
186public:
187 template <typename BufferTy, typename ElementDataTy, typename... DataTy>
188 static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer,
189 unsigned code, ElementDataTy element, DataTy &&...data) {
190 static_assert(!ElementTy::IsCompound,
191 "arrays and blobs may not appear in the middle of a record");
192 ElementTy::assertValid(element);
193 buffer.push_back(element);
194 BCRecordCoding<Fields...>::emit(Stream, buffer, code,
195 std::forward<DataTy>(data)...);
196 }
197
198 template <typename T, typename ElementDataTy, typename... DataTy>
199 static void read(ArrayRef<T> buffer, ElementDataTy &element,
200 DataTy &&...data) {
201 assert(!buffer.empty() && "too few elements in buffer");
202 element = ElementTy::convert(buffer.front());
203 BCRecordCoding<Fields...>::read(buffer.slice(1),
204 std::forward<DataTy>(data)...);
205 }
206
207 template <typename T, typename... DataTy>
208 static void read(ArrayRef<T> buffer, NoneType, DataTy &&...data) {
209 assert(!buffer.empty() && "too few elements in buffer");
210 BCRecordCoding<Fields...>::read(buffer.slice(1),
211 std::forward<DataTy>(data)...);
212 }
213};
214
215/// Helper class for dealing with a scalar element at the end of a record.
216///
217/// This has a separate implementation because up until now we've only been
218/// \em building the record (into a data buffer), and now we need to hand it
219/// off to the BitstreamWriter to be emitted.
220///
221/// \sa BCRecordLayout
222template <typename ElementTy> class BCRecordCoding<ElementTy> {
223public:
224 template <typename BufferTy, typename DataTy>
225 static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer,
226 unsigned code, const DataTy &data) {
227 static_assert(!ElementTy::IsCompound,
228 "arrays and blobs need special handling");
229 ElementTy::assertValid(data);
230 buffer.push_back(data);
231 Stream.EmitRecordWithAbbrev(code, buffer);
232 }
233
234 template <typename T, typename DataTy>
235 static void read(ArrayRef<T> buffer, DataTy &data) {
236 assert(buffer.size() == 1 && "record data does not match layout");
237 data = ElementTy::convert(buffer.front());
238 }
239
240 template <typename T> static void read(ArrayRef<T> buffer, NoneType) {
241 assert(buffer.size() == 1 && "record data does not match layout");
242 (void)buffer;
243 }
244
245 template <typename T> static void read(ArrayRef<T> buffer) = delete;
246};
247
248/// Helper class for dealing with an array at the end of a record.
249///
250/// \sa BCRecordLayout::emitRecord
251template <typename ElementTy> class BCRecordCoding<BCArray<ElementTy>> {
252public:
253 template <typename BufferTy>
254 static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer,
255 unsigned code, StringRef data) {
256 // TODO: validate array data.
257 Stream.EmitRecordWithArray(code, buffer, data);
258 }
259
260 template <typename BufferTy, typename ArrayTy>
261 static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer,
262 unsigned code, const ArrayTy &array) {
263#ifndef NDEBUG
264 for (auto &element : array)
265 ElementTy::assertValid(element);
266#endif
267 buffer.reserve(buffer.size() + std::distance(array.begin(), array.end()));
268 std::copy(array.begin(), array.end(), std::back_inserter(buffer));
269 Stream.EmitRecordWithAbbrev(code, buffer);
270 }
271
272 template <typename BufferTy, typename ElementDataTy, typename... DataTy>
273 static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer,
274 unsigned code, ElementDataTy element, DataTy... data) {
275 std::array<ElementDataTy, 1 + sizeof...(data)> array{{element, data...}};
276 emit(Stream, buffer, code, array);
277 }
278
279 template <typename BufferTy>
280 static void emit(llvm::BitstreamWriter &Stream, BufferTy &Buffer,
281 unsigned code, NoneType) {
282 Stream.EmitRecordWithAbbrev(code, Buffer);
283 }
284
285 template <typename T>
286 static void read(ArrayRef<T> Buffer, ArrayRef<T> &rawData) {
287 rawData = Buffer;
288 }
289
290 template <typename T, typename ArrayTy>
291 static void read(ArrayRef<T> buffer, ArrayTy &array) {
292 array.append(llvm::map_iterator(buffer.begin(), T::convert),
293 llvm::map_iterator(buffer.end(), T::convert));
294 }
295
296 template <typename T> static void read(ArrayRef<T> buffer, NoneType) {
297 (void)buffer;
298 }
299
300 template <typename T> static void read(ArrayRef<T> buffer) = delete;
301};
302
303/// Helper class for dealing with a blob at the end of a record.
304///
305/// \sa BCRecordLayout
306template <> class BCRecordCoding<BCBlob> {
307public:
308 template <typename BufferTy>
309 static void emit(llvm::BitstreamWriter &Stream, BufferTy &buffer,
310 unsigned code, StringRef data) {
311 Stream.EmitRecordWithBlob(code, buffer, data);
312 }
313
314 template <typename T> static void read(ArrayRef<T> buffer) { (void)buffer; }
315
316 /// Blob data is not stored in the buffer if you are using the correct
317 /// accessor; this method should not be used.
318 template <typename T, typename DataTy>
319 static void read(ArrayRef<T> buffer, DataTy &data) = delete;
320};
321
322/// A type trait whose \c type field is the last of its template parameters.
323template <typename Head, typename... Tail> struct last_type {
324 using type = typename last_type<Tail...>::type;
325};
326
327template <typename Head> struct last_type<Head> { using type = Head; };
328
329/// A type trait whose \c value field is \c true if the last type is BCBlob.
330template <typename... Types>
331using has_blob = std::is_same<BCBlob, typename last_type<int, Types...>::type>;
332
333/// A type trait whose \c value field is \c true if the given type is a
334/// BCArray (of any element kind).
335template <typename T> struct is_array {
336private:
337 template <typename E> static bool check(BCArray<E> *);
338 static int check(...);
339
340public:
341 typedef bool value_type;
342 static constexpr bool value = !std::is_same<decltype(check((T *)nullptr)),
343 decltype(check(false))>::value;
344};
345
346/// A type trait whose \c value field is \c true if the last type is a
347/// BCArray (of any element kind).
348template <typename... Types>
349using has_array = is_array<typename last_type<int, Types...>::type>;
350} // namespace detail
351
352/// Represents a single bitcode record type.
353///
354/// This class template is meant to be instantiated and then given a name,
355/// so that from then on that name can be used.
356template <typename IDField, typename... Fields> class BCGenericRecordLayout {
357 llvm::BitstreamWriter &Stream;
358
359public:
360 /// The abbreviation code used for this record in the current block.
361 ///
362 /// Note that this is not the same as the semantic record code, which is the
363 /// first field of the record.
364 const unsigned AbbrevCode;
365
366 /// Create a layout and register it with the given bitstream writer.
367 explicit BCGenericRecordLayout(llvm::BitstreamWriter &Stream)
368 : Stream(Stream), AbbrevCode(emitAbbrev(Stream)) {}
369
370 /// Emit a record to the bitstream writer, using the given buffer for scratch
371 /// space.
372 ///
373 /// Note that even fixed arguments must be specified here.
374 template <typename BufferTy, typename... Data>
375 void emit(BufferTy &buffer, unsigned id, Data &&...data) const {
376 emitRecord(Stream, buffer, AbbrevCode, id, std::forward<Data>(data)...);
377 }
378
379 /// Registers this record's layout with the bitstream reader.
380 ///
381 /// eturns The abbreviation code for the newly-registered record type.
382 static unsigned emitAbbrev(llvm::BitstreamWriter &Stream) {
383 auto Abbrev = std::make_shared<llvm::BitCodeAbbrev>();
384 detail::emitOps<IDField, Fields...>(*Abbrev);
385 return Stream.EmitAbbrev(std::move(Abbrev));
386 }
387
388 /// Emit a record identified by \p abbrCode to bitstream reader \p Stream,
389 /// using \p buffer for scratch space.
390 ///
391 /// Note that even fixed arguments must be specified here. Blobs are passed
392 /// as StringRefs, while arrays can be passed inline, as aggregates, or as
393 /// pre-encoded StringRef data. Skipped values and empty arrays should use
394 /// the special Nothing value.
395 template <typename BufferTy, typename... Data>
396 static void emitRecord(llvm::BitstreamWriter &Stream, BufferTy &buffer,
397 unsigned abbrCode, unsigned recordID, Data &&...data) {
398 static_assert(sizeof...(data) <= sizeof...(Fields) ||
399 detail::has_array<Fields...>::value,
400 "Too many record elements");
401 static_assert(sizeof...(data) >= sizeof...(Fields),
402 "Too few record elements");
403 buffer.clear();
404 detail::BCRecordCoding<IDField, Fields...>::emit(
405 Stream, buffer, abbrCode, recordID, std::forward<Data>(data)...);
406 }
407
408 /// Extract record data from \p buffer into the given data fields.
409 ///
410 /// Note that even fixed arguments must be specified here. Pass \c Nothing
411 /// if you don't care about a particular parameter. Blob data is not included
412 /// in the buffer and should be handled separately by the caller.
413 template <typename ElementTy, typename... Data>
414 static void readRecord(ArrayRef<ElementTy> buffer, Data &&...data) {
415 static_assert(sizeof...(data) <= sizeof...(Fields),
416 "Too many record elements");
417 static_assert(sizeof...(Fields) <=
418 sizeof...(data) + detail::has_blob<Fields...>::value,
419 "Too few record elements");
420 return detail::BCRecordCoding<Fields...>::read(buffer,
421 std::forward<Data>(data)...);
422 }
423
424 /// Extract record data from \p buffer into the given data fields.
425 ///
426 /// Note that even fixed arguments must be specified here. Pass \c Nothing
427 /// if you don't care about a particular parameter. Blob data is not included
428 /// in the buffer and should be handled separately by the caller.
429 template <typename BufferTy, typename... Data>
430 static void readRecord(BufferTy &buffer, Data &&...data) {
431 return readRecord(llvm::makeArrayRef(buffer), std::forward<Data>(data)...);
432 }
433};
434
435/// A record with a fixed record code.
436template <unsigned RecordCode, typename... Fields>
437class BCRecordLayout
438 : public BCGenericRecordLayout<BCLiteral<RecordCode>, Fields...> {
439 using Base = BCGenericRecordLayout<BCLiteral<RecordCode>, Fields...>;
440
441public:
442 enum : unsigned {
443 /// The record code associated with this layout.
444 Code = RecordCode
445 };
446
447 /// Create a layout and register it with the given bitstream writer.
448 explicit BCRecordLayout(llvm::BitstreamWriter &Stream) : Base(Stream) {}
449
450 /// Emit a record to the bitstream writer, using the given buffer for scratch
451 /// space.
452 ///
453 /// Note that even fixed arguments must be specified here.
454 template <typename BufferTy, typename... Data>
455 void emit(BufferTy &buffer, Data &&...data) const {
456 Base::emit(buffer, RecordCode, std::forward<Data>(data)...);
457 }
458
459 /// Emit a record identified by \p abbrCode to bitstream reader \p Stream,
460 /// using \p buffer for scratch space.
461 ///
462 /// Note that even fixed arguments must be specified here. Currently, arrays
463 /// and blobs can only be passed as StringRefs.
464 template <typename BufferTy, typename... Data>
465 static void emitRecord(llvm::BitstreamWriter &Stream, BufferTy &buffer,
466 unsigned abbrCode, Data &&...data) {
467 Base::emitRecord(Stream, buffer, abbrCode, RecordCode,
468 std::forward<Data>(data)...);
469 }
470};
471
472/// RAII object to pair entering and exiting a sub-block.
473class BCBlockRAII {
474 llvm::BitstreamWriter &Stream;
475
476public:
477 BCBlockRAII(llvm::BitstreamWriter &Stream, unsigned block, unsigned abbrev)
478 : Stream(Stream) {
479 Stream.EnterSubblock(block, abbrev);
480 }
481
482 ~BCBlockRAII() { Stream.ExitBlock(); }
483};
484} // namespace llvm
485
486#endif