Bug Summary

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