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