MLIR 24.0.0git
TranslateFromWasm.cpp
Go to the documentation of this file.
1//===- TranslateFromWasm.cpp - Translating to WasmSSA dialect -------------===//
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// This file implements the WebAssembly importer.
10//
11//===----------------------------------------------------------------------===//
12
14#include "mlir/IR/Attributes.h"
15#include "mlir/IR/Builders.h"
19#include "mlir/IR/Diagnostics.h"
20#include "mlir/IR/Location.h"
21#include "mlir/Support/LLVM.h"
24#include "llvm/Support/Debug.h"
25#include "llvm/Support/DebugLog.h"
26#include "llvm/Support/Endian.h"
27#include "llvm/Support/FormatVariadic.h"
28#include "llvm/Support/LEB128.h"
29#include "llvm/Support/LogicalResult.h"
30
31#include <cassert>
32#include <cstddef>
33#include <cstdint>
34#include <type_traits>
35#include <variant>
36
37#define DEBUG_TYPE "wasm-translate"
38
39static_assert(CHAR_BIT == 8,
40 "This code expects std::byte to be exactly 8 bits");
41
42using namespace mlir;
43using namespace mlir::wasm;
44using namespace mlir::wasmssa;
45
46namespace {
47using section_id_t = uint8_t;
48enum struct WasmSectionType : section_id_t {
49 CUSTOM = 0,
50 TYPE = 1,
51 IMPORT = 2,
52 FUNCTION = 3,
53 TABLE = 4,
54 MEMORY = 5,
55 GLOBAL = 6,
56 EXPORT = 7,
57 START = 8,
58 ELEMENT = 9,
59 CODE = 10,
60 DATA = 11,
61 DATACOUNT = 12
62};
63
64constexpr section_id_t highestWasmSectionID{
65 static_cast<section_id_t>(WasmSectionType::DATACOUNT)};
66
67#define APPLY_WASM_SEC_TRANSFORM \
68 WASM_SEC_TRANSFORM(CUSTOM) \
69 WASM_SEC_TRANSFORM(TYPE) \
70 WASM_SEC_TRANSFORM(IMPORT) \
71 WASM_SEC_TRANSFORM(FUNCTION) \
72 WASM_SEC_TRANSFORM(TABLE) \
73 WASM_SEC_TRANSFORM(MEMORY) \
74 WASM_SEC_TRANSFORM(GLOBAL) \
75 WASM_SEC_TRANSFORM(EXPORT) \
76 WASM_SEC_TRANSFORM(START) \
77 WASM_SEC_TRANSFORM(ELEMENT) \
78 WASM_SEC_TRANSFORM(CODE) \
79 WASM_SEC_TRANSFORM(DATA) \
80 WASM_SEC_TRANSFORM(DATACOUNT)
81
82template <WasmSectionType>
83constexpr const char *wasmSectionName = "";
84
85#define WASM_SEC_TRANSFORM(section) \
86 template <> \
87 [[maybe_unused]] constexpr const char \
88 *wasmSectionName<WasmSectionType::section> = #section;
90#undef WASM_SEC_TRANSFORM
91
92constexpr bool sectionShouldBeUnique(WasmSectionType secType) {
93 return secType != WasmSectionType::CUSTOM;
94}
95
96template <std::byte... Bytes>
97struct ByteSequence {};
98
99/// Template class for representing a byte sequence of only one byte
100template <std::byte Byte>
101struct UniqueByte : ByteSequence<Byte> {};
102
103[[maybe_unused]] constexpr ByteSequence<
106 WasmBinaryEncoding::Type::v128> valueTypesEncodings{};
107
108template <std::byte... allowedFlags>
109constexpr bool isValueOneOf(std::byte value,
110 ByteSequence<allowedFlags...> = {}) {
111 return ((value == allowedFlags) | ... | false);
112}
113
114template <std::byte... flags>
115constexpr bool isNotIn(std::byte value, ByteSequence<flags...> = {}) {
116 return !isValueOneOf<flags...>(value);
117}
118
119struct GlobalTypeRecord {
120 Type type;
121 bool isMutable;
122};
123
124struct TypeIdxRecord {
125 size_t id;
126};
127
128struct SymbolRefContainer {
129 FlatSymbolRefAttr symbol;
130};
131
132struct GlobalSymbolRefContainer : SymbolRefContainer {
133 Type globalType;
134};
135
136struct FunctionSymbolRefContainer : SymbolRefContainer {
137 FunctionType functionType;
138};
139
140using ImportDesc =
141 std::variant<TypeIdxRecord, TableType, LimitType, GlobalTypeRecord>;
142
143using parsed_inst_t = FailureOr<SmallVector<Value>>;
144
145struct EmptyBlockMarker {};
146using BlockTypeParseResult =
147 std::variant<EmptyBlockMarker, TypeIdxRecord, Type>;
148
149struct WasmModuleSymbolTables {
150 SmallVector<FunctionSymbolRefContainer> funcSymbols;
151 SmallVector<GlobalSymbolRefContainer> globalSymbols;
152 SmallVector<SymbolRefContainer> memSymbols;
153 SmallVector<SymbolRefContainer> tableSymbols;
154 SmallVector<FunctionType> moduleFuncTypes;
155
156 std::string getNewSymbolName(StringRef prefix, size_t id) const {
157 return (prefix + Twine{id}).str();
158 }
159
160 std::string getNewFuncSymbolName() const {
161 size_t id = funcSymbols.size();
162 return getNewSymbolName("func_", id);
163 }
164
165 std::string getNewGlobalSymbolName() const {
166 size_t id = globalSymbols.size();
167 return getNewSymbolName("global_", id);
168 }
169
170 std::string getNewMemorySymbolName() const {
171 size_t id = memSymbols.size();
172 return getNewSymbolName("mem_", id);
173 }
174
175 std::string getNewTableSymbolName() const {
176 size_t id = tableSymbols.size();
177 return getNewSymbolName("table_", id);
178 }
179};
180
181class ParserHead;
182
183/// Wrapper around SmallVector to only allow access as push and pop on the
184/// stack. Makes sure that there are no "free accesses" on the stack to preserve
185/// its state.
186/// This class also keep tracks of the Wasm labels defined by different ops,
187/// which can be targeted by control flow ops. This can be modeled as part of
188/// the Value Stack as Wasm control flow ops can only target enclosing labels.
189class ValueStack {
190private:
191 struct LabelLevel {
192 size_t stackIdx;
193 LabelLevelOpInterface levelOp;
194 };
195
196public:
197 bool empty() const { return values.empty(); }
198
199 size_t size() const { return values.size(); }
200
201 /// Pops values from the stack because they are being used in an operation.
202 /// @param operandTypes The list of expected types of the operation, used
203 /// to know how many values to pop and check if the types match the
204 /// expectation.
205 /// @param opLoc Location of the caller, used to report accurately the
206 /// location
207 /// if an error occurs.
208 /// @return Failure or the vector of popped values.
209 FailureOr<SmallVector<Value>> popOperands(TypeRange operandTypes,
210 Location *opLoc);
211
212 /// Push the results of an operation to the stack so they can be used in a
213 /// following operation.
214 /// @param results The list of results of the operation
215 /// @param opLoc Location of the caller, used to report accurately the
216 /// location
217 /// if an error occurs.
218 LogicalResult pushResults(ValueRange results, Location *opLoc);
219
220 void addLabelLevel(LabelLevelOpInterface levelOp) {
221 labelLevel.push_back({values.size(), levelOp});
222 LDBG() << "Adding a new frame context to ValueStack";
223 }
224
225 void dropLabelLevel() {
226 assert(!labelLevel.empty() && "Trying to drop a frame from empty context");
227 auto newSize = labelLevel.pop_back_val().stackIdx;
228 values.truncate(newSize);
229 }
230#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
231 /// A simple dump function for debugging.
232 /// Writes output to llvm::dbgs().
233 LLVM_DUMP_METHOD void dump() const;
234#endif
235
236private:
237 SmallVector<Value> values;
238 SmallVector<LabelLevel> labelLevel;
239};
240
241using local_val_t = TypedValue<wasmssa::LocalRefType>;
242
243template <size_t... IS>
244constexpr ByteSequence<std::byte{IS}...>
245 castIndexSequenceToBytes(std::index_sequence<IS...>) {
246 return {};
247}
248
249constexpr auto all8bitsBytes =
250 castIndexSequenceToBytes(std::make_index_sequence<256>());
251
252template <std::byte>
253struct OpCode {};
254
255class ExpressionParser {
256public:
257 using locals_t = SmallVector<local_val_t>;
258 ExpressionParser(ParserHead &parser, WasmModuleSymbolTables const &symbols,
259 ArrayRef<local_val_t> initLocal)
260 : parser{parser}, symbols{symbols}, locals{initLocal} {}
261
262private:
263 inline parsed_inst_t dispatchToInstParser(std::byte opCode,
264 OpBuilder &builder);
265 ///
266 /// RAII guard class for creating a nesting level
267 ///
268 struct NestingContextGuard {
269 NestingContextGuard(ExpressionParser &parser, LabelLevelOpInterface levelOp)
270 : parser{parser} {
271 parser.addNestingContextLevel(levelOp);
272 }
273 NestingContextGuard(NestingContextGuard &&other) : parser{other.parser} {
274 other.shouldDropOnDestruct = false;
275 }
276 NestingContextGuard(NestingContextGuard const &) = delete;
277 ~NestingContextGuard() {
278 if (shouldDropOnDestruct)
279 parser.dropNestingContextLevel();
280 }
281 ExpressionParser &parser;
282 bool shouldDropOnDestruct = true;
283 };
284
285 void addNestingContextLevel(LabelLevelOpInterface levelOp) {
286 valueStack.addLabelLevel(levelOp);
287 }
288
289 void dropNestingContextLevel() {
290 // Should always succeed as we are droping the frame that was previously
291 // created.
292 valueStack.dropLabelLevel();
293 }
294
295 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
296 EmptyBlockMarker) {
297 return builder.getFunctionType({}, {});
298 }
299
300 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
301 TypeIdxRecord type) {
302 if (type.id >= symbols.moduleFuncTypes.size())
303 return emitError(*currentOpLoc,
304 "type index references nonexistent type (")
305 << type.id << "). Only " << symbols.moduleFuncTypes.size()
306 << " types are registered";
307 return symbols.moduleFuncTypes[type.id];
308 }
309
310 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
311 Type valType) {
312 return builder.getFunctionType({}, {valType});
313 }
314
315 llvm::FailureOr<FunctionType>
316 getFuncTypeFor(OpBuilder &builder, BlockTypeParseResult parseResult) {
317 return std::visit(
318 [this, &builder](auto value) { return getFuncTypeFor(builder, value); },
319 parseResult);
320 }
321
322 llvm::FailureOr<FunctionType>
323 getFuncTypeFor(OpBuilder &builder,
324 llvm::FailureOr<BlockTypeParseResult> parseResult) {
325 if (llvm::failed(parseResult))
326 return failure();
327 return getFuncTypeFor(builder, *parseResult);
328 }
329
330 struct ParseResultWithInfo {
331 SmallVector<Value> opResults;
332 std::byte endingByte;
333 };
334
335 llvm::FailureOr<FunctionType> parseBlockFuncType(OpBuilder &builder);
336
337public:
338 template <std::byte ParseEndByte = WasmBinaryEncoding::endByte>
339 parsed_inst_t parse(OpBuilder &builder, UniqueByte<ParseEndByte> = {});
340
341 template <std::byte... ExpressionParseEnd>
342 FailureOr<ParseResultWithInfo>
343 parse(OpBuilder &builder,
344 ByteSequence<ExpressionParseEnd...> parsingEndFilters);
345
346 NestingContextGuard addNesting(LabelLevelOpInterface levelOp) {
347 return NestingContextGuard{*this, levelOp};
348 }
349
350 FailureOr<llvm::SmallVector<Value>> popOperands(TypeRange operandTypes) {
351 return valueStack.popOperands(operandTypes, &currentOpLoc.value());
352 }
353
354 LogicalResult pushResults(ValueRange results) {
355 return valueStack.pushResults(results, &currentOpLoc.value());
356 }
357
358 /// The local.set and local.tee operations behave similarly and only differ
359 /// on their return value. This function factorizes the behavior of the two
360 /// operations in one place.
361 template <typename OpToCreate>
362 parsed_inst_t parseSetOrTee(OpBuilder &);
363
364 Location getCurrentOpLoc() {
365 assert(currentOpLoc.has_value() &&
366 "expects current opcode location to be set");
367 return *currentOpLoc;
368 }
369
370 class ExprParserProxy {
371 public:
372 friend ExpressionParser;
373 inline auto parseBlockFuncType(OpBuilder &builder) {
374 return exprParser.parseBlockFuncType(builder);
375 }
376
377 /// @param blockToFill: the block which content will be populated
378 /// @param resType: the type that this block is supposed to return
379 template <typename FilterT = ByteSequence<WasmBinaryEncoding::endByte>>
380 llvm::FailureOr<std::byte>
381 parseBlockContent(OpBuilder &builder, Block *blockToFill,
382 TypeRange resTypes, Location opLoc,
383 LabelLevelOpInterface levelOp,
384 FilterT parseEndBytes = {}) {
385 OpBuilder::InsertionGuard guard(builder);
386 assert(blockToFill && blockToFill->empty() && "expected an empty block");
387 builder.setInsertionPointToStart(blockToFill);
388 LDBG() << "parsing a block of type "
389 << builder.getFunctionType(blockToFill->getArgumentTypes(),
390 resTypes);
391 auto nestingGuard = exprParser.addNesting(levelOp);
392
393 if (failed(exprParser.pushResults(blockToFill->getArguments())))
394 return failure();
395 auto bodyParsingRes = exprParser.parse(builder, parseEndBytes);
396 if (failed(bodyParsingRes))
397 return failure();
398 auto returnOperands = exprParser.popOperands(resTypes);
399 if (failed(returnOperands))
400 return failure();
401 BlockReturnOp::create(builder, opLoc, *returnOperands);
402 LDBG() << "end of parsing of a block";
403 return bodyParsingRes->endingByte;
404 }
405
406 inline ParserHead &parser() { return exprParser.parser; }
407
408 /// Blocks and Loops have a similar format and differ only in how their exit
409 /// is handled which doesn´t matter at parsing time. Factorizes in one
410 /// function.
411 template <typename OpToCreate>
412 parsed_inst_t parseBlockLikeOp(OpBuilder &);
413
414 inline auto getCurrentOpLoc() { return exprParser.getCurrentOpLoc(); }
415
416 inline auto popOperands(TypeRange operandTypes) {
417 return exprParser.popOperands(operandTypes);
418 }
419
420 inline auto &symbols() { return exprParser.symbols; }
421
422 inline auto &locals() { return exprParser.locals; }
423
424 template <typename OpToCreate>
425 parsed_inst_t parseSetOrTee(OpBuilder &builder) {
426 return exprParser.parseSetOrTee<OpToCreate>(builder);
427 }
428
429 template <typename valueT>
430 parsed_inst_t
431 parseConstInst(OpBuilder &builder,
432 std::enable_if_t<std::is_arithmetic_v<valueT>> * = nullptr);
433
434 /// Construct an operation with \p numOperands operands and a single result.
435 /// Each operand must have the same type. Suitable for e.g. binops, unary
436 /// ops, etc.
437 ///
438 /// \p opcode - The WASM opcode to build.
439 /// \p valueType - The operand and result type for the built instruction.
440 /// \p numOperands - The number of operands for the built operation.
441 ///
442 /// \returns The parsed instruction result, or failure.
443 template <typename opcode, typename valueType, unsigned int numOperands>
444 inline parsed_inst_t buildNumericOp(
445 OpBuilder &builder,
446 std::enable_if_t<std::is_arithmetic_v<valueType>> * = nullptr);
447
448 /// Construct a conversion operation of type \p opType that takes a value
449 /// from
450 /// type \p inputType on the stack and will produce a value of type
451 /// \p outputType.
452 ///
453 /// \p opType - The WASM dialect operation to build.
454 /// \p inputType - The operand type for the built instruction.
455 /// \p outputType - The result type for the built instruction.
456 ///
457 /// \returns The parsed instruction result, or failure.
458 template <typename opType, typename inputType, typename outputType,
459 typename... extraArgsT>
460 inline parsed_inst_t buildConvertOp(OpBuilder &builder, extraArgsT...);
461
462 private:
463 explicit ExprParserProxy(ExpressionParser &exprParser)
464 : exprParser{exprParser} {};
465
466 private:
467 ExpressionParser &exprParser;
468 };
469
470private:
471 std::optional<Location> currentOpLoc;
472 ParserHead &parser;
473 WasmModuleSymbolTables const &symbols;
474 locals_t locals;
475 ValueStack valueStack;
476};
477
478class ParserHead {
479public:
480 ParserHead(StringRef src, StringAttr name) : head{src}, locName{name} {}
481 ParserHead(ParserHead &&) = default;
482
483private:
484 ParserHead(ParserHead const &other) = default;
485
486public:
487 auto getLocation() const {
488 return FileLineColLoc::get(locName, 0, anchorOffset + offset);
489 }
490
491 FailureOr<StringRef> consumeNBytes(size_t nBytes) {
492 LDBG() << "Consume " << nBytes << " bytes";
493 LDBG() << " Bytes remaining: " << size();
494 LDBG() << " Current offset: " << offset;
495 if (nBytes > size())
496 return emitError(getLocation(), "trying to extract ")
497 << nBytes << "bytes when only " << size() << "are available";
498
499 StringRef res = head.slice(offset, offset + nBytes);
500 offset += nBytes;
501 LDBG() << " Updated offset (+" << nBytes << "): " << offset;
502 return res;
503 }
504
505 FailureOr<std::byte> consumeByte() {
506 FailureOr<StringRef> res = consumeNBytes(1);
507 if (failed(res))
508 return failure();
509 return std::byte{*res->bytes_begin()};
510 }
511
512 template <typename T>
513 FailureOr<T> parseLiteral();
514
515 FailureOr<uint32_t> parseVectorSize();
516
517private:
518 // TODO: This is equivalent to parseLiteral<uint32_t> and could be removed
519 // if parseLiteral specialization were moved here, but default GCC on Ubuntu
520 // 22.04 has bug with template specialization in class declaration
521 inline FailureOr<uint32_t> parseUI32();
522 inline FailureOr<int64_t> parseI64();
523
524public:
525 FailureOr<StringRef> parseName() {
526 FailureOr<uint32_t> size = parseVectorSize();
527 if (failed(size))
528 return failure();
529
530 return consumeNBytes(*size);
531 }
532
533 FailureOr<WasmSectionType> parseWasmSectionType() {
534 FailureOr<std::byte> id = consumeByte();
535 if (failed(id))
536 return failure();
537 if (std::to_integer<unsigned>(*id) > highestWasmSectionID)
538 return emitError(getLocation(), "invalid section ID: ")
539 << static_cast<int>(*id);
540 return static_cast<WasmSectionType>(*id);
541 }
542
543 FailureOr<LimitType> parseLimit(MLIRContext *ctx) {
544 using WasmLimits = WasmBinaryEncoding::LimitHeader;
545 FileLineColLoc limitLocation = getLocation();
546 FailureOr<std::byte> limitHeader = consumeByte();
547 if (failed(limitHeader))
548 return failure();
549
550 if (isNotIn<WasmLimits::bothLimits, WasmLimits::lowLimitOnly>(*limitHeader))
551 return emitError(limitLocation, "invalid limit header: ")
552 << static_cast<int>(*limitHeader);
553 FailureOr<uint32_t> minParse = parseUI32();
554 if (failed(minParse))
555 return failure();
556 std::optional<uint32_t> max{std::nullopt};
557 if (*limitHeader == WasmLimits::bothLimits) {
558 FailureOr<uint32_t> maxParse = parseUI32();
559 if (failed(maxParse))
560 return failure();
561 max = *maxParse;
562 }
563 return LimitType::get(ctx, *minParse, max);
564 }
565
566 FailureOr<Type> parseValueType(MLIRContext *ctx) {
567 FileLineColLoc typeLoc = getLocation();
568 FailureOr<std::byte> typeEncoding = consumeByte();
569 if (failed(typeEncoding))
570 return failure();
571 switch (*typeEncoding) {
573 return IntegerType::get(ctx, 32);
575 return IntegerType::get(ctx, 64);
577 return Float32Type::get(ctx);
579 return Float64Type::get(ctx);
581 return IntegerType::get(ctx, 128);
583 return wasmssa::FuncRefType::get(ctx);
585 return wasmssa::ExternRefType::get(ctx);
586 default:
587 return emitError(typeLoc, "invalid value type encoding: ")
588 << static_cast<int>(*typeEncoding);
589 }
590 }
591
592 FailureOr<GlobalTypeRecord> parseGlobalType(MLIRContext *ctx) {
593 using WasmGlobalMut = WasmBinaryEncoding::GlobalMutability;
594 FailureOr<Type> typeParsed = parseValueType(ctx);
595 if (failed(typeParsed))
596 return failure();
597 FileLineColLoc mutLoc = getLocation();
598 FailureOr<std::byte> mutSpec = consumeByte();
599 if (failed(mutSpec))
600 return failure();
601 if (isNotIn<WasmGlobalMut::isConst, WasmGlobalMut::isMutable>(*mutSpec))
602 return emitError(mutLoc, "invalid global mutability specifier: ")
603 << static_cast<int>(*mutSpec);
604 return GlobalTypeRecord{*typeParsed, *mutSpec == WasmGlobalMut::isMutable};
605 }
606
607 FailureOr<TupleType> parseResultType(MLIRContext *ctx) {
608 FailureOr<uint32_t> nParamsParsed = parseVectorSize();
609 if (failed(nParamsParsed))
610 return failure();
611 uint32_t nParams = *nParamsParsed;
612 SmallVector<Type> res{};
613 res.reserve(nParams);
614 for (size_t i = 0; i < nParams; ++i) {
615 FailureOr<Type> parsedType = parseValueType(ctx);
616 if (failed(parsedType))
617 return failure();
618 res.push_back(*parsedType);
619 }
620 return TupleType::get(ctx, res);
621 }
622
623 FailureOr<FunctionType> parseFunctionType(MLIRContext *ctx) {
624 FileLineColLoc typeLoc = getLocation();
625 FailureOr<std::byte> funcTypeHeader = consumeByte();
626 if (failed(funcTypeHeader))
627 return failure();
628 if (*funcTypeHeader != WasmBinaryEncoding::Type::funcType)
629 return emitError(typeLoc, "invalid function type header byte. Expecting ")
630 << std::to_integer<unsigned>(WasmBinaryEncoding::Type::funcType)
631 << " got " << std::to_integer<unsigned>(*funcTypeHeader);
632 FailureOr<TupleType> inputTypes = parseResultType(ctx);
633 if (failed(inputTypes))
634 return failure();
635
636 FailureOr<TupleType> resTypes = parseResultType(ctx);
637 if (failed(resTypes))
638 return failure();
639
640 return FunctionType::get(ctx, inputTypes->getTypes(), resTypes->getTypes());
641 }
642
643 FailureOr<TypeIdxRecord> parseTypeIndex() {
644 FailureOr<uint32_t> res = parseUI32();
645 if (failed(res))
646 return failure();
647 return TypeIdxRecord{*res};
648 }
649
650 FailureOr<TableType> parseTableType(MLIRContext *ctx) {
651 FailureOr<Type> elmTypeParse = parseValueType(ctx);
652 if (failed(elmTypeParse))
653 return failure();
654 if (!isWasmRefType(*elmTypeParse))
655 return emitError(getLocation(), "invalid element type for table");
656 FailureOr<LimitType> limitParse = parseLimit(ctx);
657 if (failed(limitParse))
658 return failure();
659 return TableType::get(ctx, *elmTypeParse, *limitParse);
660 }
661
662 FailureOr<ImportDesc> parseImportDesc(MLIRContext *ctx) {
663 FileLineColLoc importLoc = getLocation();
664 FailureOr<std::byte> importType = consumeByte();
665 auto packager = [](auto parseResult) -> FailureOr<ImportDesc> {
666 if (failed(parseResult))
667 return failure();
668 return {*parseResult};
669 };
670 if (failed(importType))
671 return failure();
672 switch (*importType) {
674 return packager(parseTypeIndex());
676 return packager(parseTableType(ctx));
678 return packager(parseLimit(ctx));
680 return packager(parseGlobalType(ctx));
681 default:
682 return emitError(importLoc, "invalid import type descriptor: ")
683 << static_cast<int>(*importType);
684 }
685 }
686
687 parsed_inst_t parseExpression(OpBuilder &builder,
688 WasmModuleSymbolTables const &symbols,
689 ArrayRef<local_val_t> locals = {}) {
690 auto eParser = ExpressionParser{*this, symbols, locals};
691 return eParser.parse(builder);
692 }
693
694 LogicalResult parseCodeFor(FuncOp func,
695 WasmModuleSymbolTables const &symbols) {
696 SmallVector<local_val_t> locals{};
697 // Populating locals with function argument
698 Block &block = func.getBody().front();
699 // Delete temporary return argument which was only created for IR validity
700 assert(func.getBody().getBlocks().size() == 1 &&
701 "Function should only have its default created block at this point");
702 assert(block.getOperations().size() == 1 &&
703 "Only the placeholder return op should be present at this point");
704 auto returnOp = cast<ReturnOp>(&block.back());
705 assert(returnOp);
706
707 FailureOr<uint32_t> codeSizeInBytes = parseUI32();
708 if (failed(codeSizeInBytes))
709 return failure();
710 FailureOr<StringRef> codeContent = consumeNBytes(*codeSizeInBytes);
711 if (failed(codeContent))
712 return failure();
713 auto name = StringAttr::get(func->getContext(),
714 locName.str() + "::" + func.getSymName());
715 auto cParser = ParserHead{*codeContent, name};
716 FailureOr<uint32_t> localVecSize = cParser.parseVectorSize();
717 if (failed(localVecSize))
718 return failure();
719 OpBuilder builder{&func.getBody().front().back()};
720 for (auto arg : block.getArguments())
721 locals.push_back(cast<TypedValue<LocalRefType>>(arg));
722 // Declare the local ops
723 uint32_t nVarVec = *localVecSize;
724 for (size_t i = 0; i < nVarVec; ++i) {
725 FileLineColLoc varLoc = cParser.getLocation();
726 FailureOr<uint32_t> nSubVar = cParser.parseUI32();
727 if (failed(nSubVar))
728 return failure();
729 FailureOr<Type> varT = cParser.parseValueType(func->getContext());
730 if (failed(varT))
731 return failure();
732 for (size_t j = 0; j < *nSubVar; ++j) {
733 auto local = LocalOp::create(builder, varLoc, *varT);
734 locals.push_back(local.getResult());
735 }
736 }
737 parsed_inst_t res = cParser.parseExpression(builder, symbols, locals);
738 if (failed(res))
739 return failure();
740 if (!cParser.end())
741 return emitError(cParser.getLocation(),
742 "unparsed garbage remaining at end of code block");
743 ReturnOp::create(builder, func->getLoc(), *res);
744 returnOp->erase();
745 return success();
746 }
747
748 llvm::FailureOr<BlockTypeParseResult> parseBlockType(MLIRContext *ctx) {
749 auto loc = getLocation();
750 auto blockIndicator = peek();
751 if (failed(blockIndicator))
752 return failure();
753 if (*blockIndicator == WasmBinaryEncoding::Type::emptyBlockType) {
754 offset += 1;
755 return {EmptyBlockMarker{}};
756 }
757 if (isValueOneOf(*blockIndicator, valueTypesEncodings))
758 return parseValueType(ctx);
759 /// Block type idx is a 32 bit positive integer encoded as a 33 bit signed
760 /// value
761 auto typeIdx = parseI64();
762 if (failed(typeIdx))
763 return failure();
764 if (*typeIdx < 0 || *typeIdx > std::numeric_limits<uint32_t>::max())
765 return emitError(loc, "type ID should be representable with an unsigned "
766 "32 bits integer. Got ")
767 << *typeIdx;
768 return {TypeIdxRecord{static_cast<uint32_t>(*typeIdx)}};
769 }
770
771 bool end() const { return curHead().empty(); }
772
773 ParserHead copy() const { return *this; }
774
775private:
776 StringRef curHead() const { return head.drop_front(offset); }
777
778 FailureOr<std::byte> peek() const {
779 if (end())
780 return emitError(
781 getLocation(),
782 "trying to peek at next byte, but input stream is empty");
783 return static_cast<std::byte>(curHead().front());
784 }
785
786 size_t size() const { return head.size() - offset; }
787
788 StringRef head;
789 StringAttr locName;
790 unsigned anchorOffset{0};
791 unsigned offset{0};
792};
793
794template <>
795FailureOr<float> ParserHead::parseLiteral<float>() {
796 FailureOr<StringRef> bytes = consumeNBytes(4);
797 if (failed(bytes))
798 return failure();
799 return llvm::support::endian::read<float>(bytes->bytes_begin(),
800 llvm::endianness::little);
801}
802
803template <>
804FailureOr<double> ParserHead::parseLiteral<double>() {
805 FailureOr<StringRef> bytes = consumeNBytes(8);
806 if (failed(bytes))
807 return failure();
808 return llvm::support::endian::read<double>(bytes->bytes_begin(),
809 llvm::endianness::little);
810}
811
812template <>
813FailureOr<uint32_t> ParserHead::parseLiteral<uint32_t>() {
814 char const *error = nullptr;
815 uint32_t res{0};
816 unsigned encodingSize{0};
817 StringRef src = curHead();
818 uint64_t decoded = llvm::decodeULEB128(src.bytes_begin(), &encodingSize,
819 src.bytes_end(), &error);
820 if (error)
821 return emitError(getLocation(), error);
822
823 if (std::isgreater(decoded, std::numeric_limits<uint32_t>::max()))
824 return emitError(getLocation()) << "literal does not fit on 32 bits";
825
826 res = static_cast<uint32_t>(decoded);
827 offset += encodingSize;
828 return res;
829}
830
831template <>
832FailureOr<int32_t> ParserHead::parseLiteral<int32_t>() {
833 char const *error = nullptr;
834 int32_t res{0};
835 unsigned encodingSize{0};
836 StringRef src = curHead();
837 int64_t decoded = llvm::decodeSLEB128(src.bytes_begin(), &encodingSize,
838 src.bytes_end(), &error);
839 if (error)
840 return emitError(getLocation(), error);
841 if (std::isgreater(decoded, std::numeric_limits<int32_t>::max()) ||
842 std::isgreater(std::numeric_limits<int32_t>::min(), decoded))
843 return emitError(getLocation()) << "literal does not fit on 32 bits";
844
845 res = static_cast<int32_t>(decoded);
846 offset += encodingSize;
847 return res;
848}
849
850template <>
851FailureOr<int64_t> ParserHead::parseLiteral<int64_t>() {
852 char const *error = nullptr;
853 unsigned encodingSize{0};
854 StringRef src = curHead();
855 int64_t res = llvm::decodeSLEB128(src.bytes_begin(), &encodingSize,
856 src.bytes_end(), &error);
857 if (error)
858 return emitError(getLocation(), error);
859
860 offset += encodingSize;
861 return res;
862}
863
864FailureOr<uint32_t> ParserHead::parseVectorSize() {
865 return parseLiteral<uint32_t>();
866}
867
868inline FailureOr<uint32_t> ParserHead::parseUI32() {
869 return parseLiteral<uint32_t>();
870}
871
872inline FailureOr<int64_t> ParserHead::parseI64() {
873 return parseLiteral<int64_t>();
874}
875
876#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
877void ValueStack::dump() const {
878 llvm::dbgs() << "================= Wasm ValueStack =======================\n";
879 llvm::dbgs() << "size: " << size() << "\n";
880 llvm::dbgs() << "nbFrames: " << labelLevel.size() << '\n';
881 llvm::dbgs() << "<Top>"
882 << "\n";
883 // Stack is pushed to via push_back. Therefore the top of the stack is the
884 // end of the vector. Iterate in reverse so that the first thing we print
885 // is the top of the stack.
886 auto indexGetter = [this]() {
887 size_t idx = labelLevel.size();
888 return [this, idx]() mutable -> std::optional<std::pair<size_t, size_t>> {
889 llvm::dbgs() << "IDX: " << idx << '\n';
890 if (idx == 0)
891 return std::nullopt;
892 auto frameId = idx - 1;
893 auto frameLimit = labelLevel[frameId].stackIdx;
894 idx -= 1;
895 return {{frameId, frameLimit}};
896 };
897 };
898 auto getNextFrameIndex = indexGetter();
899 auto nextFrameIdx = getNextFrameIndex();
900 size_t stackSize = size();
901 for (size_t idx = 0; idx < stackSize; ++idx) {
902 size_t actualIdx = stackSize - 1 - idx;
903 while (nextFrameIdx && (nextFrameIdx->second > actualIdx)) {
904 llvm::dbgs() << " --------------- Frame (" << nextFrameIdx->first
905 << ")\n";
906 nextFrameIdx = getNextFrameIndex();
907 }
908 llvm::dbgs() << " ";
909 values[actualIdx].dump();
910 }
911 while (nextFrameIdx) {
912 llvm::dbgs() << " --------------- Frame (" << nextFrameIdx->first << ")\n";
913 nextFrameIdx = getNextFrameIndex();
914 }
915 llvm::dbgs() << "<Bottom>"
916 << "\n";
917 llvm::dbgs() << "=========================================================\n";
918}
919#endif
920
921parsed_inst_t ValueStack::popOperands(TypeRange operandTypes, Location *opLoc) {
922 LDBG() << "Popping from ValueStack\n"
923 << " Elements(s) to pop: " << operandTypes.size() << "\n"
924 << " Current stack size: " << values.size();
925 if (operandTypes.size() > values.size())
926 return emitError(*opLoc,
927 "stack doesn't contain enough values. trying to get ")
928 << operandTypes.size() << " operands on a stack containing only "
929 << values.size() << " values";
930 size_t stackIdxOffset = values.size() - operandTypes.size();
931 SmallVector<Value> res{};
932 res.reserve(operandTypes.size());
933 for (size_t i{0}; i < operandTypes.size(); ++i) {
934 Value operand = values[i + stackIdxOffset];
935 Type stackType = operand.getType();
936 if (stackType != operandTypes[i])
937 return emitError(*opLoc, "invalid operand type on stack. expecting ")
938 << operandTypes[i] << ", value on stack is of type " << stackType;
939 LDBG() << " POP: " << operand;
940 res.push_back(operand);
941 }
942 values.resize(values.size() - operandTypes.size());
943 LDBG() << " Updated stack size: " << values.size();
944 return res;
945}
946
947LogicalResult ValueStack::pushResults(ValueRange results, Location *opLoc) {
948 LDBG() << "Pushing to ValueStack\n"
949 << " Elements(s) to push: " << results.size() << "\n"
950 << " Current stack size: " << values.size();
951 for (Value val : results) {
952 if (!isWasmValueType(val.getType()))
953 return emitError(*opLoc, "invalid value type on stack: ")
954 << val.getType();
955 LDBG() << " PUSH: " << val;
956 values.push_back(val);
957 }
958
959 LDBG() << " Updated stack size: " << values.size();
960 return success();
961}
962
963template <std::byte EndParseByte>
964parsed_inst_t ExpressionParser::parse(OpBuilder &builder,
965 UniqueByte<EndParseByte> endByte) {
966 auto res = parse(builder, ByteSequence<EndParseByte>{});
967 if (failed(res))
968 return failure();
969 return res->opResults;
970}
971
972template <std::byte... ExpressionParseEnd>
973FailureOr<ExpressionParser::ParseResultWithInfo>
974ExpressionParser::parse(OpBuilder &builder,
975 ByteSequence<ExpressionParseEnd...> parsingEndFilters) {
976 SmallVector<Value> res;
977 for (;;) {
978 currentOpLoc = parser.getLocation();
979 FailureOr<std::byte> opCode = parser.consumeByte();
980 if (failed(opCode))
981 return failure();
982 if (isValueOneOf(*opCode, parsingEndFilters))
983 return {{res, *opCode}};
984 parsed_inst_t resParsed;
985 resParsed = dispatchToInstParser(*opCode, builder);
986 if (failed(resParsed))
987 return failure();
988 std::swap(res, *resParsed);
989 if (failed(pushResults(res)))
990 return failure();
991 }
992}
993
994llvm::FailureOr<FunctionType>
995ExpressionParser::parseBlockFuncType(OpBuilder &builder) {
996 return getFuncTypeFor(builder, parser.parseBlockType(builder.getContext()));
997}
998
999template <typename OpToCreate>
1000parsed_inst_t
1001ExpressionParser::ExprParserProxy::parseBlockLikeOp(OpBuilder &builder) {
1002 auto opLoc = getCurrentOpLoc();
1003 auto funcType = parseBlockFuncType(builder);
1004 if (failed(funcType))
1005 return failure();
1006
1007 auto inputTypes = funcType->getInputs();
1008 auto inputOps = popOperands(inputTypes);
1009 if (failed(inputOps))
1010 return failure();
1011
1012 Block *curBlock = builder.getBlock();
1013 Region *curRegion = curBlock->getParent();
1014 auto resTypes = funcType->getResults();
1015 llvm::SmallVector<Location> locations{};
1016 locations.resize(resTypes.size(), getCurrentOpLoc());
1017 auto *successor =
1018 builder.createBlock(curRegion, curRegion->end(), resTypes, locations);
1019 builder.setInsertionPointToEnd(curBlock);
1020 auto blockOp =
1021 OpToCreate::create(builder, getCurrentOpLoc(), *inputOps, successor);
1022 auto *blockBody = blockOp.createBlock();
1023 if (failed(parseBlockContent(builder, blockBody, resTypes, opLoc, blockOp)))
1024 return failure();
1025 builder.setInsertionPointToStart(successor);
1026 return {ValueRange{successor->getArguments()}};
1027}
1028
1029parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::block>,
1030 OpBuilder &builder,
1031 ExpressionParser::ExprParserProxy &exprParser) {
1032 return exprParser.parseBlockLikeOp<BlockOp>(builder);
1033}
1034
1035parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::loop>,
1036 OpBuilder &builder,
1037 ExpressionParser::ExprParserProxy &exprParser) {
1038 return exprParser.parseBlockLikeOp<LoopOp>(builder);
1039}
1040
1041parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::ifOpCode>,
1042 OpBuilder &builder,
1043 ExpressionParser::ExprParserProxy &exprParser) {
1044 auto opLoc = exprParser.getCurrentOpLoc();
1045 auto funcType = exprParser.parseBlockFuncType(builder);
1046 if (failed(funcType))
1047 return failure();
1048
1049 LDBG() << "Parsing an if instruction of type " << *funcType;
1050 auto inputTypes = funcType->getInputs();
1051 auto conditionValue = exprParser.popOperands(builder.getI32Type());
1052 if (failed(conditionValue))
1053 return failure();
1054 auto inputOps = exprParser.popOperands(inputTypes);
1055 if (failed(inputOps))
1056 return failure();
1057
1058 Block *curBlock = builder.getBlock();
1059 Region *curRegion = curBlock->getParent();
1060 auto resTypes = funcType->getResults();
1061 llvm::SmallVector<Location> locations{};
1062 locations.resize(resTypes.size(), exprParser.getCurrentOpLoc());
1063 auto *successor =
1064 builder.createBlock(curRegion, curRegion->end(), resTypes, locations);
1065 builder.setInsertionPointToEnd(curBlock);
1066 auto ifOp = IfOp::create(builder, exprParser.getCurrentOpLoc(),
1067 conditionValue->front(), *inputOps, successor);
1068 auto *ifEntryBlock = ifOp.createIfBlock();
1069 constexpr auto ifElseFilter =
1070 ByteSequence<WasmBinaryEncoding::endByte,
1072 auto parseIfRes = exprParser.parseBlockContent(
1073 builder, ifEntryBlock, resTypes, opLoc, ifOp, ifElseFilter);
1074 if (failed(parseIfRes))
1075 return failure();
1076 if (*parseIfRes == WasmBinaryEncoding::OpCode::elseOpCode) {
1077 LDBG() << " else block is present.";
1078 Block *elseEntryBlock = ifOp.createElseBlock();
1079 auto parseElseRes = exprParser.parseBlockContent(builder, elseEntryBlock,
1080 resTypes, opLoc, ifOp);
1081 if (failed(parseElseRes))
1082 return failure();
1083 }
1084 builder.setInsertionPointToStart(successor);
1085 return {ValueRange{successor->getArguments()}};
1086}
1087
1088parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::branchIf>,
1089 OpBuilder &builder,
1090 ExpressionParser::ExprParserProxy &exprParser) {
1091 auto level = exprParser.parser().parseLiteral<uint32_t>();
1092 if (failed(level))
1093 return failure();
1094 Block *curBlock = builder.getBlock();
1095 Region *curRegion = curBlock->getParent();
1096 auto sip = builder.saveInsertionPoint();
1097 Block *elseBlock = builder.createBlock(curRegion, curRegion->end());
1098 auto condition = exprParser.popOperands(builder.getI32Type());
1099 if (failed(condition))
1100 return failure();
1101 builder.restoreInsertionPoint(sip);
1102 auto targetOp =
1103 LabelBranchingOpInterface::getTargetOpFromBlock(curBlock, *level);
1104 if (failed(targetOp))
1105 return failure();
1106 auto inputTypes = targetOp->getLabelTarget()->getArgumentTypes();
1107 auto branchArgs = exprParser.popOperands(inputTypes);
1108 if (failed(branchArgs))
1109 return failure();
1110 BranchIfOp::create(builder, exprParser.getCurrentOpLoc(), condition->front(),
1111 builder.getUI32IntegerAttr(*level), *branchArgs,
1112 elseBlock);
1113 builder.setInsertionPointToStart(elseBlock);
1114 return {*branchArgs};
1115}
1116
1117parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::call>,
1118 OpBuilder &builder,
1119 ExpressionParser::ExprParserProxy &exprParser) {
1120 auto loc = exprParser.getCurrentOpLoc();
1121 auto funcIdx = exprParser.parser().parseLiteral<uint32_t>();
1122 if (failed(funcIdx))
1123 return failure();
1124 if (*funcIdx >= exprParser.symbols().funcSymbols.size())
1125 return emitError(loc, "Invalid function index: ") << *funcIdx;
1126 auto callee = exprParser.symbols().funcSymbols[*funcIdx];
1127 llvm::ArrayRef<Type> inTypes = callee.functionType.getInputs();
1128 llvm::ArrayRef<Type> resTypes = callee.functionType.getResults();
1129 parsed_inst_t inOperands = exprParser.popOperands(inTypes);
1130 if (failed(inOperands))
1131 return failure();
1132 auto callOp =
1133 FuncCallOp::create(builder, loc, resTypes, callee.symbol, *inOperands);
1134 return {callOp.getResults()};
1135}
1136
1137parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::localGet>,
1138 OpBuilder &builder,
1139 ExpressionParser::ExprParserProxy &exprParser) {
1140 FailureOr<uint32_t> id = exprParser.parser().parseLiteral<uint32_t>();
1141 Location instLoc = exprParser.getCurrentOpLoc();
1142 if (failed(id))
1143 return failure();
1144 if (*id >= exprParser.locals().size())
1145 return emitError(instLoc, "invalid local index. function has ")
1146 << exprParser.locals().size()
1147 << " accessible locals, received index " << *id;
1148 return {{LocalGetOp::create(builder, instLoc, exprParser.locals()[*id])
1149 .getResult()}};
1150}
1151
1152parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::globalGet>,
1153 OpBuilder &builder,
1154 ExpressionParser::ExprParserProxy &exprParser) {
1155 FailureOr<uint32_t> id = exprParser.parser().parseLiteral<uint32_t>();
1156 Location instLoc = exprParser.getCurrentOpLoc();
1157 if (failed(id))
1158 return failure();
1159 if (*id >= exprParser.symbols().globalSymbols.size())
1160 return emitError(instLoc, "invalid global index. function has ")
1161 << exprParser.symbols().globalSymbols.size()
1162 << " accessible globals, received index " << *id;
1163 GlobalSymbolRefContainer globalVar = exprParser.symbols().globalSymbols[*id];
1164 auto globalOp = GlobalGetOp::create(builder, instLoc, globalVar.globalType,
1165 globalVar.symbol);
1166
1167 return {{globalOp.getResult()}};
1168}
1169
1170template <typename OpToCreate>
1171parsed_inst_t ExpressionParser::parseSetOrTee(OpBuilder &builder) {
1172 FailureOr<uint32_t> id = parser.parseLiteral<uint32_t>();
1173 if (failed(id))
1174 return failure();
1175 if (*id >= locals.size())
1176 return emitError(*currentOpLoc, "invalid local index. function has ")
1177 << locals.size() << " accessible locals, received index " << *id;
1178 if (valueStack.empty())
1179 return emitError(
1180 *currentOpLoc,
1181 "invalid stack access, trying to access a value on an empty stack");
1182
1183 parsed_inst_t poppedOp = popOperands(locals[*id].getType().getElementType());
1184 if (failed(poppedOp))
1185 return failure();
1186 return {
1187 OpToCreate::create(builder, *currentOpLoc, locals[*id], poppedOp->front())
1188 ->getResults()};
1189}
1190
1191parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::localSet>,
1192 OpBuilder &builder,
1193 ExpressionParser::ExprParserProxy &exprParser) {
1194 return exprParser.parseSetOrTee<LocalSetOp>(builder);
1195}
1196
1197parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::localTee>,
1198 OpBuilder &builder,
1199 ExpressionParser::ExprParserProxy &exprParser) {
1200 return exprParser.parseSetOrTee<LocalTeeOp>(builder);
1201}
1202
1203template <typename T>
1204inline Type buildLiteralType(OpBuilder &);
1205
1206template <>
1207inline Type buildLiteralType<int32_t>(OpBuilder &builder) {
1208 return builder.getI32Type();
1209}
1210
1211template <>
1212inline Type buildLiteralType<int64_t>(OpBuilder &builder) {
1213 return builder.getI64Type();
1214}
1215
1216template <>
1217[[maybe_unused]] inline Type buildLiteralType<uint32_t>(OpBuilder &builder) {
1218 return builder.getI32Type();
1219}
1220
1221template <>
1222[[maybe_unused]] inline Type buildLiteralType<uint64_t>(OpBuilder &builder) {
1223 return builder.getI64Type();
1224}
1225
1226template <>
1227inline Type buildLiteralType<float>(OpBuilder &builder) {
1228 return builder.getF32Type();
1229}
1230
1231template <>
1232inline Type buildLiteralType<double>(OpBuilder &builder) {
1233 return builder.getF64Type();
1234}
1235
1236template <typename ValT,
1237 typename E = std::enable_if_t<std::is_arithmetic_v<ValT>>>
1238struct AttrHolder;
1239
1240template <typename ValT>
1241struct AttrHolder<ValT, std::enable_if_t<std::is_integral_v<ValT>>> {
1242 using type = IntegerAttr;
1243};
1244
1245template <typename ValT>
1246struct AttrHolder<ValT, std::enable_if_t<std::is_floating_point_v<ValT>>> {
1247 using type = FloatAttr;
1248};
1249
1250template <typename ValT>
1251using attr_holder_t = typename AttrHolder<ValT>::type;
1252
1253template <typename ValT,
1254 typename EnableT = std::enable_if_t<std::is_arithmetic_v<ValT>>>
1255attr_holder_t<ValT> buildLiteralAttr(OpBuilder &builder, ValT val) {
1256 return attr_holder_t<ValT>::get(buildLiteralType<ValT>(builder), val);
1257}
1258
1259template <typename valueT>
1260parsed_inst_t ExpressionParser::ExprParserProxy::parseConstInst(
1261 OpBuilder &builder, std::enable_if_t<std::is_arithmetic_v<valueT>> *) {
1262 auto parsedConstant = parser().parseLiteral<valueT>();
1263 if (failed(parsedConstant))
1264 return failure();
1265 auto constOp =
1266 ConstOp::create(builder, getCurrentOpLoc(),
1267 buildLiteralAttr<valueT>(builder, *parsedConstant));
1268 return {{constOp.getResult()}};
1269}
1270
1271parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::constI32>,
1272 OpBuilder &builder,
1273 ExpressionParser::ExprParserProxy &exprParser) {
1274 return exprParser.parseConstInst<int32_t>(builder);
1275}
1276
1277parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::constI64>,
1278 OpBuilder &builder,
1279 ExpressionParser::ExprParserProxy &exprParser) {
1280 return exprParser.parseConstInst<int64_t>(builder);
1281}
1282
1283parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::constFP32>,
1284 OpBuilder &builder,
1285 ExpressionParser::ExprParserProxy &exprParser) {
1286 return exprParser.parseConstInst<float>(builder);
1287}
1288
1289parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::constFP64>,
1290 OpBuilder &builder,
1291 ExpressionParser::ExprParserProxy &exprParser) {
1292 return exprParser.parseConstInst<double>(builder);
1293}
1294
1295template <typename opcode, typename valueType, unsigned int numOperands>
1296inline parsed_inst_t ExpressionParser::ExprParserProxy::buildNumericOp(
1297 OpBuilder &builder, std::enable_if_t<std::is_arithmetic_v<valueType>> *) {
1298 auto ty = buildLiteralType<valueType>(builder);
1299 LDBG() << "*** buildNumericOp: numOperands = " << numOperands
1300 << ", type = " << ty << " ***";
1301 auto tysToPop = SmallVector<Type, numOperands>();
1302 tysToPop.resize(numOperands);
1303 llvm::fill(tysToPop, ty);
1304 auto operands = popOperands(tysToPop);
1305 if (failed(operands))
1306 return failure();
1307 auto op = opcode::create(builder, getCurrentOpLoc(), *operands).getResult();
1308 LDBG() << "Built operation: " << op;
1309 return {{op}};
1310}
1311
1312// Convenience macro for generating numerical operations.
1313#define BUILD_NUMERIC_OP(OP_NAME, N_ARGS, PREFIX, SUFFIX, TYPE) \
1314 inline parsed_inst_t parse( \
1315 OpCode<WasmBinaryEncoding::OpCode::PREFIX##SUFFIX>, OpBuilder &builder, \
1316 ExpressionParser::ExprParserProxy &exprParser) { \
1317 return exprParser.buildNumericOp<OP_NAME, TYPE, N_ARGS>(builder); \
1318 }
1319
1320// Macro to define binops that only support integer types.
1321#define BUILD_NUMERIC_BINOP_INT(OP_NAME, PREFIX) \
1322 BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, I32, int32_t) \
1323 BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, I64, int64_t)
1324
1325// Macro to define binops that only support floating point types.
1326#define BUILD_NUMERIC_BINOP_FP(OP_NAME, PREFIX) \
1327 BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, F32, float) \
1328 BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, F64, double)
1329
1330// Macro to define binops that support both floating point and integer types.
1331#define BUILD_NUMERIC_BINOP_INTFP(OP_NAME, PREFIX) \
1332 BUILD_NUMERIC_BINOP_INT(OP_NAME, PREFIX) \
1333 BUILD_NUMERIC_BINOP_FP(OP_NAME, PREFIX)
1334
1335// Macro to implement unary ops that only support integers.
1336#define BUILD_NUMERIC_UNARY_OP_INT(OP_NAME, PREFIX) \
1337 BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, I32, int32_t) \
1338 BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, I64, int64_t)
1339
1340// Macro to implement unary ops that support integer and floating point types.
1341#define BUILD_NUMERIC_UNARY_OP_FP(OP_NAME, PREFIX) \
1342 BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, F32, float) \
1343 BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, F64, double)
1344
1345BUILD_NUMERIC_BINOP_FP(CopySignOp, copysign)
1347BUILD_NUMERIC_BINOP_FP(GeOp, ge)
1348BUILD_NUMERIC_BINOP_FP(GtOp, gt)
1349BUILD_NUMERIC_BINOP_FP(LeOp, le)
1350BUILD_NUMERIC_BINOP_FP(LtOp, lt)
1353BUILD_NUMERIC_BINOP_INT(AndOp, and)
1354BUILD_NUMERIC_BINOP_INT(DivSIOp, divS)
1355BUILD_NUMERIC_BINOP_INT(DivUIOp, divU)
1356BUILD_NUMERIC_BINOP_INT(GeSIOp, geS)
1357BUILD_NUMERIC_BINOP_INT(GeUIOp, geU)
1358BUILD_NUMERIC_BINOP_INT(GtSIOp, gtS)
1359BUILD_NUMERIC_BINOP_INT(GtUIOp, gtU)
1360BUILD_NUMERIC_BINOP_INT(LeSIOp, leS)
1361BUILD_NUMERIC_BINOP_INT(LeUIOp, leU)
1362BUILD_NUMERIC_BINOP_INT(LtSIOp, ltS)
1363BUILD_NUMERIC_BINOP_INT(LtUIOp, ltU)
1365BUILD_NUMERIC_BINOP_INT(RemSIOp, remS)
1366BUILD_NUMERIC_BINOP_INT(RemUIOp, remU)
1367BUILD_NUMERIC_BINOP_INT(RotlOp, rotl)
1368BUILD_NUMERIC_BINOP_INT(RotrOp, rotr)
1369BUILD_NUMERIC_BINOP_INT(ShLOp, shl)
1370BUILD_NUMERIC_BINOP_INT(ShRSOp, shrS)
1371BUILD_NUMERIC_BINOP_INT(ShRUOp, shrU)
1372BUILD_NUMERIC_BINOP_INT(XOrOp, xor)
1377BUILD_NUMERIC_BINOP_INTFP(SubOp, sub)
1378BUILD_NUMERIC_UNARY_OP_FP(AbsOp, abs)
1379BUILD_NUMERIC_UNARY_OP_FP(CeilOp, ceil)
1380BUILD_NUMERIC_UNARY_OP_FP(FloorOp, floor)
1381BUILD_NUMERIC_UNARY_OP_FP(NearestOp, nearest)
1382BUILD_NUMERIC_UNARY_OP_FP(NegOp, neg)
1383BUILD_NUMERIC_UNARY_OP_FP(SqrtOp, sqrt)
1384BUILD_NUMERIC_UNARY_OP_FP(TruncOp, trunc)
1388BUILD_NUMERIC_UNARY_OP_INT(PopCntOp, popcnt)
1389
1390// Don't need these anymore so let's undef them.
1391#undef BUILD_NUMERIC_BINOP_FP
1392#undef BUILD_NUMERIC_BINOP_INT
1393#undef BUILD_NUMERIC_BINOP_INTFP
1394#undef BUILD_NUMERIC_UNARY_OP_FP
1395#undef BUILD_NUMERIC_UNARY_OP_INT
1396#undef BUILD_NUMERIC_OP
1397#undef BUILD_NUMERIC_CAST_OP
1398
1399template <typename opType, typename inputType, typename outputType,
1400 typename... extraArgsT>
1401inline parsed_inst_t
1402ExpressionParser::ExprParserProxy::buildConvertOp(OpBuilder &builder,
1403 extraArgsT... extraArgs) {
1404 static_assert(std::is_arithmetic_v<inputType>,
1405 "InputType should be an arithmetic type");
1406 static_assert(std::is_arithmetic_v<outputType>,
1407 "OutputType should be an arithmetic type");
1408 auto intype = buildLiteralType<inputType>(builder);
1409 auto outType = buildLiteralType<outputType>(builder);
1410 auto operand = popOperands(intype);
1411 if (failed(operand))
1412 return failure();
1413 auto op = opType::create(builder, getCurrentOpLoc(), outType,
1414 operand->front(), extraArgs...);
1415 LDBG() << "Built operation: " << op;
1416 return {{op.getResult()}};
1417}
1418
1419parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::demoteF64ToF32>,
1420 OpBuilder &builder,
1421 ExpressionParser::ExprParserProxy &exprParser) {
1422 return exprParser.buildConvertOp<DemoteOp, double, float>(builder);
1423}
1424
1425parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::wrap>,
1426 OpBuilder &builder,
1427 ExpressionParser::ExprParserProxy &exprParser) {
1428 return exprParser.buildConvertOp<WrapOp, int64_t, int32_t>(builder);
1429}
1430
1431#define BUILD_CONVERSION_OP(IN_T, OUT_T, SOURCE_OP, TARGET_OP) \
1432 inline parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::SOURCE_OP>, \
1433 OpBuilder &builder, \
1434 ExpressionParser::ExprParserProxy &exprParser) { \
1435 return exprParser.buildConvertOp<TARGET_OP, IN_T, OUT_T>(builder); \
1436 }
1437
1438#define BUILD_CONVERT_OP_FOR(DEST_T, WIDTH) \
1439 BUILD_CONVERSION_OP(uint32_t, DEST_T, convertUI32F##WIDTH, ConvertUOp) \
1440 BUILD_CONVERSION_OP(int32_t, DEST_T, convertSI32F##WIDTH, ConvertSOp) \
1441 BUILD_CONVERSION_OP(uint64_t, DEST_T, convertUI64F##WIDTH, ConvertUOp) \
1442 BUILD_CONVERSION_OP(int64_t, DEST_T, convertSI64F##WIDTH, ConvertSOp)
1443
1444BUILD_CONVERT_OP_FOR(float, 32)
1445BUILD_CONVERT_OP_FOR(double, 64)
1446
1447#undef BUILD_CONVERT_OP_FOR
1448
1449#define BUILD_TRUNC_OP_FOR(SRC_T, WIDTH) \
1450 BUILD_CONVERSION_OP(SRC_T, int32_t, truncSI32F##WIDTH, TruncSIOp) \
1451 BUILD_CONVERSION_OP(SRC_T, uint32_t, truncUI32F##WIDTH, TruncUIOp) \
1452 BUILD_CONVERSION_OP(SRC_T, int64_t, truncSI64F##WIDTH, TruncSIOp) \
1453 BUILD_CONVERSION_OP(SRC_T, uint64_t, truncUI64F##WIDTH, TruncUIOp)
1454
1455BUILD_TRUNC_OP_FOR(float, 32)
1456BUILD_TRUNC_OP_FOR(double, 64)
1457
1458#undef BUILD_TRUNC_OP_FOR
1459
1460BUILD_CONVERSION_OP(int32_t, int64_t, extendS, ExtendSI32Op)
1461BUILD_CONVERSION_OP(int32_t, int64_t, extendU, ExtendUI32Op)
1462
1463#undef BUILD_CONVERSION_OP
1464
1465parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::saturatedTruncate>,
1466 OpBuilder &builder,
1467 ExpressionParser::ExprParserProxy &parser,
1468 std::uint32_t subOpCode) {
1469 if (subOpCode > 7)
1470 return emitError(parser.getCurrentOpLoc())
1471 << "invalid sub-opcode for trunc_saturate: " << subOpCode;
1472 LDBG() << "Sub subOpcode for operation: " << subOpCode;
1473 bool isDestUnsigned = (subOpCode & 1) != 0;
1474 bool isSrcF64 = (subOpCode & 2) != 0;
1475 bool isDestI64 = (subOpCode & 4) != 0;
1476
1477 Type srcType = isSrcF64 ? builder.getF64Type() : builder.getF32Type();
1478 Type destType = isDestI64 ? builder.getI64Type() : builder.getI32Type();
1479 auto srcOp = parser.popOperands(srcType);
1480 if (failed(srcOp))
1481 return failure();
1482 Operation *op = isDestUnsigned
1483 ? TruncSatUIOp::create(builder, parser.getCurrentOpLoc(),
1484 destType, srcOp->front())
1485 : TruncSatSIOp::create(builder, parser.getCurrentOpLoc(),
1486 destType, srcOp->front());
1487 LDBG() << "Built operation: " << op;
1488 return {{op->getResult(0)}};
1489}
1490
1491#define BUILD_SLICE_EXTEND_PARSER(IT_WIDTH, EXTRACT_WIDTH) \
1492 parsed_inst_t parse( \
1493 OpCode<WasmBinaryEncoding::OpCode::extendI##IT_WIDTH##EXTRACT_WIDTH##S>, \
1494 OpBuilder &builder, ExpressionParser::ExprParserProxy &exprParser) { \
1495 using inout_t = int##IT_WIDTH##_t; \
1496 auto attr = builder.getUI32IntegerAttr(EXTRACT_WIDTH); \
1497 return exprParser.buildConvertOp<ExtendLowBitsSOp, inout_t, inout_t>( \
1498 builder, attr); \
1499 }
1500
1506
1507#undef BUILD_SLICE_EXTEND_PARSER
1508
1509parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::promoteF32ToF64>,
1510 OpBuilder &builder,
1511 ExpressionParser::ExprParserProxy &exprParser) {
1512 return exprParser.buildConvertOp<PromoteOp, float, double>(builder);
1513}
1514
1515#define BUILD_REINTERPRET_PARSER(WIDTH, FP_TYPE) \
1516 inline parsed_inst_t parse( \
1517 OpCode<WasmBinaryEncoding::OpCode::reinterpretF##WIDTH##AsI##WIDTH>, \
1518 OpBuilder &builder, ExpressionParser::ExprParserProxy &exprParser) { \
1519 return exprParser.buildConvertOp<ReinterpretOp, FP_TYPE, int##WIDTH##_t>( \
1520 builder); \
1521 } \
1522 \
1523 inline parsed_inst_t parse( \
1524 OpCode<WasmBinaryEncoding::OpCode::reinterpretI##WIDTH##AsF##WIDTH>, \
1525 OpBuilder &builder, ExpressionParser::ExprParserProxy &exprParser) { \
1526 return exprParser.buildConvertOp<ReinterpretOp, int##WIDTH##_t, FP_TYPE>( \
1527 builder); \
1528 }
1529
1530BUILD_REINTERPRET_PARSER(32, float)
1531BUILD_REINTERPRET_PARSER(64, double)
1532
1533#undef BUILD_REINTERPRET_PARSER
1534
1535class InstDispatcher {
1536private:
1537 template <std::byte OpCode, typename = void>
1538 struct HasParserRegistered : std::false_type {};
1539
1540 template <std::byte opCode>
1541 struct HasParserRegistered<
1542 opCode, std::void_t<decltype(parse(
1543 std::declval<OpCode<opCode>>(), std::declval<OpBuilder &>(),
1544 std::declval<ExpressionParser::ExprParserProxy &>()))>>
1545 : std::true_type {};
1546
1547 template <std::byte opCode>
1548 static constexpr bool hasParseredRegistered =
1549 HasParserRegistered<opCode>::value;
1550
1551 template <std::byte OpCode, typename = void>
1552 struct HasParserWthSubOpCodeRegistered : std::false_type {};
1553
1554 template <std::byte opCode>
1555 struct HasParserWthSubOpCodeRegistered<
1556 opCode, std::void_t<decltype(parse(
1557 std::declval<OpCode<opCode>>(), std::declval<OpBuilder &>(),
1558 std::declval<ExpressionParser::ExprParserProxy &>(),
1559 std::declval<std::uint32_t>()))>> : std::true_type {
1560 static_assert(!hasParseredRegistered<opCode>,
1561 "plain parser and parser with sub-opcode can't be registered "
1562 "for the same opcode");
1563 };
1564
1565 template <std::byte opCode>
1566 static constexpr bool hasParseredWithSubOpCodeRegistered =
1567 HasParserWthSubOpCodeRegistered<opCode>::value;
1568
1569 using dispatch_t = parsed_inst_t (*)(OpBuilder &,
1570 ExpressionParser::ExprParserProxy &);
1571
1572 static inline parsed_inst_t
1573 unreachableHandler(OpBuilder &,
1574 ExpressionParser::ExprParserProxy &expressionParser) {
1575 llvm_unreachable("Failure in opcode parser dispatch logic.");
1576 return mlir::failure();
1577 }
1578
1579 template <std::byte opCode>
1580 static parsed_inst_t
1581 forwardToParser(OpBuilder &builder,
1582 ExpressionParser::ExprParserProxy &exprParser) {
1583 return parse(OpCode<opCode>{}, builder, exprParser);
1584 }
1585
1586 template <std::byte opCode>
1587 static parsed_inst_t
1588 forwardToParserWithSubOpCode(OpBuilder &builder,
1589 ExpressionParser::ExprParserProxy &exprParser) {
1590 auto loc = exprParser.getCurrentOpLoc();
1591 auto subOpCode = exprParser.parser().parseLiteral<std::uint32_t>();
1592 if (failed(subOpCode))
1593 return emitError(loc) << "expecting sub opcode for opcode "
1594 << static_cast<uint8_t>(opCode);
1595 return parse(OpCode<opCode>{}, builder, exprParser, subOpCode.value());
1596 }
1597
1598 template <std::byte opCode>
1599 static constexpr dispatch_t getHandlerForOpCode() {
1600 if constexpr (hasParseredRegistered<opCode>)
1601 return forwardToParser<opCode>;
1602 else if constexpr (hasParseredWithSubOpCodeRegistered<opCode>)
1603 return forwardToParserWithSubOpCode<opCode>;
1604 else
1605 return unreachableHandler;
1606 }
1607
1608private:
1609 static inline parsed_inst_t
1610 invalidOpcodeDiag(OpBuilder &,
1611 ExpressionParser::ExprParserProxy &expressionParser,
1612 std::byte opCode) {
1613 return emitError(expressionParser.getCurrentOpLoc(),
1614 "unknown instruction opcode: ")
1615 << static_cast<int>(opCode);
1616 }
1617
1618 template <std::byte... opCodes>
1619 static inline parsed_inst_t
1620 dispatchImpl(std::byte opCode, OpBuilder &builder,
1621 ExpressionParser::ExprParserProxy &exprParser,
1622 ByteSequence<opCodes...>) {
1623 static constexpr std::array<bool, 256> opcodeValidityMap{
1624 (hasParseredRegistered<opCodes> ||
1625 hasParseredWithSubOpCodeRegistered<opCodes>)...};
1626 static constexpr std::array<dispatch_t, 256> dispatchTable{
1627 getHandlerForOpCode<opCodes>()...};
1628 if (opcodeValidityMap[static_cast<size_t>(opCode)]) {
1629 return dispatchTable[static_cast<size_t>(opCode)](builder, exprParser);
1630 }
1631 return invalidOpcodeDiag(builder, exprParser, opCode);
1632 }
1633
1634public:
1635 ///
1636 /// @brief dispatch control flow to the sub parser registered for opCode in
1637 /// `ParserRegistry`
1638 ///
1639 /// @param opCode opCode of the instruction to be Parsed
1640 /// @param builder builder that will be passed to the parser
1641 /// @param exprParser the generic parser passed to the sub parser
1642 ///
1643 /// @return the result of the parser or an error if there is no parser
1644 /// registered for the opcode (emits a diagnostic)
1645 static parsed_inst_t dispatch(std::byte opCode, OpBuilder &builder,
1646 ExpressionParser::ExprParserProxy &exprParser) {
1647 return dispatchImpl(opCode, builder, exprParser, all8bitsBytes);
1648 }
1649};
1650
1651inline parsed_inst_t
1652ExpressionParser::dispatchToInstParser(std::byte opCode, OpBuilder &builder) {
1653 ExpressionParser::ExprParserProxy exprParser{*this};
1654 return InstDispatcher::dispatch(opCode, builder, exprParser);
1655}
1656class WasmBinaryParser {
1657private:
1658 struct SectionRegistry {
1659 using section_location_t = StringRef;
1660
1661 std::array<SmallVector<section_location_t>, highestWasmSectionID + 1>
1662 registry;
1663
1664 template <WasmSectionType SecType>
1665 std::conditional_t<sectionShouldBeUnique(SecType),
1666 std::optional<section_location_t>,
1667 ArrayRef<section_location_t>>
1668 getContentForSection() const {
1669 constexpr auto idx = static_cast<size_t>(SecType);
1670 if constexpr (sectionShouldBeUnique(SecType)) {
1671 return registry[idx].empty() ? std::nullopt
1672 : std::make_optional(registry[idx][0]);
1673 } else {
1674 return registry[idx];
1675 }
1676 }
1677
1678 bool hasSection(WasmSectionType secType) const {
1679 return !registry[static_cast<size_t>(secType)].empty();
1680 }
1681
1682 ///
1683 /// @returns success if registration valid, failure in case registration
1684 /// can't be done (if another section of same type already exist and this
1685 /// section type should only be present once)
1686 ///
1687 LogicalResult registerSection(WasmSectionType secType,
1688 section_location_t location, Location loc) {
1689 if (sectionShouldBeUnique(secType) && hasSection(secType))
1690 return emitError(loc,
1691 "trying to add a second instance of unique section");
1692
1693 registry[static_cast<size_t>(secType)].push_back(location);
1694 emitRemark(loc, "Adding section with section ID ")
1695 << static_cast<uint8_t>(secType);
1696 return success();
1697 }
1698
1699 LogicalResult populateFromBody(ParserHead ph) {
1700 while (!ph.end()) {
1701 FileLineColLoc sectionLoc = ph.getLocation();
1702 FailureOr<WasmSectionType> secType = ph.parseWasmSectionType();
1703 if (failed(secType))
1704 return failure();
1705
1706 FailureOr<uint32_t> secSizeParsed = ph.parseLiteral<uint32_t>();
1707 if (failed(secSizeParsed))
1708 return failure();
1709
1710 uint32_t secSize = *secSizeParsed;
1711 FailureOr<StringRef> sectionContent = ph.consumeNBytes(secSize);
1712 if (failed(sectionContent))
1713 return failure();
1714
1715 LogicalResult registration =
1716 registerSection(*secType, *sectionContent, sectionLoc);
1717
1718 if (failed(registration))
1719 return failure();
1720 }
1721 return success();
1722 }
1723 };
1724
1725 auto getLocation(int offset = 0) const {
1726 return FileLineColLoc::get(srcName, 0, offset);
1727 }
1728
1729 template <WasmSectionType>
1730 LogicalResult parseSectionItem(ParserHead &, size_t);
1731
1732 template <WasmSectionType section>
1733 LogicalResult parseSection() {
1734 auto secName = std::string{wasmSectionName<section>};
1735 auto sectionNameAttr =
1736 StringAttr::get(ctx, srcName.strref() + ":" + secName + "-SECTION");
1737 unsigned offset = 0;
1738 auto getLocation = [sectionNameAttr, &offset]() {
1739 return FileLineColLoc::get(sectionNameAttr, 0, offset);
1740 };
1741 auto secContent = registry.getContentForSection<section>();
1742 if (!secContent) {
1743 LDBG() << secName << " section is not present in file.";
1744 return success();
1745 }
1746
1747 auto secSrc = secContent.value();
1748 ParserHead ph{secSrc, sectionNameAttr};
1749 FailureOr<uint32_t> nElemsParsed = ph.parseVectorSize();
1750 if (failed(nElemsParsed))
1751 return failure();
1752 uint32_t nElems = *nElemsParsed;
1753 LDBG() << "starting to parse " << nElems << " items for section "
1754 << secName;
1755 for (size_t i = 0; i < nElems; ++i) {
1756 if (failed(parseSectionItem<section>(ph, i)))
1757 return failure();
1758 }
1759
1760 if (!ph.end())
1761 return emitError(getLocation(), "unparsed garbage at end of section ")
1762 << secName;
1763 return success();
1764 }
1765
1766 /// Handles the registration of a function import
1767 LogicalResult visitImport(Location loc, StringRef moduleName,
1768 StringRef importName, TypeIdxRecord tid) {
1769 using llvm::Twine;
1770 if (tid.id >= symbols.moduleFuncTypes.size())
1771 return emitError(loc, "invalid type id: ")
1772 << tid.id << ". Only " << symbols.moduleFuncTypes.size()
1773 << " type registrations";
1774 FunctionType type = symbols.moduleFuncTypes[tid.id];
1775 std::string symbol = symbols.getNewFuncSymbolName();
1776 auto funcOp = FuncImportOp::create(builder, loc, symbol, moduleName,
1777 importName, type);
1778 symbols.funcSymbols.push_back({{FlatSymbolRefAttr::get(funcOp)}, type});
1779 return funcOp.verify();
1780 }
1781
1782 /// Handles the registration of a memory import
1783 LogicalResult visitImport(Location loc, StringRef moduleName,
1784 StringRef importName, LimitType limitType) {
1785 std::string symbol = symbols.getNewMemorySymbolName();
1786 auto memOp = MemImportOp::create(builder, loc, symbol, moduleName,
1787 importName, limitType);
1788 symbols.memSymbols.push_back({FlatSymbolRefAttr::get(memOp)});
1789 return memOp.verify();
1790 }
1791
1792 /// Handles the registration of a table import
1793 LogicalResult visitImport(Location loc, StringRef moduleName,
1794 StringRef importName, TableType tableType) {
1795 std::string symbol = symbols.getNewTableSymbolName();
1796 auto tableOp = TableImportOp::create(builder, loc, symbol, moduleName,
1797 importName, tableType);
1798 symbols.tableSymbols.push_back({FlatSymbolRefAttr::get(tableOp)});
1799 return tableOp.verify();
1800 }
1801
1802 /// Handles the registration of a global variable import
1803 LogicalResult visitImport(Location loc, StringRef moduleName,
1804 StringRef importName, GlobalTypeRecord globalType) {
1805 std::string symbol = symbols.getNewGlobalSymbolName();
1806 auto giOp =
1807 GlobalImportOp::create(builder, loc, symbol, moduleName, importName,
1808 globalType.type, globalType.isMutable);
1809 symbols.globalSymbols.push_back(
1810 {{FlatSymbolRefAttr::get(giOp)}, giOp.getType()});
1811 return giOp.verify();
1812 }
1813
1814 // Detect occurence of errors
1815 LogicalResult peekDiag(Diagnostic &diag) {
1816 if (diag.getSeverity() == DiagnosticSeverity::Error)
1817 isValid = false;
1818 return failure();
1819 }
1820
1821public:
1822 WasmBinaryParser(llvm::SourceMgr &sourceMgr, MLIRContext *ctx)
1823 : builder{ctx}, ctx{ctx} {
1825 [this](Diagnostic &diag) { return peekDiag(diag); });
1827 if (sourceMgr.getNumBuffers() != 1) {
1828 emitError(UnknownLoc::get(ctx), "one source file should be provided");
1829 return;
1830 }
1831 uint32_t sourceBufId = sourceMgr.getMainFileID();
1832 StringRef source = sourceMgr.getMemoryBuffer(sourceBufId)->getBuffer();
1833 srcName = StringAttr::get(
1834 ctx, sourceMgr.getMemoryBuffer(sourceBufId)->getBufferIdentifier());
1835
1836 auto parser = ParserHead{source, srcName};
1837 auto const wasmHeader = StringRef{"\0asm", 4};
1838 FileLineColLoc magicLoc = parser.getLocation();
1839 FailureOr<StringRef> magic = parser.consumeNBytes(wasmHeader.size());
1840 if (failed(magic) || magic->compare(wasmHeader)) {
1841 emitError(magicLoc, "source file does not contain valid Wasm header");
1842 return;
1843 }
1844 auto const expectedVersionString = StringRef{"\1\0\0\0", 4};
1845 FileLineColLoc versionLoc = parser.getLocation();
1846 FailureOr<StringRef> version =
1847 parser.consumeNBytes(expectedVersionString.size());
1848 if (failed(version))
1849 return;
1850 if (version->compare(expectedVersionString)) {
1851 emitError(versionLoc,
1852 "unsupported Wasm version. only version 1 is supported");
1853 return;
1854 }
1855 LogicalResult fillRegistry = registry.populateFromBody(parser.copy());
1856 if (failed(fillRegistry))
1857 return;
1858
1859 mOp = ModuleOp::create(builder, getLocation());
1860 builder.setInsertionPointToStart(&mOp.getBodyRegion().front());
1861 LogicalResult parsingTypes = parseSection<WasmSectionType::TYPE>();
1862 if (failed(parsingTypes))
1863 return;
1864
1865 LogicalResult parsingImports = parseSection<WasmSectionType::IMPORT>();
1866 if (failed(parsingImports))
1867 return;
1868
1869 firstInternalFuncID = symbols.funcSymbols.size();
1870
1871 LogicalResult parsingFunctions = parseSection<WasmSectionType::FUNCTION>();
1872 if (failed(parsingFunctions))
1873 return;
1874
1875 LogicalResult parsingTables = parseSection<WasmSectionType::TABLE>();
1876 if (failed(parsingTables))
1877 return;
1878
1879 LogicalResult parsingMems = parseSection<WasmSectionType::MEMORY>();
1880 if (failed(parsingMems))
1881 return;
1882
1883 LogicalResult parsingGlobals = parseSection<WasmSectionType::GLOBAL>();
1884 if (failed(parsingGlobals))
1885 return;
1886
1887 LogicalResult parsingCode = parseSection<WasmSectionType::CODE>();
1888 if (failed(parsingCode))
1889 return;
1890
1891 LogicalResult parsingExports = parseSection<WasmSectionType::EXPORT>();
1892 if (failed(parsingExports))
1893 return;
1894
1895 // Copy over sizes of containers into statistics.
1896 LDBG() << "WASM Imports:"
1897 << "\n"
1898 << " - Num functions: " << symbols.funcSymbols.size() << "\n"
1899 << " - Num globals: " << symbols.globalSymbols.size() << "\n"
1900 << " - Num memories: " << symbols.memSymbols.size() << "\n"
1901 << " - Num tables: " << symbols.tableSymbols.size();
1902 }
1903
1904 ModuleOp getModule() {
1905 if (isValid)
1906 return mOp;
1907 if (mOp)
1908 mOp.erase();
1909 return ModuleOp{};
1910 }
1911
1912private:
1913 mlir::StringAttr srcName;
1914 OpBuilder builder;
1915 WasmModuleSymbolTables symbols;
1916 MLIRContext *ctx;
1917 ModuleOp mOp;
1918 SectionRegistry registry;
1919 size_t firstInternalFuncID{0};
1920 bool isValid{true};
1921};
1922
1923template <>
1924LogicalResult
1925WasmBinaryParser::parseSectionItem<WasmSectionType::IMPORT>(ParserHead &ph,
1926 size_t) {
1927 FileLineColLoc importLoc = ph.getLocation();
1928 auto moduleName = ph.parseName();
1929 if (failed(moduleName))
1930 return failure();
1931
1932 auto importName = ph.parseName();
1933 if (failed(importName))
1934 return failure();
1935
1936 FailureOr<ImportDesc> import = ph.parseImportDesc(ctx);
1937 if (failed(import))
1938 return failure();
1939
1940 return std::visit(
1941 [this, importLoc, &moduleName, &importName](auto import) {
1942 return visitImport(importLoc, *moduleName, *importName, import);
1943 },
1944 *import);
1945}
1946
1947template <>
1948LogicalResult
1949WasmBinaryParser::parseSectionItem<WasmSectionType::EXPORT>(ParserHead &ph,
1950 size_t) {
1951 FileLineColLoc exportLoc = ph.getLocation();
1952
1953 auto exportName = ph.parseName();
1954 if (failed(exportName))
1955 return failure();
1956
1957 FailureOr<std::byte> opcode = ph.consumeByte();
1958 if (failed(opcode))
1959 return failure();
1960
1961 FailureOr<uint32_t> idx = ph.parseLiteral<uint32_t>();
1962 if (failed(idx))
1963 return failure();
1964
1965 using SymbolRefDesc = std::variant<SmallVector<SymbolRefContainer>,
1966 SmallVector<GlobalSymbolRefContainer>,
1967 SmallVector<FunctionSymbolRefContainer>>;
1968
1969 SymbolRefDesc currentSymbolList;
1970 std::string symbolType = "";
1971 switch (*opcode) {
1973 symbolType = "function";
1974 currentSymbolList = symbols.funcSymbols;
1975 break;
1977 symbolType = "table";
1978 currentSymbolList = symbols.tableSymbols;
1979 break;
1981 symbolType = "memory";
1982 currentSymbolList = symbols.memSymbols;
1983 break;
1985 symbolType = "global";
1986 currentSymbolList = symbols.globalSymbols;
1987 break;
1988 default:
1989 return emitError(exportLoc, "invalid value for export type: ")
1990 << std::to_integer<unsigned>(*opcode);
1991 }
1992
1993 auto currentSymbol = std::visit(
1994 [&](const auto &list) -> FailureOr<FlatSymbolRefAttr> {
1995 if (*idx > list.size()) {
1996 emitError(
1997 exportLoc,
1998 llvm::formatv(
1999 "trying to export {0} {1} which is undefined in this scope",
2000 symbolType, *idx));
2001 return failure();
2002 }
2003 return list[*idx].symbol;
2004 },
2005 currentSymbolList);
2006
2007 if (failed(currentSymbol))
2008 return failure();
2009
2010 Operation *op = SymbolTable::lookupSymbolIn(mOp, *currentSymbol);
2011 op->setAttr("exported", UnitAttr::get(op->getContext()));
2012 StringAttr symName = SymbolTable::getSymbolName(op);
2013 return SymbolTable{mOp}.rename(symName, *exportName);
2014}
2015
2016template <>
2017LogicalResult
2018WasmBinaryParser::parseSectionItem<WasmSectionType::TABLE>(ParserHead &ph,
2019 size_t) {
2020 FileLineColLoc opLocation = ph.getLocation();
2021 FailureOr<TableType> tableType = ph.parseTableType(ctx);
2022 if (failed(tableType))
2023 return failure();
2024 LDBG() << " Parsed table description: " << *tableType;
2025 StringAttr symbol = builder.getStringAttr(symbols.getNewTableSymbolName());
2026 auto tableOp =
2027 TableOp::create(builder, opLocation, symbol.strref(), *tableType);
2028 symbols.tableSymbols.push_back({SymbolRefAttr::get(tableOp)});
2029 return success();
2030}
2031
2032template <>
2033LogicalResult
2034WasmBinaryParser::parseSectionItem<WasmSectionType::FUNCTION>(ParserHead &ph,
2035 size_t) {
2036 FileLineColLoc opLoc = ph.getLocation();
2037 auto typeIdxParsed = ph.parseLiteral<uint32_t>();
2038 if (failed(typeIdxParsed))
2039 return failure();
2040 uint32_t typeIdx = *typeIdxParsed;
2041 if (typeIdx >= symbols.moduleFuncTypes.size())
2042 return emitError(getLocation(), "invalid type index: ") << typeIdx;
2043 std::string symbol = symbols.getNewFuncSymbolName();
2044 auto funcOp =
2045 FuncOp::create(builder, opLoc, symbol, symbols.moduleFuncTypes[typeIdx]);
2046 Block *block = funcOp.addEntryBlock();
2047 OpBuilder::InsertionGuard guard{builder};
2048 builder.setInsertionPointToEnd(block);
2049 ReturnOp::create(builder, opLoc);
2050 symbols.funcSymbols.push_back(
2051 {{FlatSymbolRefAttr::get(funcOp.getSymNameAttr())},
2052 symbols.moduleFuncTypes[typeIdx]});
2053 return funcOp.verify();
2054}
2055
2056template <>
2057LogicalResult
2058WasmBinaryParser::parseSectionItem<WasmSectionType::TYPE>(ParserHead &ph,
2059 size_t) {
2060 FailureOr<FunctionType> funcType = ph.parseFunctionType(ctx);
2061 if (failed(funcType))
2062 return failure();
2063 LDBG() << "Parsed function type " << *funcType;
2064 symbols.moduleFuncTypes.push_back(*funcType);
2065 return success();
2066}
2067
2068template <>
2069LogicalResult
2070WasmBinaryParser::parseSectionItem<WasmSectionType::MEMORY>(ParserHead &ph,
2071 size_t) {
2072 FileLineColLoc opLocation = ph.getLocation();
2073 FailureOr<LimitType> memory = ph.parseLimit(ctx);
2074 if (failed(memory))
2075 return failure();
2076
2077 LDBG() << " Registering memory " << *memory;
2078 std::string symbol = symbols.getNewMemorySymbolName();
2079 auto memOp = MemOp::create(builder, opLocation, symbol, *memory);
2080 symbols.memSymbols.push_back({SymbolRefAttr::get(memOp)});
2081 return success();
2082}
2083
2084template <>
2085LogicalResult
2086WasmBinaryParser::parseSectionItem<WasmSectionType::GLOBAL>(ParserHead &ph,
2087 size_t) {
2088 FileLineColLoc globalLocation = ph.getLocation();
2089 auto globalTypeParsed = ph.parseGlobalType(ctx);
2090 if (failed(globalTypeParsed))
2091 return failure();
2092
2093 GlobalTypeRecord globalType = *globalTypeParsed;
2094 auto symbol = builder.getStringAttr(symbols.getNewGlobalSymbolName());
2095 auto globalOp = wasmssa::GlobalOp::create(
2096 builder, globalLocation, symbol, globalType.type, globalType.isMutable);
2097 symbols.globalSymbols.push_back(
2098 {{FlatSymbolRefAttr::get(globalOp)}, globalOp.getType()});
2099 OpBuilder::InsertionGuard guard{builder};
2100 Block *block = builder.createBlock(&globalOp.getInitializer());
2101 builder.setInsertionPointToStart(block);
2102 parsed_inst_t expr = ph.parseExpression(builder, symbols);
2103 if (failed(expr))
2104 return failure();
2105 if (block->empty())
2106 return emitError(globalLocation, "global with empty initializer");
2107 if (expr->size() != 1 && (*expr)[0].getType() != globalType.type)
2108 return emitError(
2109 globalLocation,
2110 "initializer result type does not match global declaration type");
2111 ReturnOp::create(builder, globalLocation, *expr);
2112 return success();
2113}
2114
2115template <>
2116LogicalResult WasmBinaryParser::parseSectionItem<WasmSectionType::CODE>(
2117 ParserHead &ph, size_t innerFunctionId) {
2118 unsigned long funcId = innerFunctionId + firstInternalFuncID;
2119 FunctionSymbolRefContainer symRef = symbols.funcSymbols[funcId];
2120 auto funcOp =
2121 dyn_cast<FuncOp>(SymbolTable::lookupSymbolIn(mOp, symRef.symbol));
2122 assert(funcOp);
2123 if (failed(ph.parseCodeFor(funcOp, symbols)))
2124 return failure();
2125 return success();
2126}
2127} // namespace
2128
2129namespace mlir::wasm {
2131 MLIRContext *context) {
2132 WasmBinaryParser wBN{source, context};
2133 ModuleOp mOp = wBN.getModule();
2134 if (mOp)
2135 return {mOp};
2136
2137 return {nullptr};
2138}
2139} // namespace mlir::wasm
return success()
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static Type getElementType(Type type)
Determine the element type of type.
static std::string diag(const llvm::Value &value)
memberIdxs push_back(ArrayAttr::get(parser.getContext(), values))
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
#define EXPORT
#define BUILD_NUMERIC_BINOP_INTFP(OP_NAME, PREFIX)
#define BUILD_CONVERT_OP_FOR(DEST_T, WIDTH)
#define APPLY_WASM_SEC_TRANSFORM
#define BUILD_REINTERPRET_PARSER(WIDTH, FP_TYPE)
#define BUILD_TRUNC_OP_FOR(SRC_T, WIDTH)
#define BUILD_CONVERSION_OP(IN_T, OUT_T, SOURCE_OP, TARGET_OP)
#define BUILD_SLICE_EXTEND_PARSER(IT_WIDTH, EXTRACT_WIDTH)
#define BUILD_NUMERIC_UNARY_OP_INT(OP_NAME, PREFIX)
#define BUILD_NUMERIC_BINOP_INT(OP_NAME, PREFIX)
#define BUILD_NUMERIC_BINOP_FP(OP_NAME, PREFIX)
#define BUILD_NUMERIC_UNARY_OP_FP(OP_NAME, PREFIX)
#define mul(a, b)
#define add(a, b)
#define div(a, b)
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Definition Block.cpp:154
bool empty()
Definition Block.h:172
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
OpListType & getOperations()
Definition Block.h:161
Operation & back()
Definition Block.h:176
BlockArgListType getArguments()
Definition Block.h:111
FloatType getF32Type()
Definition Builders.cpp:51
FunctionType getFunctionType(TypeRange inputs, TypeRange results)
Definition Builders.cpp:84
IntegerType getI64Type()
Definition Builders.cpp:73
IntegerType getI32Type()
Definition Builders.cpp:71
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
MLIRContext * getContext() const
Definition Builders.h:56
IntegerAttr getUI32IntegerAttr(uint32_t value)
Definition Builders.cpp:220
FloatType getF64Type()
Definition Builders.cpp:53
HandlerID registerHandler(HandlerTy handler)
Register a new handler for diagnostics to the engine.
static FileLineColLoc get(StringAttr filename, unsigned line, unsigned column)
Definition Location.cpp:157
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
DiagnosticEngine & getDiagEngine()
Returns the diagnostic engine for this context.
void loadAllAvailableDialects()
Load all dialects available in the registry in this context.
This class helps build Operations.
Definition Builders.h:210
InsertPoint saveInsertionPoint() const
Return a saved insertion point.
Definition Builders.h:388
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
Block * getBlock() const
Returns the current block of the builder.
Definition Builders.h:451
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
void restoreInsertionPoint(InsertPoint ip)
Restore the insert point to a previously saved point.
Definition Builders.h:393
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Definition Operation.h:607
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
This class acts as an owning reference to an op, and will automatically destroy the held op on destru...
Definition OwningOpRef.h:29
iterator end()
Definition Region.h:56
static Operation * lookupSymbolIn(Operation *op, StringAttr symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
static StringAttr getSymbolName(Operation *symbol)
Returns the name of the given symbol operation, aborting if no symbol is present.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
Type getType() const
Return the type of this value.
Definition Value.h:105
QueryRef parse(llvm::StringRef line, const QuerySession &qs)
Definition Query.cpp:21
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
OwningOpRef< ModuleOp > importWebAssemblyToModule(llvm::SourceMgr &source, MLIRContext *context)
If source contains a valid Wasm binary file, this function returns a a ModuleOp containing the repres...
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
Definition Value.h:494
InFlightDiagnostic emitRemark(Location loc)
Utility method to emit a remark message using this location.
static constexpr std::byte memory
static constexpr std::byte table
static constexpr std::byte global
static constexpr std::byte function
static constexpr std::byte memType
static constexpr std::byte typeID
static constexpr std::byte tableType
static constexpr std::byte globalType
static constexpr std::byte elseOpCode
static constexpr std::byte externRef
static constexpr std::byte i32
static constexpr std::byte funcType
static constexpr std::byte i64
static constexpr std::byte emptyBlockType
static constexpr std::byte funcRef
static constexpr std::byte v128
static constexpr std::byte f64
static constexpr std::byte f32
static constexpr std::byte endByte