Bug Summary

File:build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/mlir/lib/Bytecode/Reader/BytecodeReader.cpp
Warning:line 174, column 9
The left operand of '&' is a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name BytecodeReader.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-16/lib/clang/16.0.0 -D MLIR_CUDA_CONVERSIONS_ENABLED=1 -D MLIR_ROCM_CONVERSIONS_ENABLED=1 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/mlir/lib/Bytecode/Reader -I /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/mlir/lib/Bytecode/Reader -I include -I /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/llvm/include -I /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/mlir/include -I tools/mlir/include -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-16/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/= -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/= -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-10-03-140002-15933-1 -x c++ /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/mlir/lib/Bytecode/Reader/BytecodeReader.cpp
1//===- BytecodeReader.cpp - MLIR Bytecode Reader --------------------------===//
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// TODO: Support for big-endian architectures.
10// TODO: Properly preserve use lists of values.
11
12#include "mlir/Bytecode/BytecodeReader.h"
13#include "../Encoding.h"
14#include "mlir/AsmParser/AsmParser.h"
15#include "mlir/Bytecode/BytecodeImplementation.h"
16#include "mlir/IR/BuiltinDialect.h"
17#include "mlir/IR/BuiltinOps.h"
18#include "mlir/IR/OpImplementation.h"
19#include "mlir/IR/Verifier.h"
20#include "llvm/ADT/MapVector.h"
21#include "llvm/ADT/ScopeExit.h"
22#include "llvm/ADT/SmallString.h"
23#include "llvm/ADT/StringExtras.h"
24#include "llvm/Support/MemoryBufferRef.h"
25#include "llvm/Support/SaveAndRestore.h"
26
27#define DEBUG_TYPE"mlir-bytecode-reader" "mlir-bytecode-reader"
28
29using namespace mlir;
30
31/// Stringify the given section ID.
32static std::string toString(bytecode::Section::ID sectionID) {
33 switch (sectionID) {
34 case bytecode::Section::kString:
35 return "String (0)";
36 case bytecode::Section::kDialect:
37 return "Dialect (1)";
38 case bytecode::Section::kAttrType:
39 return "AttrType (2)";
40 case bytecode::Section::kAttrTypeOffset:
41 return "AttrTypeOffset (3)";
42 case bytecode::Section::kIR:
43 return "IR (4)";
44 case bytecode::Section::kResource:
45 return "Resource (5)";
46 case bytecode::Section::kResourceOffset:
47 return "ResourceOffset (6)";
48 default:
49 return ("Unknown (" + Twine(static_cast<unsigned>(sectionID)) + ")").str();
50 }
51}
52
53/// Returns true if the given top-level section ID is optional.
54static bool isSectionOptional(bytecode::Section::ID sectionID) {
55 switch (sectionID) {
56 case bytecode::Section::kString:
57 case bytecode::Section::kDialect:
58 case bytecode::Section::kAttrType:
59 case bytecode::Section::kAttrTypeOffset:
60 case bytecode::Section::kIR:
61 return false;
62 case bytecode::Section::kResource:
63 case bytecode::Section::kResourceOffset:
64 return true;
65 default:
66 llvm_unreachable("unknown section ID")::llvm::llvm_unreachable_internal("unknown section ID", "mlir/lib/Bytecode/Reader/BytecodeReader.cpp"
, 66)
;
67 }
68}
69
70//===----------------------------------------------------------------------===//
71// EncodingReader
72//===----------------------------------------------------------------------===//
73
74namespace {
75class EncodingReader {
76public:
77 explicit EncodingReader(ArrayRef<uint8_t> contents, Location fileLoc)
78 : dataIt(contents.data()), dataEnd(contents.end()), fileLoc(fileLoc) {}
79 explicit EncodingReader(StringRef contents, Location fileLoc)
80 : EncodingReader({reinterpret_cast<const uint8_t *>(contents.data()),
81 contents.size()},
82 fileLoc) {}
83
84 /// Returns true if the entire section has been read.
85 bool empty() const { return dataIt == dataEnd; }
86
87 /// Returns the remaining size of the bytecode.
88 size_t size() const { return dataEnd - dataIt; }
89
90 /// Align the current reader position to the specified alignment.
91 LogicalResult alignTo(unsigned alignment) {
92 if (!llvm::isPowerOf2_32(alignment))
93 return emitError("expected alignment to be a power-of-two");
94
95 // Shift the reader position to the next alignment boundary.
96 while (uintptr_t(dataIt) & (uintptr_t(alignment) - 1)) {
97 uint8_t padding;
98 if (failed(parseByte(padding)))
99 return failure();
100 if (padding != bytecode::kAlignmentByte) {
101 return emitError("expected alignment byte (0xCB), but got: '0x" +
102 llvm::utohexstr(padding) + "'");
103 }
104 }
105
106 // TODO: Check that the current data pointer is actually at the expected
107 // alignment.
108
109 return success();
110 }
111
112 /// Emit an error using the given arguments.
113 template <typename... Args>
114 InFlightDiagnostic emitError(Args &&...args) const {
115 return ::emitError(fileLoc).append(std::forward<Args>(args)...);
116 }
117 InFlightDiagnostic emitError() const { return ::emitError(fileLoc); }
118
119 /// Parse a single byte from the stream.
120 template <typename T>
121 LogicalResult parseByte(T &value) {
122 if (empty())
5
Taking true branch
123 return emitError("attempting to parse a byte at the end of the bytecode");
6
Returning without writing to 'value'
124 value = static_cast<T>(*dataIt++);
125 return success();
126 }
127 /// Parse a range of bytes of 'length' into the given result.
128 LogicalResult parseBytes(size_t length, ArrayRef<uint8_t> &result) {
129 if (length > size()) {
130 return emitError("attempting to parse ", length, " bytes when only ",
131 size(), " remain");
132 }
133 result = {dataIt, length};
134 dataIt += length;
135 return success();
136 }
137 /// Parse a range of bytes of 'length' into the given result, which can be
138 /// assumed to be large enough to hold `length`.
139 LogicalResult parseBytes(size_t length, uint8_t *result) {
140 if (length > size()) {
141 return emitError("attempting to parse ", length, " bytes when only ",
142 size(), " remain");
143 }
144 memcpy(result, dataIt, length);
145 dataIt += length;
146 return success();
147 }
148
149 /// Parse an aligned blob of data, where the alignment was encoded alongside
150 /// the data.
151 LogicalResult parseBlobAndAlignment(ArrayRef<uint8_t> &data,
152 uint64_t &alignment) {
153 uint64_t dataSize;
154 if (failed(parseVarInt(alignment)) || failed(parseVarInt(dataSize)) ||
155 failed(alignTo(alignment)))
156 return failure();
157 return parseBytes(dataSize, data);
158 }
159
160 /// Parse a variable length encoded integer from the byte stream. The first
161 /// encoded byte contains a prefix in the low bits indicating the encoded
162 /// length of the value. This length prefix is a bit sequence of '0's followed
163 /// by a '1'. The number of '0' bits indicate the number of _additional_ bytes
164 /// (not including the prefix byte). All remaining bits in the first byte,
165 /// along with all of the bits in additional bytes, provide the value of the
166 /// integer encoded in little-endian order.
167 LogicalResult parseVarInt(uint64_t &result) {
168 // Parse the first byte of the encoding, which contains the length prefix.
169 if (failed(parseByte(result)))
4
Calling 'EncodingReader::parseByte'
7
Returning from 'EncodingReader::parseByte'
8
Taking false branch
170 return failure();
171
172 // Handle the overwhelmingly common case where the value is stored in a
173 // single byte. In this case, the first bit is the `1` marker bit.
174 if (LLVM_LIKELY(result & 1)__builtin_expect((bool)(result & 1), true)) {
9
The left operand of '&' is a garbage value
175 result >>= 1;
176 return success();
177 }
178
179 // Handle the overwhelming uncommon case where the value required all 8
180 // bytes (i.e. a really really big number). In this case, the marker byte is
181 // all zeros: `00000000`.
182 if (LLVM_UNLIKELY(result == 0)__builtin_expect((bool)(result == 0), false))
183 return parseBytes(sizeof(result), reinterpret_cast<uint8_t *>(&result));
184 return parseMultiByteVarInt(result);
185 }
186
187 /// Parse a signed variable length encoded integer from the byte stream. A
188 /// signed varint is encoded as a normal varint with zigzag encoding applied,
189 /// i.e. the low bit of the value is used to indicate the sign.
190 LogicalResult parseSignedVarInt(uint64_t &result) {
191 if (failed(parseVarInt(result)))
192 return failure();
193 // Essentially (but using unsigned): (x >> 1) ^ -(x & 1)
194 result = (result >> 1) ^ (~(result & 1) + 1);
195 return success();
196 }
197
198 /// Parse a variable length encoded integer whose low bit is used to encode an
199 /// unrelated flag, i.e: `(integerValue << 1) | (flag ? 1 : 0)`.
200 LogicalResult parseVarIntWithFlag(uint64_t &result, bool &flag) {
201 if (failed(parseVarInt(result)))
202 return failure();
203 flag = result & 1;
204 result >>= 1;
205 return success();
206 }
207
208 /// Skip the first `length` bytes within the reader.
209 LogicalResult skipBytes(size_t length) {
210 if (length > size()) {
211 return emitError("attempting to skip ", length, " bytes when only ",
212 size(), " remain");
213 }
214 dataIt += length;
215 return success();
216 }
217
218 /// Parse a null-terminated string into `result` (without including the NUL
219 /// terminator).
220 LogicalResult parseNullTerminatedString(StringRef &result) {
221 const char *startIt = (const char *)dataIt;
222 const char *nulIt = (const char *)memchr(startIt, 0, size());
223 if (!nulIt)
224 return emitError(
225 "malformed null-terminated string, no null character found");
226
227 result = StringRef(startIt, nulIt - startIt);
228 dataIt = (const uint8_t *)nulIt + 1;
229 return success();
230 }
231
232 /// Parse a section header, placing the kind of section in `sectionID` and the
233 /// contents of the section in `sectionData`.
234 LogicalResult parseSection(bytecode::Section::ID &sectionID,
235 ArrayRef<uint8_t> &sectionData) {
236 uint8_t sectionIDAndHasAlignment;
237 uint64_t length;
238 if (failed(parseByte(sectionIDAndHasAlignment)) ||
239 failed(parseVarInt(length)))
240 return failure();
241
242 // Extract the section ID and whether the section is aligned. The high bit
243 // of the ID is the alignment flag.
244 sectionID = static_cast<bytecode::Section::ID>(sectionIDAndHasAlignment &
245 0b01111111);
246 bool hasAlignment = sectionIDAndHasAlignment & 0b10000000;
247
248 // Check that the section is actually valid before trying to process its
249 // data.
250 if (sectionID >= bytecode::Section::kNumSections)
251 return emitError("invalid section ID: ", unsigned(sectionID));
252
253 // Process the section alignment if present.
254 if (hasAlignment) {
255 uint64_t alignment;
256 if (failed(parseVarInt(alignment)) || failed(alignTo(alignment)))
257 return failure();
258 }
259
260 // Parse the actual section data.
261 return parseBytes(static_cast<size_t>(length), sectionData);
262 }
263
264private:
265 /// Parse a variable length encoded integer from the byte stream. This method
266 /// is a fallback when the number of bytes used to encode the value is greater
267 /// than 1, but less than the max (9). The provided `result` value can be
268 /// assumed to already contain the first byte of the value.
269 /// NOTE: This method is marked noinline to avoid pessimizing the common case
270 /// of single byte encoding.
271 LLVM_ATTRIBUTE_NOINLINE__attribute__((noinline)) LogicalResult parseMultiByteVarInt(uint64_t &result) {
272 // Count the number of trailing zeros in the marker byte, this indicates the
273 // number of trailing bytes that are part of the value. We use `uint32_t`
274 // here because we only care about the first byte, and so that be actually
275 // get ctz intrinsic calls when possible (the `uint8_t` overload uses a loop
276 // implementation).
277 uint32_t numBytes =
278 llvm::countTrailingZeros<uint32_t>(result, llvm::ZB_Undefined);
279 assert(numBytes > 0 && numBytes <= 7 &&(static_cast <bool> (numBytes > 0 && numBytes
<= 7 && "unexpected number of trailing zeros in varint encoding"
) ? void (0) : __assert_fail ("numBytes > 0 && numBytes <= 7 && \"unexpected number of trailing zeros in varint encoding\""
, "mlir/lib/Bytecode/Reader/BytecodeReader.cpp", 280, __extension__
__PRETTY_FUNCTION__))
280 "unexpected number of trailing zeros in varint encoding")(static_cast <bool> (numBytes > 0 && numBytes
<= 7 && "unexpected number of trailing zeros in varint encoding"
) ? void (0) : __assert_fail ("numBytes > 0 && numBytes <= 7 && \"unexpected number of trailing zeros in varint encoding\""
, "mlir/lib/Bytecode/Reader/BytecodeReader.cpp", 280, __extension__
__PRETTY_FUNCTION__))
;
281
282 // Parse in the remaining bytes of the value.
283 if (failed(parseBytes(numBytes, reinterpret_cast<uint8_t *>(&result) + 1)))
284 return failure();
285
286 // Shift out the low-order bits that were used to mark how the value was
287 // encoded.
288 result >>= (numBytes + 1);
289 return success();
290 }
291
292 /// The current data iterator, and an iterator to the end of the buffer.
293 const uint8_t *dataIt, *dataEnd;
294
295 /// A location for the bytecode used to report errors.
296 Location fileLoc;
297};
298} // namespace
299
300/// Resolve an index into the given entry list. `entry` may either be a
301/// reference, in which case it is assigned to the corresponding value in
302/// `entries`, or a pointer, in which case it is assigned to the address of the
303/// element in `entries`.
304template <typename RangeT, typename T>
305static LogicalResult resolveEntry(EncodingReader &reader, RangeT &entries,
306 uint64_t index, T &entry,
307 StringRef entryStr) {
308 if (index >= entries.size())
309 return reader.emitError("invalid ", entryStr, " index: ", index);
310
311 // If the provided entry is a pointer, resolve to the address of the entry.
312 if constexpr (std::is_convertible_v<llvm::detail::ValueOfRange<RangeT>, T>)
313 entry = entries[index];
314 else
315 entry = &entries[index];
316 return success();
317}
318
319/// Parse and resolve an index into the given entry list.
320template <typename RangeT, typename T>
321static LogicalResult parseEntry(EncodingReader &reader, RangeT &entries,
322 T &entry, StringRef entryStr) {
323 uint64_t entryIdx;
324 if (failed(reader.parseVarInt(entryIdx)))
325 return failure();
326 return resolveEntry(reader, entries, entryIdx, entry, entryStr);
327}
328
329//===----------------------------------------------------------------------===//
330// StringSectionReader
331//===----------------------------------------------------------------------===//
332
333namespace {
334/// This class is used to read references to the string section from the
335/// bytecode.
336class StringSectionReader {
337public:
338 /// Initialize the string section reader with the given section data.
339 LogicalResult initialize(Location fileLoc, ArrayRef<uint8_t> sectionData);
340
341 /// Parse a shared string from the string section. The shared string is
342 /// encoded using an index to a corresponding string in the string section.
343 LogicalResult parseString(EncodingReader &reader, StringRef &result) {
344 return parseEntry(reader, strings, result, "string");
345 }
346
347private:
348 /// The table of strings referenced within the bytecode file.
349 SmallVector<StringRef> strings;
350};
351} // namespace
352
353LogicalResult StringSectionReader::initialize(Location fileLoc,
354 ArrayRef<uint8_t> sectionData) {
355 EncodingReader stringReader(sectionData, fileLoc);
356
357 // Parse the number of strings in the section.
358 uint64_t numStrings;
359 if (failed(stringReader.parseVarInt(numStrings)))
360 return failure();
361 strings.resize(numStrings);
362
363 // Parse each of the strings. The sizes of the strings are encoded in reverse
364 // order, so that's the order we populate the table.
365 size_t stringDataEndOffset = sectionData.size();
366 for (StringRef &string : llvm::reverse(strings)) {
367 uint64_t stringSize;
368 if (failed(stringReader.parseVarInt(stringSize)))
369 return failure();
370 if (stringDataEndOffset < stringSize) {
371 return stringReader.emitError(
372 "string size exceeds the available data size");
373 }
374
375 // Extract the string from the data, dropping the null character.
376 size_t stringOffset = stringDataEndOffset - stringSize;
377 string = StringRef(
378 reinterpret_cast<const char *>(sectionData.data() + stringOffset),
379 stringSize - 1);
380 stringDataEndOffset = stringOffset;
381 }
382
383 // Check that the only remaining data was for the strings, i.e. the reader
384 // should be at the same offset as the first string.
385 if ((sectionData.size() - stringReader.size()) != stringDataEndOffset) {
386 return stringReader.emitError("unexpected trailing data between the "
387 "offsets for strings and their data");
388 }
389 return success();
390}
391
392//===----------------------------------------------------------------------===//
393// BytecodeDialect
394//===----------------------------------------------------------------------===//
395
396namespace {
397/// This struct represents a dialect entry within the bytecode.
398struct BytecodeDialect {
399 /// Load the dialect into the provided context if it hasn't been loaded yet.
400 /// Returns failure if the dialect couldn't be loaded *and* the provided
401 /// context does not allow unregistered dialects. The provided reader is used
402 /// for error emission if necessary.
403 LogicalResult load(EncodingReader &reader, MLIRContext *ctx) {
404 if (dialect)
405 return success();
406 Dialect *loadedDialect = ctx->getOrLoadDialect(name);
407 if (!loadedDialect && !ctx->allowsUnregisteredDialects()) {
408 return reader.emitError(
409 "dialect '", name,
410 "' is unknown. If this is intended, please call "
411 "allowUnregisteredDialects() on the MLIRContext, or use "
412 "-allow-unregistered-dialect with the MLIR tool used.");
413 }
414 dialect = loadedDialect;
415
416 // If the dialect was actually loaded, check to see if it has a bytecode
417 // interface.
418 if (loadedDialect)
419 interface = dyn_cast<BytecodeDialectInterface>(loadedDialect);
420 return success();
421 }
422
423 /// Return the loaded dialect, or nullptr if the dialect is unknown. This can
424 /// only be called after `load`.
425 Dialect *getLoadedDialect() const {
426 assert(dialect &&(static_cast <bool> (dialect && "expected `load` to be invoked before `getLoadedDialect`"
) ? void (0) : __assert_fail ("dialect && \"expected `load` to be invoked before `getLoadedDialect`\""
, "mlir/lib/Bytecode/Reader/BytecodeReader.cpp", 427, __extension__
__PRETTY_FUNCTION__))
427 "expected `load` to be invoked before `getLoadedDialect`")(static_cast <bool> (dialect && "expected `load` to be invoked before `getLoadedDialect`"
) ? void (0) : __assert_fail ("dialect && \"expected `load` to be invoked before `getLoadedDialect`\""
, "mlir/lib/Bytecode/Reader/BytecodeReader.cpp", 427, __extension__
__PRETTY_FUNCTION__))
;
428 return *dialect;
429 }
430
431 /// The loaded dialect entry. This field is None if we haven't attempted to
432 /// load, nullptr if we failed to load, otherwise the loaded dialect.
433 Optional<Dialect *> dialect;
434
435 /// The bytecode interface of the dialect, or nullptr if the dialect does not
436 /// implement the bytecode interface. This field should only be checked if the
437 /// `dialect` field is non-None.
438 const BytecodeDialectInterface *interface = nullptr;
439
440 /// The name of the dialect.
441 StringRef name;
442};
443
444/// This struct represents an operation name entry within the bytecode.
445struct BytecodeOperationName {
446 BytecodeOperationName(BytecodeDialect *dialect, StringRef name)
447 : dialect(dialect), name(name) {}
448
449 /// The loaded operation name, or None if it hasn't been processed yet.
450 Optional<OperationName> opName;
451
452 /// The dialect that owns this operation name.
453 BytecodeDialect *dialect;
454
455 /// The name of the operation, without the dialect prefix.
456 StringRef name;
457};
458} // namespace
459
460/// Parse a single dialect group encoded in the byte stream.
461static LogicalResult parseDialectGrouping(
462 EncodingReader &reader, MutableArrayRef<BytecodeDialect> dialects,
463 function_ref<LogicalResult(BytecodeDialect *)> entryCallback) {
464 // Parse the dialect and the number of entries in the group.
465 BytecodeDialect *dialect;
466 if (failed(parseEntry(reader, dialects, dialect, "dialect")))
467 return failure();
468 uint64_t numEntries;
469 if (failed(reader.parseVarInt(numEntries)))
470 return failure();
471
472 for (uint64_t i = 0; i < numEntries; ++i)
473 if (failed(entryCallback(dialect)))
474 return failure();
475 return success();
476}
477
478//===----------------------------------------------------------------------===//
479// ResourceSectionReader
480//===----------------------------------------------------------------------===//
481
482namespace {
483/// This class is used to read the resource section from the bytecode.
484class ResourceSectionReader {
485public:
486 /// Initialize the resource section reader with the given section data.
487 LogicalResult initialize(Location fileLoc, const ParserConfig &config,
488 MutableArrayRef<BytecodeDialect> dialects,
489 StringSectionReader &stringReader,
490 ArrayRef<uint8_t> sectionData,
491 ArrayRef<uint8_t> offsetSectionData);
492
493 /// Parse a dialect resource handle from the resource section.
494 LogicalResult parseResourceHandle(EncodingReader &reader,
495 AsmDialectResourceHandle &result) {
496 return parseEntry(reader, dialectResources, result, "resource handle");
497 }
498
499private:
500 /// The table of dialect resources within the bytecode file.
501 SmallVector<AsmDialectResourceHandle> dialectResources;
502};
503
504class ParsedResourceEntry : public AsmParsedResourceEntry {
505public:
506 ParsedResourceEntry(StringRef key, AsmResourceEntryKind kind,
507 EncodingReader &reader, StringSectionReader &stringReader)
508 : key(key), kind(kind), reader(reader), stringReader(stringReader) {}
509 ~ParsedResourceEntry() override = default;
510
511 StringRef getKey() const final { return key; }
512
513 InFlightDiagnostic emitError() const final { return reader.emitError(); }
514
515 AsmResourceEntryKind getKind() const final { return kind; }
516
517 FailureOr<bool> parseAsBool() const final {
518 if (kind != AsmResourceEntryKind::Bool)
519 return emitError() << "expected a bool resource entry, but found a "
520 << toString(kind) << " entry instead";
521
522 bool value;
523 if (failed(reader.parseByte(value)))
524 return failure();
525 return value;
526 }
527 FailureOr<std::string> parseAsString() const final {
528 if (kind != AsmResourceEntryKind::String)
529 return emitError() << "expected a string resource entry, but found a "
530 << toString(kind) << " entry instead";
531
532 StringRef string;
533 if (failed(stringReader.parseString(reader, string)))
534 return failure();
535 return string.str();
536 }
537
538 FailureOr<AsmResourceBlob>
539 parseAsBlob(BlobAllocatorFn allocator) const final {
540 if (kind != AsmResourceEntryKind::Blob)
541 return emitError() << "expected a blob resource entry, but found a "
542 << toString(kind) << " entry instead";
543
544 ArrayRef<uint8_t> data;
545 uint64_t alignment;
546 if (failed(reader.parseBlobAndAlignment(data, alignment)))
547 return failure();
548
549 // Allocate memory for the blob using the provided allocator and copy the
550 // data into it.
551 // FIXME: If the current holder of the bytecode can ensure its lifetime
552 // (e.g. when mmap'd), we should not copy the data. We should use the data
553 // from the bytecode directly.
554 AsmResourceBlob blob = allocator(data.size(), alignment);
555 assert(llvm::isAddrAligned(llvm::Align(alignment), blob.getData().data()) &&(static_cast <bool> (llvm::isAddrAligned(llvm::Align(alignment
), blob.getData().data()) && blob.isMutable() &&
"blob allocator did not return a properly aligned address") ?
void (0) : __assert_fail ("llvm::isAddrAligned(llvm::Align(alignment), blob.getData().data()) && blob.isMutable() && \"blob allocator did not return a properly aligned address\""
, "mlir/lib/Bytecode/Reader/BytecodeReader.cpp", 557, __extension__
__PRETTY_FUNCTION__))
556 blob.isMutable() &&(static_cast <bool> (llvm::isAddrAligned(llvm::Align(alignment
), blob.getData().data()) && blob.isMutable() &&
"blob allocator did not return a properly aligned address") ?
void (0) : __assert_fail ("llvm::isAddrAligned(llvm::Align(alignment), blob.getData().data()) && blob.isMutable() && \"blob allocator did not return a properly aligned address\""
, "mlir/lib/Bytecode/Reader/BytecodeReader.cpp", 557, __extension__
__PRETTY_FUNCTION__))
557 "blob allocator did not return a properly aligned address")(static_cast <bool> (llvm::isAddrAligned(llvm::Align(alignment
), blob.getData().data()) && blob.isMutable() &&
"blob allocator did not return a properly aligned address") ?
void (0) : __assert_fail ("llvm::isAddrAligned(llvm::Align(alignment), blob.getData().data()) && blob.isMutable() && \"blob allocator did not return a properly aligned address\""
, "mlir/lib/Bytecode/Reader/BytecodeReader.cpp", 557, __extension__
__PRETTY_FUNCTION__))
;
558 memcpy(blob.getMutableData().data(), data.data(), data.size());
559 return blob;
560 }
561
562private:
563 StringRef key;
564 AsmResourceEntryKind kind;
565 EncodingReader &reader;
566 StringSectionReader &stringReader;
567};
568} // namespace
569
570template <typename T>
571static LogicalResult
572parseResourceGroup(Location fileLoc, bool allowEmpty,
573 EncodingReader &offsetReader, EncodingReader &resourceReader,
574 StringSectionReader &stringReader, T *handler,
575 function_ref<LogicalResult(StringRef)> processKeyFn = {}) {
576 uint64_t numResources;
577 if (failed(offsetReader.parseVarInt(numResources)))
578 return failure();
579
580 for (uint64_t i = 0; i < numResources; ++i) {
581 StringRef key;
582 AsmResourceEntryKind kind;
583 uint64_t resourceOffset;
584 ArrayRef<uint8_t> data;
585 if (failed(stringReader.parseString(offsetReader, key)) ||
586 failed(offsetReader.parseVarInt(resourceOffset)) ||
587 failed(offsetReader.parseByte(kind)) ||
588 failed(resourceReader.parseBytes(resourceOffset, data)))
589 return failure();
590
591 // Process the resource key.
592 if ((processKeyFn && failed(processKeyFn(key))))
593 return failure();
594
595 // If the resource data is empty and we allow it, don't error out when
596 // parsing below, just skip it.
597 if (allowEmpty && data.empty())
598 continue;
599
600 // Ignore the entry if we don't have a valid handler.
601 if (!handler)
602 continue;
603
604 // Otherwise, parse the resource value.
605 EncodingReader entryReader(data, fileLoc);
606 ParsedResourceEntry entry(key, kind, entryReader, stringReader);
607 if (failed(handler->parseResource(entry)))
608 return failure();
609 if (!entryReader.empty()) {
610 return entryReader.emitError(
611 "unexpected trailing bytes in resource entry '", key, "'");
612 }
613 }
614 return success();
615}
616
617LogicalResult
618ResourceSectionReader::initialize(Location fileLoc, const ParserConfig &config,
619 MutableArrayRef<BytecodeDialect> dialects,
620 StringSectionReader &stringReader,
621 ArrayRef<uint8_t> sectionData,
622 ArrayRef<uint8_t> offsetSectionData) {
623 EncodingReader resourceReader(sectionData, fileLoc);
624 EncodingReader offsetReader(offsetSectionData, fileLoc);
625
626 // Read the number of external resource providers.
627 uint64_t numExternalResourceGroups;
628 if (failed(offsetReader.parseVarInt(numExternalResourceGroups)))
629 return failure();
630
631 // Utility functor that dispatches to `parseResourceGroup`, but implicitly
632 // provides most of the arguments.
633 auto parseGroup = [&](auto *handler, bool allowEmpty = false,
634 function_ref<LogicalResult(StringRef)> keyFn = {}) {
635 return parseResourceGroup(fileLoc, allowEmpty, offsetReader, resourceReader,
636 stringReader, handler, keyFn);
637 };
638
639 // Read the external resources from the bytecode.
640 for (uint64_t i = 0; i < numExternalResourceGroups; ++i) {
641 StringRef key;
642 if (failed(stringReader.parseString(offsetReader, key)))
643 return failure();
644
645 // Get the handler for these resources.
646 // TODO: Should we require handling external resources in some scenarios?
647 AsmResourceParser *handler = config.getResourceParser(key);
648 if (!handler) {
649 emitWarning(fileLoc) << "ignoring unknown external resources for '" << key
650 << "'";
651 }
652
653 if (failed(parseGroup(handler)))
654 return failure();
655 }
656
657 // Read the dialect resources from the bytecode.
658 MLIRContext *ctx = fileLoc->getContext();
659 while (!offsetReader.empty()) {
660 BytecodeDialect *dialect;
661 if (failed(parseEntry(offsetReader, dialects, dialect, "dialect")) ||
662 failed(dialect->load(resourceReader, ctx)))
663 return failure();
664 Dialect *loadedDialect = dialect->getLoadedDialect();
665 if (!loadedDialect) {
666 return resourceReader.emitError()
667 << "dialect '" << dialect->name << "' is unknown";
668 }
669 const auto *handler = dyn_cast<OpAsmDialectInterface>(loadedDialect);
670 if (!handler) {
671 return resourceReader.emitError()
672 << "unexpected resources for dialect '" << dialect->name << "'";
673 }
674
675 // Ensure that each resource is declared before being processed.
676 auto processResourceKeyFn = [&](StringRef key) -> LogicalResult {
677 FailureOr<AsmDialectResourceHandle> handle =
678 handler->declareResource(key);
679 if (failed(handle)) {
680 return resourceReader.emitError()
681 << "unknown 'resource' key '" << key << "' for dialect '"
682 << dialect->name << "'";
683 }
684 dialectResources.push_back(*handle);
685 return success();
686 };
687
688 // Parse the resources for this dialect. We allow empty resources because we
689 // just treat these as declarations.
690 if (failed(parseGroup(handler, /*allowEmpty=*/true, processResourceKeyFn)))
691 return failure();
692 }
693
694 return success();
695}
696
697//===----------------------------------------------------------------------===//
698// Attribute/Type Reader
699//===----------------------------------------------------------------------===//
700
701namespace {
702/// This class provides support for reading attribute and type entries from the
703/// bytecode. Attribute and Type entries are read lazily on demand, so we use
704/// this reader to manage when to actually parse them from the bytecode.
705class AttrTypeReader {
706 /// This class represents a single attribute or type entry.
707 template <typename T>
708 struct Entry {
709 /// The entry, or null if it hasn't been resolved yet.
710 T entry = {};
711 /// The parent dialect of this entry.
712 BytecodeDialect *dialect = nullptr;
713 /// A flag indicating if the entry was encoded using a custom encoding,
714 /// instead of using the textual assembly format.
715 bool hasCustomEncoding = false;
716 /// The raw data of this entry in the bytecode.
717 ArrayRef<uint8_t> data;
718 };
719 using AttrEntry = Entry<Attribute>;
720 using TypeEntry = Entry<Type>;
721
722public:
723 AttrTypeReader(StringSectionReader &stringReader,
724 ResourceSectionReader &resourceReader, Location fileLoc)
725 : stringReader(stringReader), resourceReader(resourceReader),
726 fileLoc(fileLoc) {}
727
728 /// Initialize the attribute and type information within the reader.
729 LogicalResult initialize(MutableArrayRef<BytecodeDialect> dialects,
730 ArrayRef<uint8_t> sectionData,
731 ArrayRef<uint8_t> offsetSectionData);
732
733 /// Resolve the attribute or type at the given index. Returns nullptr on
734 /// failure.
735 Attribute resolveAttribute(size_t index) {
736 return resolveEntry(attributes, index, "Attribute");
737 }
738 Type resolveType(size_t index) { return resolveEntry(types, index, "Type"); }
739
740 /// Parse a reference to an attribute or type using the given reader.
741 LogicalResult parseAttribute(EncodingReader &reader, Attribute &result) {
742 uint64_t attrIdx;
743 if (failed(reader.parseVarInt(attrIdx)))
744 return failure();
745 result = resolveAttribute(attrIdx);
746 return success(!!result);
747 }
748 LogicalResult parseType(EncodingReader &reader, Type &result) {
749 uint64_t typeIdx;
750 if (failed(reader.parseVarInt(typeIdx)))
751 return failure();
752 result = resolveType(typeIdx);
753 return success(!!result);
754 }
755
756 template <typename T>
757 LogicalResult parseAttribute(EncodingReader &reader, T &result) {
758 Attribute baseResult;
759 if (failed(parseAttribute(reader, baseResult)))
760 return failure();
761 if ((result = baseResult.dyn_cast<T>()))
762 return success();
763 return reader.emitError("expected attribute of type: ",
764 llvm::getTypeName<T>(), ", but got: ", baseResult);
765 }
766
767private:
768 /// Resolve the given entry at `index`.
769 template <typename T>
770 T resolveEntry(SmallVectorImpl<Entry<T>> &entries, size_t index,
771 StringRef entryType);
772
773 /// Parse an entry using the given reader that was encoded using the textual
774 /// assembly format.
775 template <typename T>
776 LogicalResult parseAsmEntry(T &result, EncodingReader &reader,
777 StringRef entryType);
778
779 /// Parse an entry using the given reader that was encoded using a custom
780 /// bytecode format.
781 template <typename T>
782 LogicalResult parseCustomEntry(Entry<T> &entry, EncodingReader &reader,
783 StringRef entryType);
784
785 /// The string section reader used to resolve string references when parsing
786 /// custom encoded attribute/type entries.
787 StringSectionReader &stringReader;
788
789 /// The resource section reader used to resolve resource references when
790 /// parsing custom encoded attribute/type entries.
791 ResourceSectionReader &resourceReader;
792
793 /// The set of attribute and type entries.
794 SmallVector<AttrEntry> attributes;
795 SmallVector<TypeEntry> types;
796
797 /// A location used for error emission.
798 Location fileLoc;
799};
800
801class DialectReader : public DialectBytecodeReader {
802public:
803 DialectReader(AttrTypeReader &attrTypeReader,
804 StringSectionReader &stringReader,
805 ResourceSectionReader &resourceReader, EncodingReader &reader)
806 : attrTypeReader(attrTypeReader), stringReader(stringReader),
807 resourceReader(resourceReader), reader(reader) {}
808
809 InFlightDiagnostic emitError(const Twine &msg) override {
810 return reader.emitError(msg);
811 }
812
813 //===--------------------------------------------------------------------===//
814 // IR
815 //===--------------------------------------------------------------------===//
816
817 LogicalResult readAttribute(Attribute &result) override {
818 return attrTypeReader.parseAttribute(reader, result);
819 }
820
821 LogicalResult readType(Type &result) override {
822 return attrTypeReader.parseType(reader, result);
823 }
824
825 FailureOr<AsmDialectResourceHandle> readResourceHandle() override {
826 AsmDialectResourceHandle handle;
827 if (failed(resourceReader.parseResourceHandle(reader, handle)))
828 return failure();
829 return handle;
830 }
831
832 //===--------------------------------------------------------------------===//
833 // Primitives
834 //===--------------------------------------------------------------------===//
835
836 LogicalResult readVarInt(uint64_t &result) override {
837 return reader.parseVarInt(result);
838 }
839
840 LogicalResult readSignedVarInt(int64_t &result) override {
841 uint64_t unsignedResult;
842 if (failed(reader.parseSignedVarInt(unsignedResult)))
843 return failure();
844 result = static_cast<int64_t>(unsignedResult);
845 return success();
846 }
847
848 FailureOr<APInt> readAPIntWithKnownWidth(unsigned bitWidth) override {
849 // Small values are encoded using a single byte.
850 if (bitWidth <= 8) {
851 uint8_t value;
852 if (failed(reader.parseByte(value)))
853 return failure();
854 return APInt(bitWidth, value);
855 }
856
857 // Large values up to 64 bits are encoded using a single varint.
858 if (bitWidth <= 64) {
859 uint64_t value;
860 if (failed(reader.parseSignedVarInt(value)))
861 return failure();
862 return APInt(bitWidth, value);
863 }
864
865 // Otherwise, for really big values we encode the array of active words in
866 // the value.
867 uint64_t numActiveWords;
868 if (failed(reader.parseVarInt(numActiveWords)))
869 return failure();
870 SmallVector<uint64_t, 4> words(numActiveWords);
871 for (uint64_t i = 0; i < numActiveWords; ++i)
872 if (failed(reader.parseSignedVarInt(words[i])))
873 return failure();
874 return APInt(bitWidth, words);
875 }
876
877 FailureOr<APFloat>
878 readAPFloatWithKnownSemantics(const llvm::fltSemantics &semantics) override {
879 FailureOr<APInt> intVal =
880 readAPIntWithKnownWidth(APFloat::getSizeInBits(semantics));
881 if (failed(intVal))
882 return failure();
883 return APFloat(semantics, *intVal);
884 }
885
886 LogicalResult readString(StringRef &result) override {
887 return stringReader.parseString(reader, result);
888 }
889
890 LogicalResult readBlob(ArrayRef<char> &result) override {
891 uint64_t dataSize;
1
'dataSize' declared without an initial value
892 ArrayRef<uint8_t> data;
893 if (failed(reader.parseVarInt(dataSize)) ||
2
Passing value via 1st parameter 'result'
3
Calling 'EncodingReader::parseVarInt'
894 failed(reader.parseBytes(dataSize, data)))
895 return failure();
896 result = llvm::makeArrayRef(reinterpret_cast<const char *>(data.data()),
897 data.size());
898 return success();
899 }
900
901private:
902 AttrTypeReader &attrTypeReader;
903 StringSectionReader &stringReader;
904 ResourceSectionReader &resourceReader;
905 EncodingReader &reader;
906};
907} // namespace
908
909LogicalResult
910AttrTypeReader::initialize(MutableArrayRef<BytecodeDialect> dialects,
911 ArrayRef<uint8_t> sectionData,
912 ArrayRef<uint8_t> offsetSectionData) {
913 EncodingReader offsetReader(offsetSectionData, fileLoc);
914
915 // Parse the number of attribute and type entries.
916 uint64_t numAttributes, numTypes;
917 if (failed(offsetReader.parseVarInt(numAttributes)) ||
918 failed(offsetReader.parseVarInt(numTypes)))
919 return failure();
920 attributes.resize(numAttributes);
921 types.resize(numTypes);
922
923 // A functor used to accumulate the offsets for the entries in the given
924 // range.
925 uint64_t currentOffset = 0;
926 auto parseEntries = [&](auto &&range) {
927 size_t currentIndex = 0, endIndex = range.size();
928
929 // Parse an individual entry.
930 auto parseEntryFn = [&](BytecodeDialect *dialect) -> LogicalResult {
931 auto &entry = range[currentIndex++];
932
933 uint64_t entrySize;
934 if (failed(offsetReader.parseVarIntWithFlag(entrySize,
935 entry.hasCustomEncoding)))
936 return failure();
937
938 // Verify that the offset is actually valid.
939 if (currentOffset + entrySize > sectionData.size()) {
940 return offsetReader.emitError(
941 "Attribute or Type entry offset points past the end of section");
942 }
943
944 entry.data = sectionData.slice(currentOffset, entrySize);
945 entry.dialect = dialect;
946 currentOffset += entrySize;
947 return success();
948 };
949 while (currentIndex != endIndex)
950 if (failed(parseDialectGrouping(offsetReader, dialects, parseEntryFn)))
951 return failure();
952 return success();
953 };
954
955 // Process each of the attributes, and then the types.
956 if (failed(parseEntries(attributes)) || failed(parseEntries(types)))
957 return failure();
958
959 // Ensure that we read everything from the section.
960 if (!offsetReader.empty()) {
961 return offsetReader.emitError(
962 "unexpected trailing data in the Attribute/Type offset section");
963 }
964 return success();
965}
966
967template <typename T>
968T AttrTypeReader::resolveEntry(SmallVectorImpl<Entry<T>> &entries, size_t index,
969 StringRef entryType) {
970 if (index >= entries.size()) {
971 emitError(fileLoc) << "invalid " << entryType << " index: " << index;
972 return {};
973 }
974
975 // If the entry has already been resolved, there is nothing left to do.
976 Entry<T> &entry = entries[index];
977 if (entry.entry)
978 return entry.entry;
979
980 // Parse the entry.
981 EncodingReader reader(entry.data, fileLoc);
982
983 // Parse based on how the entry was encoded.
984 if (entry.hasCustomEncoding) {
985 if (failed(parseCustomEntry(entry, reader, entryType)))
986 return T();
987 } else if (failed(parseAsmEntry(entry.entry, reader, entryType))) {
988 return T();
989 }
990
991 if (!reader.empty()) {
992 reader.emitError("unexpected trailing bytes after " + entryType + " entry");
993 return T();
994 }
995 return entry.entry;
996}
997
998template <typename T>
999LogicalResult AttrTypeReader::parseAsmEntry(T &result, EncodingReader &reader,
1000 StringRef entryType) {
1001 StringRef asmStr;
1002 if (failed(reader.parseNullTerminatedString(asmStr)))
1003 return failure();
1004
1005 // Invoke the MLIR assembly parser to parse the entry text.
1006 size_t numRead = 0;
1007 MLIRContext *context = fileLoc->getContext();
1008 if constexpr (std::is_same_v<T, Type>)
1009 result = ::parseType(asmStr, context, numRead);
1010 else
1011 result = ::parseAttribute(asmStr, context, numRead);
1012 if (!result)
1013 return failure();
1014
1015 // Ensure there weren't dangling characters after the entry.
1016 if (numRead != asmStr.size()) {
1017 return reader.emitError("trailing characters found after ", entryType,
1018 " assembly format: ", asmStr.drop_front(numRead));
1019 }
1020 return success();
1021}
1022
1023template <typename T>
1024LogicalResult AttrTypeReader::parseCustomEntry(Entry<T> &entry,
1025 EncodingReader &reader,
1026 StringRef entryType) {
1027 if (failed(entry.dialect->load(reader, fileLoc.getContext())))
1028 return failure();
1029
1030 // Ensure that the dialect implements the bytecode interface.
1031 if (!entry.dialect->interface) {
1032 return reader.emitError("dialect '", entry.dialect->name,
1033 "' does not implement the bytecode interface");
1034 }
1035
1036 // Ask the dialect to parse the entry.
1037 DialectReader dialectReader(*this, stringReader, resourceReader, reader);
1038 if constexpr (std::is_same_v<T, Type>)
1039 entry.entry = entry.dialect->interface->readType(dialectReader);
1040 else
1041 entry.entry = entry.dialect->interface->readAttribute(dialectReader);
1042 return success(!!entry.entry);
1043}
1044
1045//===----------------------------------------------------------------------===//
1046// Bytecode Reader
1047//===----------------------------------------------------------------------===//
1048
1049namespace {
1050/// This class is used to read a bytecode buffer and translate it into MLIR.
1051class BytecodeReader {
1052public:
1053 BytecodeReader(Location fileLoc, const ParserConfig &config)
1054 : config(config), fileLoc(fileLoc),
1055 attrTypeReader(stringReader, resourceReader, fileLoc),
1056 // Use the builtin unrealized conversion cast operation to represent
1057 // forward references to values that aren't yet defined.
1058 forwardRefOpState(UnknownLoc::get(config.getContext()),
1059 "builtin.unrealized_conversion_cast", ValueRange(),
1060 NoneType::get(config.getContext())) {}
1061
1062 /// Read the bytecode defined within `buffer` into the given block.
1063 LogicalResult read(llvm::MemoryBufferRef buffer, Block *block);
1064
1065private:
1066 /// Return the context for this config.
1067 MLIRContext *getContext() const { return config.getContext(); }
1068
1069 /// Parse the bytecode version.
1070 LogicalResult parseVersion(EncodingReader &reader);
1071
1072 //===--------------------------------------------------------------------===//
1073 // Dialect Section
1074
1075 LogicalResult parseDialectSection(ArrayRef<uint8_t> sectionData);
1076
1077 /// Parse an operation name reference using the given reader.
1078 FailureOr<OperationName> parseOpName(EncodingReader &reader);
1079
1080 //===--------------------------------------------------------------------===//
1081 // Attribute/Type Section
1082
1083 /// Parse an attribute or type using the given reader.
1084 template <typename T>
1085 LogicalResult parseAttribute(EncodingReader &reader, T &result) {
1086 return attrTypeReader.parseAttribute(reader, result);
1087 }
1088 LogicalResult parseType(EncodingReader &reader, Type &result) {
1089 return attrTypeReader.parseType(reader, result);
1090 }
1091
1092 //===--------------------------------------------------------------------===//
1093 // Resource Section
1094
1095 LogicalResult
1096 parseResourceSection(Optional<ArrayRef<uint8_t>> resourceData,
1097 Optional<ArrayRef<uint8_t>> resourceOffsetData);
1098
1099 //===--------------------------------------------------------------------===//
1100 // IR Section
1101
1102 /// This struct represents the current read state of a range of regions. This
1103 /// struct is used to enable iterative parsing of regions.
1104 struct RegionReadState {
1105 RegionReadState(Operation *op, bool isIsolatedFromAbove)
1106 : RegionReadState(op->getRegions(), isIsolatedFromAbove) {}
1107 RegionReadState(MutableArrayRef<Region> regions, bool isIsolatedFromAbove)
1108 : curRegion(regions.begin()), endRegion(regions.end()),
1109 isIsolatedFromAbove(isIsolatedFromAbove) {}
1110
1111 /// The current regions being read.
1112 MutableArrayRef<Region>::iterator curRegion, endRegion;
1113
1114 /// The number of values defined immediately within this region.
1115 unsigned numValues = 0;
1116
1117 /// The current blocks of the region being read.
1118 SmallVector<Block *> curBlocks;
1119 Region::iterator curBlock = {};
1120
1121 /// The number of operations remaining to be read from the current block
1122 /// being read.
1123 uint64_t numOpsRemaining = 0;
1124
1125 /// A flag indicating if the regions being read are isolated from above.
1126 bool isIsolatedFromAbove = false;
1127 };
1128
1129 LogicalResult parseIRSection(ArrayRef<uint8_t> sectionData, Block *block);
1130 LogicalResult parseRegions(EncodingReader &reader,
1131 std::vector<RegionReadState> &regionStack,
1132 RegionReadState &readState);
1133 FailureOr<Operation *> parseOpWithoutRegions(EncodingReader &reader,
1134 RegionReadState &readState,
1135 bool &isIsolatedFromAbove);
1136
1137 LogicalResult parseRegion(EncodingReader &reader, RegionReadState &readState);
1138 LogicalResult parseBlock(EncodingReader &reader, RegionReadState &readState);
1139 LogicalResult parseBlockArguments(EncodingReader &reader, Block *block);
1140
1141 //===--------------------------------------------------------------------===//
1142 // Value Processing
1143
1144 /// Parse an operand reference using the given reader. Returns nullptr in the
1145 /// case of failure.
1146 Value parseOperand(EncodingReader &reader);
1147
1148 /// Sequentially define the given value range.
1149 LogicalResult defineValues(EncodingReader &reader, ValueRange values);
1150
1151 /// Create a value to use for a forward reference.
1152 Value createForwardRef();
1153
1154 //===--------------------------------------------------------------------===//
1155 // Fields
1156
1157 /// This class represents a single value scope, in which a value scope is
1158 /// delimited by isolated from above regions.
1159 struct ValueScope {
1160 /// Push a new region state onto this scope, reserving enough values for
1161 /// those defined within the current region of the provided state.
1162 void push(RegionReadState &readState) {
1163 nextValueIDs.push_back(values.size());
1164 values.resize(values.size() + readState.numValues);
1165 }
1166
1167 /// Pop the values defined for the current region within the provided region
1168 /// state.
1169 void pop(RegionReadState &readState) {
1170 values.resize(values.size() - readState.numValues);
1171 nextValueIDs.pop_back();
1172 }
1173
1174 /// The set of values defined in this scope.
1175 std::vector<Value> values;
1176
1177 /// The ID for the next defined value for each region current being
1178 /// processed in this scope.
1179 SmallVector<unsigned, 4> nextValueIDs;
1180 };
1181
1182 /// The configuration of the parser.
1183 const ParserConfig &config;
1184
1185 /// A location to use when emitting errors.
1186 Location fileLoc;
1187
1188 /// The reader used to process attribute and types within the bytecode.
1189 AttrTypeReader attrTypeReader;
1190
1191 /// The version of the bytecode being read.
1192 uint64_t version = 0;
1193
1194 /// The producer of the bytecode being read.
1195 StringRef producer;
1196
1197 /// The table of IR units referenced within the bytecode file.
1198 SmallVector<BytecodeDialect> dialects;
1199 SmallVector<BytecodeOperationName> opNames;
1200
1201 /// The reader used to process resources within the bytecode.
1202 ResourceSectionReader resourceReader;
1203
1204 /// The table of strings referenced within the bytecode file.
1205 StringSectionReader stringReader;
1206
1207 /// The current set of available IR value scopes.
1208 std::vector<ValueScope> valueScopes;
1209 /// A block containing the set of operations defined to create forward
1210 /// references.
1211 Block forwardRefOps;
1212 /// A block containing previously created, and no longer used, forward
1213 /// reference operations.
1214 Block openForwardRefOps;
1215 /// An operation state used when instantiating forward references.
1216 OperationState forwardRefOpState;
1217};
1218} // namespace
1219
1220LogicalResult BytecodeReader::read(llvm::MemoryBufferRef buffer, Block *block) {
1221 EncodingReader reader(buffer.getBuffer(), fileLoc);
1222
1223 // Skip over the bytecode header, this should have already been checked.
1224 if (failed(reader.skipBytes(StringRef("ML\xefR").size())))
1225 return failure();
1226 // Parse the bytecode version and producer.
1227 if (failed(parseVersion(reader)) ||
1228 failed(reader.parseNullTerminatedString(producer)))
1229 return failure();
1230
1231 // Add a diagnostic handler that attaches a note that includes the original
1232 // producer of the bytecode.
1233 ScopedDiagnosticHandler diagHandler(getContext(), [&](Diagnostic &diag) {
1234 diag.attachNote() << "in bytecode version " << version
1235 << " produced by: " << producer;
1236 return failure();
1237 });
1238
1239 // Parse the raw data for each of the top-level sections of the bytecode.
1240 Optional<ArrayRef<uint8_t>> sectionDatas[bytecode::Section::kNumSections];
1241 while (!reader.empty()) {
1242 // Read the next section from the bytecode.
1243 bytecode::Section::ID sectionID;
1244 ArrayRef<uint8_t> sectionData;
1245 if (failed(reader.parseSection(sectionID, sectionData)))
1246 return failure();
1247
1248 // Check for duplicate sections, we only expect one instance of each.
1249 if (sectionDatas[sectionID]) {
1250 return reader.emitError("duplicate top-level section: ",
1251 toString(sectionID));
1252 }
1253 sectionDatas[sectionID] = sectionData;
1254 }
1255 // Check that all of the required sections were found.
1256 for (int i = 0; i < bytecode::Section::kNumSections; ++i) {
1257 bytecode::Section::ID sectionID = static_cast<bytecode::Section::ID>(i);
1258 if (!sectionDatas[i] && !isSectionOptional(sectionID)) {
1259 return reader.emitError("missing data for top-level section: ",
1260 toString(sectionID));
1261 }
1262 }
1263
1264 // Process the string section first.
1265 if (failed(stringReader.initialize(
1266 fileLoc, *sectionDatas[bytecode::Section::kString])))
1267 return failure();
1268
1269 // Process the dialect section.
1270 if (failed(parseDialectSection(*sectionDatas[bytecode::Section::kDialect])))
1271 return failure();
1272
1273 // Process the resource section if present.
1274 if (failed(parseResourceSection(
1275 sectionDatas[bytecode::Section::kResource],
1276 sectionDatas[bytecode::Section::kResourceOffset])))
1277 return failure();
1278
1279 // Process the attribute and type section.
1280 if (failed(attrTypeReader.initialize(
1281 dialects, *sectionDatas[bytecode::Section::kAttrType],
1282 *sectionDatas[bytecode::Section::kAttrTypeOffset])))
1283 return failure();
1284
1285 // Finally, process the IR section.
1286 return parseIRSection(*sectionDatas[bytecode::Section::kIR], block);
1287}
1288
1289LogicalResult BytecodeReader::parseVersion(EncodingReader &reader) {
1290 if (failed(reader.parseVarInt(version)))
1291 return failure();
1292
1293 // Validate the bytecode version.
1294 uint64_t currentVersion = bytecode::kVersion;
1295 if (version < currentVersion) {
1296 return reader.emitError("bytecode version ", version,
1297 " is older than the current version of ",
1298 currentVersion, ", and upgrade is not supported");
1299 }
1300 if (version > currentVersion) {
1301 return reader.emitError("bytecode version ", version,
1302 " is newer than the current version ",
1303 currentVersion);
1304 }
1305 return success();
1306}
1307
1308//===----------------------------------------------------------------------===//
1309// Dialect Section
1310
1311LogicalResult
1312BytecodeReader::parseDialectSection(ArrayRef<uint8_t> sectionData) {
1313 EncodingReader sectionReader(sectionData, fileLoc);
1314
1315 // Parse the number of dialects in the section.
1316 uint64_t numDialects;
1317 if (failed(sectionReader.parseVarInt(numDialects)))
1318 return failure();
1319 dialects.resize(numDialects);
1320
1321 // Parse each of the dialects.
1322 for (uint64_t i = 0; i < numDialects; ++i)
1323 if (failed(stringReader.parseString(sectionReader, dialects[i].name)))
1324 return failure();
1325
1326 // Parse the operation names, which are grouped by dialect.
1327 auto parseOpName = [&](BytecodeDialect *dialect) {
1328 StringRef opName;
1329 if (failed(stringReader.parseString(sectionReader, opName)))
1330 return failure();
1331 opNames.emplace_back(dialect, opName);
1332 return success();
1333 };
1334 while (!sectionReader.empty())
1335 if (failed(parseDialectGrouping(sectionReader, dialects, parseOpName)))
1336 return failure();
1337 return success();
1338}
1339
1340FailureOr<OperationName> BytecodeReader::parseOpName(EncodingReader &reader) {
1341 BytecodeOperationName *opName = nullptr;
1342 if (failed(parseEntry(reader, opNames, opName, "operation name")))
1343 return failure();
1344
1345 // Check to see if this operation name has already been resolved. If we
1346 // haven't, load the dialect and build the operation name.
1347 if (!opName->opName) {
1348 if (failed(opName->dialect->load(reader, getContext())))
1349 return failure();
1350 opName->opName.emplace((opName->dialect->name + "." + opName->name).str(),
1351 getContext());
1352 }
1353 return *opName->opName;
1354}
1355
1356//===----------------------------------------------------------------------===//
1357// Resource Section
1358
1359LogicalResult BytecodeReader::parseResourceSection(
1360 Optional<ArrayRef<uint8_t>> resourceData,
1361 Optional<ArrayRef<uint8_t>> resourceOffsetData) {
1362 // Ensure both sections are either present or not.
1363 if (resourceData.has_value() != resourceOffsetData.has_value()) {
1364 if (resourceOffsetData)
1365 return emitError(fileLoc, "unexpected resource offset section when "
1366 "resource section is not present");
1367 return emitError(
1368 fileLoc,
1369 "expected resource offset section when resource section is present");
1370 }
1371
1372 // If the resource sections are absent, there is nothing to do.
1373 if (!resourceData)
1374 return success();
1375
1376 // Initialize the resource reader with the resource sections.
1377 return resourceReader.initialize(fileLoc, config, dialects, stringReader,
1378 *resourceData, *resourceOffsetData);
1379}
1380
1381//===----------------------------------------------------------------------===//
1382// IR Section
1383
1384LogicalResult BytecodeReader::parseIRSection(ArrayRef<uint8_t> sectionData,
1385 Block *block) {
1386 EncodingReader reader(sectionData, fileLoc);
1387
1388 // A stack of operation regions currently being read from the bytecode.
1389 std::vector<RegionReadState> regionStack;
1390
1391 // Parse the top-level block using a temporary module operation.
1392 OwningOpRef<ModuleOp> moduleOp = ModuleOp::create(fileLoc);
1393 regionStack.emplace_back(*moduleOp, /*isIsolatedFromAbove=*/true);
1394 regionStack.back().curBlocks.push_back(moduleOp->getBody());
1395 regionStack.back().curBlock = regionStack.back().curRegion->begin();
1396 if (failed(parseBlock(reader, regionStack.back())))
1397 return failure();
1398 valueScopes.emplace_back();
1399 valueScopes.back().push(regionStack.back());
1400
1401 // Iteratively parse regions until everything has been resolved.
1402 while (!regionStack.empty())
1403 if (failed(parseRegions(reader, regionStack, regionStack.back())))
1404 return failure();
1405 if (!forwardRefOps.empty()) {
1406 return reader.emitError(
1407 "not all forward unresolved forward operand references");
1408 }
1409
1410 // Verify that the parsed operations are valid.
1411 if (config.shouldVerifyAfterParse() && failed(verify(*moduleOp)))
1412 return failure();
1413
1414 // Splice the parsed operations over to the provided top-level block.
1415 auto &parsedOps = moduleOp->getBody()->getOperations();
1416 auto &destOps = block->getOperations();
1417 destOps.splice(destOps.empty() ? destOps.end() : std::prev(destOps.end()),
1418 parsedOps, parsedOps.begin(), parsedOps.end());
1419 return success();
1420}
1421
1422LogicalResult
1423BytecodeReader::parseRegions(EncodingReader &reader,
1424 std::vector<RegionReadState> &regionStack,
1425 RegionReadState &readState) {
1426 // Read the regions of this operation.
1427 for (; readState.curRegion != readState.endRegion; ++readState.curRegion) {
1428 // If the current block hasn't been setup yet, parse the header for this
1429 // region.
1430 if (readState.curBlock == Region::iterator()) {
1431 if (failed(parseRegion(reader, readState)))
1432 return failure();
1433
1434 // If the region is empty, there is nothing to more to do.
1435 if (readState.curRegion->empty())
1436 continue;
1437 }
1438
1439 // Parse the blocks within the region.
1440 do {
1441 while (readState.numOpsRemaining--) {
1442 // Read in the next operation. We don't read its regions directly, we
1443 // handle those afterwards as necessary.
1444 bool isIsolatedFromAbove = false;
1445 FailureOr<Operation *> op =
1446 parseOpWithoutRegions(reader, readState, isIsolatedFromAbove);
1447 if (failed(op))
1448 return failure();
1449
1450 // If the op has regions, add it to the stack for processing.
1451 if ((*op)->getNumRegions()) {
1452 regionStack.emplace_back(*op, isIsolatedFromAbove);
1453
1454 // If the op is isolated from above, push a new value scope.
1455 if (isIsolatedFromAbove)
1456 valueScopes.emplace_back();
1457 return success();
1458 }
1459 }
1460
1461 // Move to the next block of the region.
1462 if (++readState.curBlock == readState.curRegion->end())
1463 break;
1464 if (failed(parseBlock(reader, readState)))
1465 return failure();
1466 } while (true);
1467
1468 // Reset the current block and any values reserved for this region.
1469 readState.curBlock = {};
1470 valueScopes.back().pop(readState);
1471 }
1472
1473 // When the regions have been fully parsed, pop them off of the read stack. If
1474 // the regions were isolated from above, we also pop the last value scope.
1475 if (readState.isIsolatedFromAbove)
1476 valueScopes.pop_back();
1477 regionStack.pop_back();
1478 return success();
1479}
1480
1481FailureOr<Operation *>
1482BytecodeReader::parseOpWithoutRegions(EncodingReader &reader,
1483 RegionReadState &readState,
1484 bool &isIsolatedFromAbove) {
1485 // Parse the name of the operation.
1486 FailureOr<OperationName> opName = parseOpName(reader);
1487 if (failed(opName))
1488 return failure();
1489
1490 // Parse the operation mask, which indicates which components of the operation
1491 // are present.
1492 uint8_t opMask;
1493 if (failed(reader.parseByte(opMask)))
1494 return failure();
1495
1496 /// Parse the location.
1497 LocationAttr opLoc;
1498 if (failed(parseAttribute(reader, opLoc)))
1499 return failure();
1500
1501 // With the location and name resolved, we can start building the operation
1502 // state.
1503 OperationState opState(opLoc, *opName);
1504
1505 // Parse the attributes of the operation.
1506 if (opMask & bytecode::OpEncodingMask::kHasAttrs) {
1507 DictionaryAttr dictAttr;
1508 if (failed(parseAttribute(reader, dictAttr)))
1509 return failure();
1510 opState.attributes = dictAttr;
1511 }
1512
1513 /// Parse the results of the operation.
1514 if (opMask & bytecode::OpEncodingMask::kHasResults) {
1515 uint64_t numResults;
1516 if (failed(reader.parseVarInt(numResults)))
1517 return failure();
1518 opState.types.resize(numResults);
1519 for (int i = 0, e = numResults; i < e; ++i)
1520 if (failed(parseType(reader, opState.types[i])))
1521 return failure();
1522 }
1523
1524 /// Parse the operands of the operation.
1525 if (opMask & bytecode::OpEncodingMask::kHasOperands) {
1526 uint64_t numOperands;
1527 if (failed(reader.parseVarInt(numOperands)))
1528 return failure();
1529 opState.operands.resize(numOperands);
1530 for (int i = 0, e = numOperands; i < e; ++i)
1531 if (!(opState.operands[i] = parseOperand(reader)))
1532 return failure();
1533 }
1534
1535 /// Parse the successors of the operation.
1536 if (opMask & bytecode::OpEncodingMask::kHasSuccessors) {
1537 uint64_t numSuccs;
1538 if (failed(reader.parseVarInt(numSuccs)))
1539 return failure();
1540 opState.successors.resize(numSuccs);
1541 for (int i = 0, e = numSuccs; i < e; ++i) {
1542 if (failed(parseEntry(reader, readState.curBlocks, opState.successors[i],
1543 "successor")))
1544 return failure();
1545 }
1546 }
1547
1548 /// Parse the regions of the operation.
1549 if (opMask & bytecode::OpEncodingMask::kHasInlineRegions) {
1550 uint64_t numRegions;
1551 if (failed(reader.parseVarIntWithFlag(numRegions, isIsolatedFromAbove)))
1552 return failure();
1553
1554 opState.regions.reserve(numRegions);
1555 for (int i = 0, e = numRegions; i < e; ++i)
1556 opState.regions.push_back(std::make_unique<Region>());
1557 }
1558
1559 // Create the operation at the back of the current block.
1560 Operation *op = Operation::create(opState);
1561 readState.curBlock->push_back(op);
1562
1563 // If the operation had results, update the value references.
1564 if (op->getNumResults() && failed(defineValues(reader, op->getResults())))
1565 return failure();
1566
1567 return op;
1568}
1569
1570LogicalResult BytecodeReader::parseRegion(EncodingReader &reader,
1571 RegionReadState &readState) {
1572 // Parse the number of blocks in the region.
1573 uint64_t numBlocks;
1574 if (failed(reader.parseVarInt(numBlocks)))
1575 return failure();
1576
1577 // If the region is empty, there is nothing else to do.
1578 if (numBlocks == 0)
1579 return success();
1580
1581 // Parse the number of values defined in this region.
1582 uint64_t numValues;
1583 if (failed(reader.parseVarInt(numValues)))
1584 return failure();
1585 readState.numValues = numValues;
1586
1587 // Create the blocks within this region. We do this before processing so that
1588 // we can rely on the blocks existing when creating operations.
1589 readState.curBlocks.clear();
1590 readState.curBlocks.reserve(numBlocks);
1591 for (uint64_t i = 0; i < numBlocks; ++i) {
1592 readState.curBlocks.push_back(new Block());
1593 readState.curRegion->push_back(readState.curBlocks.back());
1594 }
1595
1596 // Prepare the current value scope for this region.
1597 valueScopes.back().push(readState);
1598
1599 // Parse the entry block of the region.
1600 readState.curBlock = readState.curRegion->begin();
1601 return parseBlock(reader, readState);
1602}
1603
1604LogicalResult BytecodeReader::parseBlock(EncodingReader &reader,
1605 RegionReadState &readState) {
1606 bool hasArgs;
1607 if (failed(reader.parseVarIntWithFlag(readState.numOpsRemaining, hasArgs)))
1608 return failure();
1609
1610 // Parse the arguments of the block.
1611 if (hasArgs && failed(parseBlockArguments(reader, &*readState.curBlock)))
1612 return failure();
1613
1614 // We don't parse the operations of the block here, that's done elsewhere.
1615 return success();
1616}
1617
1618LogicalResult BytecodeReader::parseBlockArguments(EncodingReader &reader,
1619 Block *block) {
1620 // Parse the value ID for the first argument, and the number of arguments.
1621 uint64_t numArgs;
1622 if (failed(reader.parseVarInt(numArgs)))
1623 return failure();
1624
1625 SmallVector<Type> argTypes;
1626 SmallVector<Location> argLocs;
1627 argTypes.reserve(numArgs);
1628 argLocs.reserve(numArgs);
1629
1630 while (numArgs--) {
1631 Type argType;
1632 LocationAttr argLoc;
1633 if (failed(parseType(reader, argType)) ||
1634 failed(parseAttribute(reader, argLoc)))
1635 return failure();
1636
1637 argTypes.push_back(argType);
1638 argLocs.push_back(argLoc);
1639 }
1640 block->addArguments(argTypes, argLocs);
1641 return defineValues(reader, block->getArguments());
1642}
1643
1644//===----------------------------------------------------------------------===//
1645// Value Processing
1646
1647Value BytecodeReader::parseOperand(EncodingReader &reader) {
1648 std::vector<Value> &values = valueScopes.back().values;
1649 Value *value = nullptr;
1650 if (failed(parseEntry(reader, values, value, "value")))
1651 return Value();
1652
1653 // Create a new forward reference if necessary.
1654 if (!*value)
1655 *value = createForwardRef();
1656 return *value;
1657}
1658
1659LogicalResult BytecodeReader::defineValues(EncodingReader &reader,
1660 ValueRange newValues) {
1661 ValueScope &valueScope = valueScopes.back();
1662 std::vector<Value> &values = valueScope.values;
1663
1664 unsigned &valueID = valueScope.nextValueIDs.back();
1665 unsigned valueIDEnd = valueID + newValues.size();
1666 if (valueIDEnd > values.size()) {
1667 return reader.emitError(
1668 "value index range was outside of the expected range for "
1669 "the parent region, got [",
1670 valueID, ", ", valueIDEnd, "), but the maximum index was ",
1671 values.size() - 1);
1672 }
1673
1674 // Assign the values and update any forward references.
1675 for (unsigned i = 0, e = newValues.size(); i != e; ++i, ++valueID) {
1676 Value newValue = newValues[i];
1677
1678 // Check to see if a definition for this value already exists.
1679 if (Value oldValue = std::exchange(values[valueID], newValue)) {
1680 Operation *forwardRefOp = oldValue.getDefiningOp();
1681
1682 // Assert that this is a forward reference operation. Given how we compute
1683 // definition ids (incrementally as we parse), it shouldn't be possible
1684 // for the value to be defined any other way.
1685 assert(forwardRefOp && forwardRefOp->getBlock() == &forwardRefOps &&(static_cast <bool> (forwardRefOp && forwardRefOp
->getBlock() == &forwardRefOps && "value index was already defined?"
) ? void (0) : __assert_fail ("forwardRefOp && forwardRefOp->getBlock() == &forwardRefOps && \"value index was already defined?\""
, "mlir/lib/Bytecode/Reader/BytecodeReader.cpp", 1686, __extension__
__PRETTY_FUNCTION__))
1686 "value index was already defined?")(static_cast <bool> (forwardRefOp && forwardRefOp
->getBlock() == &forwardRefOps && "value index was already defined?"
) ? void (0) : __assert_fail ("forwardRefOp && forwardRefOp->getBlock() == &forwardRefOps && \"value index was already defined?\""
, "mlir/lib/Bytecode/Reader/BytecodeReader.cpp", 1686, __extension__
__PRETTY_FUNCTION__))
;
1687
1688 oldValue.replaceAllUsesWith(newValue);
1689 forwardRefOp->moveBefore(&openForwardRefOps, openForwardRefOps.end());
1690 }
1691 }
1692 return success();
1693}
1694
1695Value BytecodeReader::createForwardRef() {
1696 // Check for an avaliable existing operation to use. Otherwise, create a new
1697 // fake operation to use for the reference.
1698 if (!openForwardRefOps.empty()) {
1699 Operation *op = &openForwardRefOps.back();
1700 op->moveBefore(&forwardRefOps, forwardRefOps.end());
1701 } else {
1702 forwardRefOps.push_back(Operation::create(forwardRefOpState));
1703 }
1704 return forwardRefOps.back().getResult(0);
1705}
1706
1707//===----------------------------------------------------------------------===//
1708// Entry Points
1709//===----------------------------------------------------------------------===//
1710
1711bool mlir::isBytecode(llvm::MemoryBufferRef buffer) {
1712 return buffer.getBuffer().startswith("ML\xefR");
1713}
1714
1715LogicalResult mlir::readBytecodeFile(llvm::MemoryBufferRef buffer, Block *block,
1716 const ParserConfig &config) {
1717 Location sourceFileLoc =
1718 FileLineColLoc::get(config.getContext(), buffer.getBufferIdentifier(),
1719 /*line=*/0, /*column=*/0);
1720 if (!isBytecode(buffer)) {
1721 return emitError(sourceFileLoc,
1722 "input buffer is not an MLIR bytecode file");
1723 }
1724
1725 BytecodeReader reader(sourceFileLoc, config);
1726 return reader.read(buffer, block);
1727}