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