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"
35#define DEBUG_TYPE "wasm-translate"
37static_assert(CHAR_BIT == 8,
38 "This code expects std::byte to be exactly 8 bits");
45using section_id_t = uint8_t;
46enum struct WasmSectionType : section_id_t {
62constexpr section_id_t highestWasmSectionID{
63 static_cast<section_id_t
>(WasmSectionType::DATACOUNT)};
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)
80template <WasmSectionType>
81constexpr const char *wasmSectionName =
"";
83#define WASM_SEC_TRANSFORM(section) \
85 [[maybe_unused]] constexpr const char \
86 *wasmSectionName<WasmSectionType::section> = #section;
88#undef WASM_SEC_TRANSFORM
90constexpr bool sectionShouldBeUnique(WasmSectionType secType) {
91 return secType != WasmSectionType::CUSTOM;
94template <std::byte... Bytes>
95struct ByteSequence {};
98template <std::
byte Byte>
99struct UniqueByte : ByteSequence<Byte> {};
101[[maybe_unused]]
constexpr ByteSequence<
106template <std::byte... allowedFlags>
107constexpr bool isValueOneOf(std::byte value,
108 ByteSequence<allowedFlags...> = {}) {
109 return ((value == allowedFlags) | ... |
false);
112template <std::byte... flags>
113constexpr bool isNotIn(std::byte value, ByteSequence<flags...> = {}) {
114 return !isValueOneOf<flags...>(value);
117struct GlobalTypeRecord {
122struct TypeIdxRecord {
126struct SymbolRefContainer {
127 FlatSymbolRefAttr symbol;
130struct GlobalSymbolRefContainer : SymbolRefContainer {
134struct FunctionSymbolRefContainer : SymbolRefContainer {
135 FunctionType functionType;
139 std::variant<TypeIdxRecord, TableType, LimitType, GlobalTypeRecord>;
141using parsed_inst_t = FailureOr<SmallVector<Value>>;
143struct EmptyBlockMarker {};
144using BlockTypeParseResult =
145 std::variant<EmptyBlockMarker, TypeIdxRecord, Type>;
147struct WasmModuleSymbolTables {
148 SmallVector<FunctionSymbolRefContainer> funcSymbols;
149 SmallVector<GlobalSymbolRefContainer> globalSymbols;
150 SmallVector<SymbolRefContainer> memSymbols;
151 SmallVector<SymbolRefContainer> tableSymbols;
152 SmallVector<FunctionType> moduleFuncTypes;
154 std::string getNewSymbolName(StringRef prefix,
size_t id)
const {
155 return (prefix + Twine{
id}).str();
158 std::string getNewFuncSymbolName()
const {
159 size_t id = funcSymbols.size();
160 return getNewSymbolName(
"func_",
id);
163 std::string getNewGlobalSymbolName()
const {
164 size_t id = globalSymbols.size();
165 return getNewSymbolName(
"global_",
id);
168 std::string getNewMemorySymbolName()
const {
169 size_t id = memSymbols.size();
170 return getNewSymbolName(
"mem_",
id);
173 std::string getNewTableSymbolName()
const {
174 size_t id = tableSymbols.size();
175 return getNewSymbolName(
"table_",
id);
191 LabelLevelOpInterface levelOp;
195 bool empty()
const {
return values.empty(); }
197 size_t size()
const {
return values.size(); }
207 FailureOr<SmallVector<Value>> popOperands(
TypeRange operandTypes,
216 LogicalResult pushResults(
ValueRange results, Location *opLoc);
218 void addLabelLevel(LabelLevelOpInterface levelOp) {
219 labelLevel.push_back({values.size(), levelOp});
220 LDBG() <<
"Adding a new frame context to ValueStack";
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);
228#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
231 LLVM_DUMP_METHOD
void dump()
const;
235 SmallVector<Value> values;
236 SmallVector<LabelLevel> labelLevel;
241template <
size_t... IS>
242constexpr ByteSequence<std::byte{IS}...>
243 castIndexSequenceToBytes(std::index_sequence<IS...>) {
247constexpr auto all8bitsBytes =
248 castIndexSequenceToBytes(std::make_index_sequence<256>());
250class ExpressionParser {
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} {}
258 template <
typename valueT>
260 parseConstInst(OpBuilder &builder,
261 std::enable_if_t<std::is_arithmetic_v<valueT>> * =
nullptr);
272 template <
typename opcode,
typename valueType,
unsigned int numOperands>
274 buildNumericOp(OpBuilder &builder,
275 std::enable_if_t<std::is_arithmetic_v<valueType>> * =
nullptr);
286 template <
typename opType,
typename inputType,
typename outputType,
287 typename... extraArgsT>
288 inline parsed_inst_t buildConvertOp(OpBuilder &builder, extraArgsT...);
290 inline parsed_inst_t dispatchToInstParser(std::byte opCode,
295 struct NestingContextGuard {
296 NestingContextGuard(ExpressionParser &parser, LabelLevelOpInterface levelOp)
298 parser.addNestingContextLevel(levelOp);
300 NestingContextGuard(NestingContextGuard &&other) : parser{other.parser} {
301 other.shouldDropOnDestruct =
false;
303 NestingContextGuard(NestingContextGuard
const &) =
delete;
304 ~NestingContextGuard() {
305 if (shouldDropOnDestruct)
306 parser.dropNestingContextLevel();
308 ExpressionParser &parser;
309 bool shouldDropOnDestruct =
true;
312 void addNestingContextLevel(LabelLevelOpInterface levelOp) {
313 valueStack.addLabelLevel(levelOp);
316 void dropNestingContextLevel() {
319 valueStack.dropLabelLevel();
322 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
327 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
328 TypeIdxRecord type) {
329 if (type.id >= symbols.moduleFuncTypes.size())
331 "type index references nonexistent type (")
332 << type.id <<
"). Only " << symbols.moduleFuncTypes.size()
333 <<
" types are registered";
334 return symbols.moduleFuncTypes[type.id];
337 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
342 llvm::FailureOr<FunctionType>
343 getFuncTypeFor(OpBuilder &builder, BlockTypeParseResult parseResult) {
345 [
this, &builder](
auto value) {
return getFuncTypeFor(builder, value); },
349 llvm::FailureOr<FunctionType>
350 getFuncTypeFor(OpBuilder &builder,
351 llvm::FailureOr<BlockTypeParseResult> parseResult) {
352 if (llvm::failed(parseResult))
354 return getFuncTypeFor(builder, *parseResult);
357 llvm::FailureOr<FunctionType> parseBlockFuncType(OpBuilder &builder);
359 struct ParseResultWithInfo {
360 SmallVector<Value> opResults;
361 std::byte endingByte;
364 template <
typename FilterT = ByteSequence<WasmBinaryEncoding::endByte>>
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};
373 LDBG() <<
"parsing a block of type "
376 auto nC = addNesting(levelOp);
380 auto bodyParsingRes =
parse(builder, parseEndBytes);
381 if (
failed(bodyParsingRes))
383 auto returnOperands = popOperands(resTypes);
384 if (
failed(returnOperands))
386 BlockReturnOp::create(builder, opLoc, *returnOperands);
387 LDBG() <<
"end of parsing of a block";
388 return bodyParsingRes->endingByte;
392 template <std::
byte ParseEndByte = WasmBinaryEncoding::endByte>
393 parsed_inst_t
parse(OpBuilder &builder, UniqueByte<ParseEndByte> = {});
395 template <std::byte... ExpressionParseEnd>
396 FailureOr<ParseResultWithInfo>
397 parse(OpBuilder &builder,
398 ByteSequence<ExpressionParseEnd...> parsingEndFilters);
400 NestingContextGuard addNesting(LabelLevelOpInterface levelOp) {
401 return NestingContextGuard{*
this, levelOp};
404 FailureOr<llvm::SmallVector<Value>> popOperands(
TypeRange operandTypes) {
405 return valueStack.popOperands(operandTypes, ¤tOpLoc.value());
408 LogicalResult pushResults(
ValueRange results) {
409 return valueStack.pushResults(results, ¤tOpLoc.value());
415 template <
typename OpToCreate>
416 parsed_inst_t parseSetOrTee(OpBuilder &);
421 template <
typename OpToCreate>
422 parsed_inst_t parseBlockLikeOp(OpBuilder &);
424 Location getCurrentOpLoc() {
425 assert(currentOpLoc.has_value() &&
426 "expects current opcode location to be set");
427 return *currentOpLoc;
430 class TopLevelInstParserRegistry {
432 template <std::
byte opCode>
433 static constexpr bool hasParserForOpcode =
false;
435 template <std::
byte opCode>
436 static parsed_inst_t parseInstrWithOpCode(OpBuilder &,
437 ExpressionParser &) =
delete;
441 std::optional<Location> currentOpLoc;
443 WasmModuleSymbolTables
const &symbols;
445 ValueStack valueStack;
448static inline parsed_inst_t
449unreachableHandler(
OpBuilder &, ExpressionParser &expressionParser) {
450 llvm_unreachable(
"Failure in opcode parser dispatch logic.");
451 return mlir::failure();
454template <
typename ParserRegistry>
455class InstDispatcher {
457 using dispatch_t = parsed_inst_t (*)(OpBuilder &, ExpressionParser &);
459 template <std::
byte opCode>
460 static constexpr dispatch_t getHandlerForOpCode() {
461 if constexpr (ParserRegistry::template hasParserForOpcode<opCode>)
462 return ParserRegistry::template parseInstrWithOpCode<opCode>;
464 return unreachableHandler;
468 template <std::
byte opCode>
469 static constexpr bool isValidInst =
470 ParserRegistry::template hasParserForOpcode<opCode>;
473 static inline parsed_inst_t
474 invalidOpcodeDiag(OpBuilder &, ExpressionParser &expressionParser,
476 return emitError(expressionParser.getCurrentOpLoc(),
477 "unknown instruction opcode: ")
478 <<
static_cast<int>(opCode);
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);
492 return invalidOpcodeDiag(builder, exprParser, opCode);
506 static parsed_inst_t dispatch(std::byte opCode, OpBuilder &builder,
507 ExpressionParser &exprParser) {
508 return dispatchImpl(opCode, builder, exprParser, all8bitsBytes);
514 ParserHead(StringRef src, StringAttr name) : head{src}, locName{name} {}
515 ParserHead(ParserHead &&) =
default;
518 ParserHead(ParserHead
const &other) =
default;
521 auto getLocation()
const {
525 FailureOr<StringRef> consumeNBytes(
size_t nBytes) {
526 LDBG() <<
"Consume " << nBytes <<
" bytes";
527 LDBG() <<
" Bytes remaining: " << size();
528 LDBG() <<
" Current offset: " << offset;
530 return emitError(getLocation(),
"trying to extract ")
531 << nBytes <<
"bytes when only " << size() <<
"are available";
533 StringRef res = head.slice(offset, offset + nBytes);
535 LDBG() <<
" Updated offset (+" << nBytes <<
"): " << offset;
539 FailureOr<std::byte> consumeByte() {
540 FailureOr<StringRef> res = consumeNBytes(1);
543 return std::byte{*res->bytes_begin()};
546 template <
typename T>
547 FailureOr<T> parseLiteral();
549 FailureOr<uint32_t> parseVectorSize();
555 inline FailureOr<uint32_t> parseUI32();
556 inline FailureOr<int64_t> parseI64();
559 FailureOr<StringRef> parseName() {
560 FailureOr<uint32_t> size = parseVectorSize();
564 return consumeNBytes(*size);
567 FailureOr<WasmSectionType> parseWasmSectionType() {
568 FailureOr<std::byte>
id = consumeByte();
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);
577 FailureOr<LimitType> parseLimit(MLIRContext *ctx) {
578 using WasmLimits = WasmBinaryEncoding::LimitHeader;
579 FileLineColLoc limitLocation = getLocation();
580 FailureOr<std::byte> limitHeader = consumeByte();
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();
590 std::optional<uint32_t>
max{std::nullopt};
591 if (*limitHeader == WasmLimits::bothLimits) {
592 FailureOr<uint32_t> maxParse = parseUI32();
597 return LimitType::get(ctx, *minParse,
max);
600 FailureOr<Type> parseValueType(MLIRContext *ctx) {
601 FileLineColLoc typeLoc = getLocation();
602 FailureOr<std::byte> typeEncoding = consumeByte();
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);
621 return emitError(typeLoc,
"invalid value type encoding: ")
622 <<
static_cast<int>(*typeEncoding);
626 FailureOr<GlobalTypeRecord> parseGlobalType(MLIRContext *ctx) {
627 using WasmGlobalMut = WasmBinaryEncoding::GlobalMutability;
628 FailureOr<Type> typeParsed = parseValueType(ctx);
631 FileLineColLoc mutLoc = getLocation();
632 FailureOr<std::byte> mutSpec = consumeByte();
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};
641 FailureOr<TupleType> parseResultType(MLIRContext *ctx) {
642 FailureOr<uint32_t> nParamsParsed = parseVectorSize();
643 if (
failed(nParamsParsed))
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);
652 res.push_back(*parsedType);
654 return TupleType::get(ctx, res);
657 FailureOr<FunctionType> parseFunctionType(MLIRContext *ctx) {
658 FileLineColLoc typeLoc = getLocation();
659 FailureOr<std::byte> funcTypeHeader = consumeByte();
660 if (
failed(funcTypeHeader))
663 return emitError(typeLoc,
"invalid function type header byte. Expecting ")
665 <<
" got " << std::to_integer<unsigned>(*funcTypeHeader);
666 FailureOr<TupleType> inputTypes = parseResultType(ctx);
670 FailureOr<TupleType> resTypes = parseResultType(ctx);
674 return FunctionType::get(ctx, inputTypes->getTypes(), resTypes->getTypes());
677 FailureOr<TypeIdxRecord> parseTypeIndex() {
678 FailureOr<uint32_t> res = parseUI32();
681 return TypeIdxRecord{*res};
684 FailureOr<TableType> parseTableType(MLIRContext *ctx) {
685 FailureOr<Type> elmTypeParse = parseValueType(ctx);
688 if (!isWasmRefType(*elmTypeParse))
689 return emitError(getLocation(),
"invalid element type for table");
690 FailureOr<LimitType> limitParse = parseLimit(ctx);
693 return TableType::get(ctx, *elmTypeParse, *limitParse);
696 FailureOr<ImportDesc> parseImportDesc(MLIRContext *ctx) {
697 FileLineColLoc importLoc = getLocation();
698 FailureOr<std::byte> importType = consumeByte();
699 auto packager = [](
auto parseResult) -> FailureOr<ImportDesc> {
702 return {*parseResult};
706 switch (*importType) {
708 return packager(parseTypeIndex());
710 return packager(parseTableType(ctx));
712 return packager(parseLimit(ctx));
714 return packager(parseGlobalType(ctx));
716 return emitError(importLoc,
"invalid import type descriptor: ")
717 <<
static_cast<int>(*importType);
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);
728 LogicalResult parseCodeFor(FuncOp func,
729 WasmModuleSymbolTables
const &symbols) {
730 SmallVector<local_val_t> locals{};
732 Block &block = func.getBody().front();
734 assert(func.getBody().getBlocks().size() == 1 &&
735 "Function should only have its default created block at this point");
737 "Only the placeholder return op should be present at this point");
738 auto returnOp = cast<ReturnOp>(&block.
back());
741 FailureOr<uint32_t> codeSizeInBytes = parseUI32();
742 if (
failed(codeSizeInBytes))
744 FailureOr<StringRef> codeContent = consumeNBytes(*codeSizeInBytes);
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();
753 OpBuilder builder{&func.getBody().front().back()};
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();
763 FailureOr<Type> varT = cParser.parseValueType(func->getContext());
766 for (
size_t j = 0; j < *nSubVar; ++j) {
767 auto local = LocalOp::create(builder, varLoc, *varT);
768 locals.push_back(local.getResult());
771 parsed_inst_t res = cParser.parseExpression(builder, symbols, locals);
776 "unparsed garbage remaining at end of code block");
777 ReturnOp::create(builder, func->getLoc(), *res);
782 llvm::FailureOr<BlockTypeParseResult> parseBlockType(MLIRContext *ctx) {
783 auto loc = getLocation();
784 auto blockIndicator = peek();
785 if (
failed(blockIndicator))
789 return {EmptyBlockMarker{}};
791 if (isValueOneOf(*blockIndicator, valueTypesEncodings))
792 return parseValueType(ctx);
795 auto typeIdx = parseI64();
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 ")
802 return {TypeIdxRecord{
static_cast<uint32_t
>(*typeIdx)}};
805 bool end()
const {
return curHead().empty(); }
807 ParserHead
copy()
const {
return *
this; }
810 StringRef curHead()
const {
return head.drop_front(offset); }
812 FailureOr<std::byte> peek()
const {
816 "trying to peek at next byte, but input stream is empty");
817 return static_cast<std::byte
>(curHead().front());
820 size_t size()
const {
return head.size() - offset; }
824 unsigned anchorOffset{0};
829FailureOr<float> ParserHead::parseLiteral<float>() {
830 FailureOr<StringRef> bytes = consumeNBytes(4);
833 return llvm::support::endian::read<float>(bytes->bytes_begin(),
834 llvm::endianness::little);
838FailureOr<double> ParserHead::parseLiteral<double>() {
839 FailureOr<StringRef> bytes = consumeNBytes(8);
842 return llvm::support::endian::read<double>(bytes->bytes_begin(),
843 llvm::endianness::little);
847FailureOr<uint32_t> ParserHead::parseLiteral<uint32_t>() {
848 char const *error =
nullptr;
850 unsigned encodingSize{0};
851 StringRef src = curHead();
852 uint64_t decoded = llvm::decodeULEB128(src.bytes_begin(), &encodingSize,
853 src.bytes_end(), &error);
857 if (std::isgreater(decoded, std::numeric_limits<uint32_t>::max()))
858 return emitError(getLocation()) <<
"literal does not fit on 32 bits";
860 res =
static_cast<uint32_t
>(decoded);
861 offset += encodingSize;
866FailureOr<int32_t> ParserHead::parseLiteral<int32_t>() {
867 char const *error =
nullptr;
869 unsigned encodingSize{0};
870 StringRef src = curHead();
871 int64_t decoded = llvm::decodeSLEB128(src.bytes_begin(), &encodingSize,
872 src.bytes_end(), &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";
879 res =
static_cast<int32_t
>(decoded);
880 offset += encodingSize;
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);
894 offset += encodingSize;
898FailureOr<uint32_t> ParserHead::parseVectorSize() {
899 return parseLiteral<uint32_t>();
902inline FailureOr<uint32_t> ParserHead::parseUI32() {
903 return parseLiteral<uint32_t>();
906inline FailureOr<int64_t> ParserHead::parseI64() {
907 return parseLiteral<int64_t>();
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>"
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';
926 auto frameId = idx - 1;
927 auto frameLimit = labelLevel[frameId].stackIdx;
929 return {{frameId, frameLimit}};
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
940 nextFrameIdx = getNextFrameIndex();
943 values[actualIdx].dump();
945 while (nextFrameIdx) {
946 llvm::dbgs() <<
" --------------- Frame (" << nextFrameIdx->first <<
")\n";
947 nextFrameIdx = getNextFrameIndex();
949 llvm::dbgs() <<
"<Bottom>"
951 llvm::dbgs() <<
"=========================================================\n";
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())
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);
976 values.resize(values.size() - operandTypes.size());
977 LDBG() <<
" Updated stack size: " << values.size();
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: ")
989 LDBG() <<
" PUSH: " << val;
990 values.push_back(val);
993 LDBG() <<
" Updated stack size: " << values.size();
997template <std::
byte EndParseByte>
998parsed_inst_t ExpressionParser::parse(OpBuilder &builder,
999 UniqueByte<EndParseByte> endByte) {
1000 auto res =
parse(builder, ByteSequence<EndParseByte>{});
1003 return res->opResults;
1006template <std::byte... ExpressionParseEnd>
1007FailureOr<ExpressionParser::ParseResultWithInfo>
1008ExpressionParser::parse(OpBuilder &builder,
1009 ByteSequence<ExpressionParseEnd...> parsingEndFilters) {
1010 SmallVector<Value> res;
1012 currentOpLoc = parser.getLocation();
1013 FailureOr<std::byte> opCode = parser.consumeByte();
1016 if (isValueOneOf(*opCode, parsingEndFilters))
1017 return {{res, *opCode}};
1018 parsed_inst_t resParsed;
1019 resParsed = dispatchToInstParser(*opCode, builder);
1022 std::swap(res, *resParsed);
1023 if (
failed(pushResults(res)))
1028llvm::FailureOr<FunctionType>
1029ExpressionParser::parseBlockFuncType(OpBuilder &builder) {
1030 return getFuncTypeFor(builder, parser.parseBlockType(builder.
getContext()));
1033template <
typename OpToCreate>
1034parsed_inst_t ExpressionParser::parseBlockLikeOp(OpBuilder &builder) {
1035 auto opLoc = currentOpLoc;
1036 auto funcType = parseBlockFuncType(builder);
1040 auto inputTypes = funcType->getInputs();
1041 auto inputOps = popOperands(inputTypes);
1046 Region *curRegion = curBlock->
getParent();
1047 auto resTypes = funcType->getResults();
1048 llvm::SmallVector<Location> locations{};
1049 locations.resize(resTypes.size(), *currentOpLoc);
1051 builder.
createBlock(curRegion, curRegion->
end(), resTypes, locations);
1054 OpToCreate::create(builder, *currentOpLoc, *inputOps, successor);
1055 auto *blockBody = blockOp.createBlock();
1056 if (
failed(parseBlockContent(builder, blockBody, resTypes, *opLoc, blockOp)))
1059 return {
ValueRange{successor->getArguments()}};
1067#define REGISTER_PARSER_OPCODE_PARSER(parserType, opcode, builderName, \
1070 constexpr bool parserType::hasParserForOpcode<opcode> = true; \
1072 inline parsed_inst_t parserType::parseInstrWithOpCode<opcode>( \
1073 OpBuilder & (builderName), ExpressionParser & (parserName))
1075#define REGISTER_PRIMARY_WASM_INST_PARSER(opcode, builderName, parserName) \
1076 REGISTER_PARSER_OPCODE_PARSER(ExpressionParser::TopLevelInstParserRegistry, \
1077 opcode, builderName, parserName)
1081 return exprParser.parseBlockLikeOp<BlockOp>(builder);
1086 return exprParser.parseBlockLikeOp<LoopOp>(builder);
1091 auto opLoc = exprParser.currentOpLoc;
1092 auto funcType = exprParser.parseBlockFuncType(builder);
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))
1101 auto inputOps = exprParser.popOperands(inputTypes);
1106 Region *curRegion = curBlock->
getParent();
1107 auto resTypes = funcType->getResults();
1108 llvm::SmallVector<Location> locations{};
1109 locations.resize(resTypes.size(), exprParser.getCurrentOpLoc());
1111 builder.
createBlock(curRegion, curRegion->
end(), resTypes, locations);
1113 auto ifOp = IfOp::create(builder, exprParser.getCurrentOpLoc(),
1114 conditionValue->front(), *inputOps, successor);
1115 auto *ifEntryBlock = ifOp.createIfBlock();
1116 constexpr auto ifElseFilter =
1119 auto parseIfRes = exprParser.parseBlockContent(
1120 builder, ifEntryBlock, resTypes, *opLoc, ifOp, ifElseFilter);
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))
1132 return {
ValueRange{successor->getArguments()}};
1137 auto level = exprParser.parser.parseLiteral<uint32_t>();
1141 Region *curRegion = curBlock->
getParent();
1144 auto condition = exprParser.popOperands(builder.
getI32Type());
1149 LabelBranchingOpInterface::getTargetOpFromBlock(curBlock, *level);
1152 auto inputTypes = targetOp->getLabelTarget()->getArgumentTypes();
1153 auto branchArgs = exprParser.popOperands(inputTypes);
1156 BranchIfOp::create(builder, exprParser.getCurrentOpLoc(), condition->front(),
1160 return {*branchArgs};
1165 auto loc = *exprParser.currentOpLoc;
1166 auto funcIdx = exprParser.parser.parseLiteral<uint32_t>();
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);
1178 FuncCallOp::create(builder, loc, resTypes, callee.symbol, *inOperands);
1179 return {callOp.getResults()};
1184 FailureOr<uint32_t>
id = exprParser.parser.parseLiteral<uint32_t>();
1185 Location instLoc = *exprParser.currentOpLoc;
1188 if (*
id >= exprParser.locals.size())
1189 return emitError(instLoc,
"invalid local index. function has ")
1190 << exprParser.locals.size() <<
" accessible locals, received index "
1192 return {{LocalGetOp::create(builder, instLoc, exprParser.locals[*
id])
1197 builder, exprParser) {
1198 FailureOr<uint32_t>
id = exprParser.parser.parseLiteral<uint32_t>();
1199 Location instLoc = *exprParser.currentOpLoc;
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,
1210 return {{globalOp.getResult()}};
1213template <
typename OpToCreate>
1214parsed_inst_t ExpressionParser::parseSetOrTee(OpBuilder &builder) {
1215 FailureOr<uint32_t>
id = parser.parseLiteral<uint32_t>();
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())
1224 "invalid stack access, trying to access a value on an empty stack");
1230 OpToCreate::create(builder, *currentOpLoc, locals[*
id], poppedOp->front())
1236 return exprParser.parseSetOrTee<LocalSetOp>(builder);
1241 return exprParser.parseSetOrTee<LocalTeeOp>(builder);
1244template <
typename T>
1245inline Type buildLiteralType(OpBuilder &);
1248inline Type buildLiteralType<int32_t>(OpBuilder &builder) {
1253inline Type buildLiteralType<int64_t>(OpBuilder &builder) {
1258[[maybe_unused]]
inline Type buildLiteralType<uint32_t>(OpBuilder &builder) {
1263[[maybe_unused]]
inline Type buildLiteralType<uint64_t>(OpBuilder &builder) {
1268inline Type buildLiteralType<float>(OpBuilder &builder) {
1273inline Type buildLiteralType<double>(OpBuilder &builder) {
1277template <
typename ValT,
1278 typename E = std::enable_if_t<std::is_arithmetic_v<ValT>>>
1281template <
typename ValT>
1282struct AttrHolder<ValT, std::enable_if_t<std::is_integral_v<ValT>>> {
1283 using type = IntegerAttr;
1286template <
typename ValT>
1287struct AttrHolder<ValT, std::enable_if_t<std::is_floating_point_v<ValT>>> {
1288 using type = FloatAttr;
1291template <
typename ValT>
1292using attr_holder_t =
typename AttrHolder<ValT>::type;
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);
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))
1307 ConstOp::create(builder, *currentOpLoc,
1308 buildLiteralAttr<valueT>(builder, *parsedConstant));
1309 return {{constOp.getResult()}};
1314 return exprParser.parseConstInst<int32_t>(builder);
1319 return exprParser.parseConstInst<int64_t>(builder);
1323 builder, exprParser) {
1324 return exprParser.parseConstInst<
float>(builder);
1328 builder, exprParser) {
1329 return exprParser.parseConstInst<
double>(builder);
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);
1344 auto op = opcode::create(builder, *currentOpLoc, *operands).getResult();
1345 LDBG() <<
"Built operation: " << op;
1350#define BUILD_NUMERIC_OP(OP_NAME, N_ARGS, PREFIX, SUFFIX, TYPE) \
1352 constexpr bool ExpressionParser::TopLevelInstParserRegistry:: \
1353 hasParserForOpcode<WasmBinaryEncoding::OpCode::PREFIX##SUFFIX> = true; \
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); \
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)
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)
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)
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)
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)
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
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))
1453 auto op = opType::create(builder, *currentOpLoc, outType, operand->front(),
1455 LDBG() <<
"Built operation: " << op;
1456 return {{op.getResult()}};
1460 builder, exprParser) {
1461 return exprParser.buildConvertOp<DemoteOp, double,
float>(builder);
1466 return exprParser.buildConvertOp<WrapOp, int64_t, int32_t>(builder);
1469#define BUILD_CONVERSION_OP(IN_T, OUT_T, SOURCE_OP, TARGET_OP) \
1471 constexpr bool ExpressionParser::TopLevelInstParserRegistry:: \
1472 hasParserForOpcode<WasmBinaryEncoding::OpCode::SOURCE_OP> = true; \
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); \
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)
1489#undef BUILD_CONVERT_OP_FOR
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)
1500#undef BUILD_TRUNC_OP_FOR
1505#undef BUILD_CONVERSION_OP
1507#define BUILD_SLICE_EXTEND_PARSER(IT_WIDTH, EXTRACT_WIDTH) \
1510 ExpressionParser::TopLevelInstParserRegistry::hasParserForOpcode< \
1511 WasmBinaryEncoding::OpCode::extendI##IT_WIDTH##EXTRACT_WIDTH##S> = \
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>( \
1530#undef BUILD_SLICE_EXTEND_PARSER
1533 builder, exprParser) {
1534 return exprParser.buildConvertOp<PromoteOp, float,
double>(builder);
1537#define BUILD_REINTERPRET_PARSER(WIDTH, FP_TYPE) \
1540 ExpressionParser::TopLevelInstParserRegistry::hasParserForOpcode< \
1541 WasmBinaryEncoding::OpCode::reinterpretF##WIDTH##AsI##WIDTH> = true; \
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>( \
1553 ExpressionParser::TopLevelInstParserRegistry::hasParserForOpcode< \
1554 WasmBinaryEncoding::OpCode::reinterpretI##WIDTH##AsF##WIDTH> = true; \
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>( \
1567#undef BUILD_REINTERPRET_PARSER
1570ExpressionParser::dispatchToInstParser(std::byte opCode,
OpBuilder &builder) {
1571 return InstDispatcher<ExpressionParser::TopLevelInstParserRegistry>::dispatch(
1572 opCode, builder, *
this);
1574class WasmBinaryParser {
1576 struct SectionRegistry {
1577 using section_location_t = StringRef;
1579 std::array<SmallVector<section_location_t>, highestWasmSectionID + 1>
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]);
1592 return registry[idx];
1596 bool hasSection(WasmSectionType secType)
const {
1597 return !registry[
static_cast<size_t>(secType)].empty();
1605 LogicalResult registerSection(WasmSectionType secType,
1606 section_location_t location, Location loc) {
1607 if (sectionShouldBeUnique(secType) && hasSection(secType))
1609 "trying to add a second instance of unique section");
1611 registry[
static_cast<size_t>(secType)].
push_back(location);
1612 emitRemark(loc,
"Adding section with section ID ")
1613 <<
static_cast<uint8_t
>(secType);
1617 LogicalResult populateFromBody(ParserHead ph) {
1619 FileLineColLoc sectionLoc = ph.getLocation();
1620 FailureOr<WasmSectionType> secType = ph.parseWasmSectionType();
1624 FailureOr<uint32_t> secSizeParsed = ph.parseLiteral<uint32_t>();
1625 if (
failed(secSizeParsed))
1628 uint32_t secSize = *secSizeParsed;
1629 FailureOr<StringRef> sectionContent = ph.consumeNBytes(secSize);
1630 if (
failed(sectionContent))
1633 LogicalResult registration =
1634 registerSection(*secType, *sectionContent, sectionLoc);
1636 if (
failed(registration))
1643 auto getLocation(
int offset = 0)
const {
1647 template <WasmSectionType>
1648 LogicalResult parseSectionItem(ParserHead &,
size_t);
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]() {
1659 auto secContent = registry.getContentForSection<section>();
1661 LDBG() << secName <<
" section is not present in file.";
1665 auto secSrc = secContent.value();
1666 ParserHead ph{secSrc, sectionNameAttr};
1667 FailureOr<uint32_t> nElemsParsed = ph.parseVectorSize();
1668 if (
failed(nElemsParsed))
1670 uint32_t nElems = *nElemsParsed;
1671 LDBG() <<
"starting to parse " << nElems <<
" items for section "
1673 for (
size_t i = 0; i < nElems; ++i) {
1674 if (
failed(parseSectionItem<section>(ph, i)))
1679 return emitError(getLocation(),
"unparsed garbage at end of section ")
1685 LogicalResult visitImport(Location loc, StringRef moduleName,
1686 StringRef importName, TypeIdxRecord tid) {
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,
1697 return funcOp.verify();
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);
1707 return memOp.verify();
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);
1717 return tableOp.verify();
1721 LogicalResult visitImport(Location loc, StringRef moduleName,
1722 StringRef importName, GlobalTypeRecord globalType) {
1723 std::string symbol = symbols.getNewGlobalSymbolName();
1725 GlobalImportOp::create(builder, loc, symbol, moduleName, importName,
1726 globalType.type, globalType.isMutable);
1727 symbols.globalSymbols.push_back(
1729 return giOp.verify();
1733 LogicalResult peekDiag(Diagnostic &
diag) {
1734 if (
diag.getSeverity() == DiagnosticSeverity::Error)
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");
1749 uint32_t sourceBufId = sourceMgr.getMainFileID();
1750 StringRef source = sourceMgr.getMemoryBuffer(sourceBufId)->getBuffer();
1751 srcName = StringAttr::get(
1752 ctx, sourceMgr.getMemoryBuffer(sourceBufId)->getBufferIdentifier());
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");
1762 auto const expectedVersionString = StringRef{
"\1\0\0\0", 4};
1763 FileLineColLoc versionLoc = parser.getLocation();
1764 FailureOr<StringRef> version =
1765 parser.consumeNBytes(expectedVersionString.size());
1768 if (version->compare(expectedVersionString)) {
1770 "unsupported Wasm version. only version 1 is supported");
1773 LogicalResult fillRegistry = registry.populateFromBody(parser.copy());
1774 if (
failed(fillRegistry))
1777 mOp = ModuleOp::create(builder, getLocation());
1779 LogicalResult parsingTypes = parseSection<WasmSectionType::TYPE>();
1780 if (
failed(parsingTypes))
1783 LogicalResult parsingImports = parseSection<WasmSectionType::IMPORT>();
1784 if (
failed(parsingImports))
1787 firstInternalFuncID = symbols.funcSymbols.size();
1789 LogicalResult parsingFunctions = parseSection<WasmSectionType::FUNCTION>();
1790 if (
failed(parsingFunctions))
1793 LogicalResult parsingTables = parseSection<WasmSectionType::TABLE>();
1794 if (
failed(parsingTables))
1797 LogicalResult parsingMems = parseSection<WasmSectionType::MEMORY>();
1801 LogicalResult parsingGlobals = parseSection<WasmSectionType::GLOBAL>();
1802 if (
failed(parsingGlobals))
1805 LogicalResult parsingCode = parseSection<WasmSectionType::CODE>();
1809 LogicalResult parsingExports = parseSection<WasmSectionType::EXPORT>();
1810 if (
failed(parsingExports))
1814 LDBG() <<
"WASM Imports:"
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();
1822 ModuleOp getModule() {
1831 mlir::StringAttr srcName;
1833 WasmModuleSymbolTables symbols;
1836 SectionRegistry registry;
1837 size_t firstInternalFuncID{0};
1843WasmBinaryParser::parseSectionItem<WasmSectionType::IMPORT>(ParserHead &ph,
1845 FileLineColLoc importLoc = ph.getLocation();
1846 auto moduleName = ph.parseName();
1850 auto importName = ph.parseName();
1854 FailureOr<ImportDesc>
import = ph.parseImportDesc(ctx);
1859 [
this, importLoc, &moduleName, &importName](
auto import) {
1860 return visitImport(importLoc, *moduleName, *importName,
import);
1867WasmBinaryParser::parseSectionItem<WasmSectionType::EXPORT>(ParserHead &ph,
1869 FileLineColLoc exportLoc = ph.getLocation();
1871 auto exportName = ph.parseName();
1875 FailureOr<std::byte> opcode = ph.consumeByte();
1879 FailureOr<uint32_t> idx = ph.parseLiteral<uint32_t>();
1883 using SymbolRefDesc = std::variant<SmallVector<SymbolRefContainer>,
1884 SmallVector<GlobalSymbolRefContainer>,
1885 SmallVector<FunctionSymbolRefContainer>>;
1887 SymbolRefDesc currentSymbolList;
1888 std::string symbolType =
"";
1891 symbolType =
"function";
1892 currentSymbolList = symbols.funcSymbols;
1895 symbolType =
"table";
1896 currentSymbolList = symbols.tableSymbols;
1899 symbolType =
"memory";
1900 currentSymbolList = symbols.memSymbols;
1903 symbolType =
"global";
1904 currentSymbolList = symbols.globalSymbols;
1907 return emitError(exportLoc,
"invalid value for export type: ")
1908 << std::to_integer<unsigned>(*opcode);
1911 auto currentSymbol = std::visit(
1912 [&](
const auto &list) -> FailureOr<FlatSymbolRefAttr> {
1913 if (*idx > list.size()) {
1917 "trying to export {0} {1} which is undefined in this scope",
1921 return list[*idx].symbol;
1925 if (
failed(currentSymbol))
1931 return SymbolTable{mOp}.rename(symName, *exportName);
1936WasmBinaryParser::parseSectionItem<WasmSectionType::TABLE>(ParserHead &ph,
1938 FileLineColLoc opLocation = ph.getLocation();
1939 FailureOr<TableType> tableType = ph.parseTableType(ctx);
1942 LDBG() <<
" Parsed table description: " << *tableType;
1943 StringAttr symbol = builder.
getStringAttr(symbols.getNewTableSymbolName());
1945 TableOp::create(builder, opLocation, symbol.strref(), *tableType);
1946 symbols.tableSymbols.push_back({SymbolRefAttr::get(tableOp)});
1952WasmBinaryParser::parseSectionItem<WasmSectionType::FUNCTION>(ParserHead &ph,
1954 FileLineColLoc opLoc = ph.getLocation();
1955 auto typeIdxParsed = ph.parseLiteral<uint32_t>();
1956 if (
failed(typeIdxParsed))
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();
1963 FuncOp::create(builder, opLoc, symbol, symbols.moduleFuncTypes[typeIdx]);
1964 Block *block = funcOp.addEntryBlock();
1965 OpBuilder::InsertionGuard guard{builder};
1967 ReturnOp::create(builder, opLoc);
1968 symbols.funcSymbols.push_back(
1970 symbols.moduleFuncTypes[typeIdx]});
1971 return funcOp.verify();
1976WasmBinaryParser::parseSectionItem<WasmSectionType::TYPE>(ParserHead &ph,
1978 FailureOr<FunctionType> funcType = ph.parseFunctionType(ctx);
1981 LDBG() <<
"Parsed function type " << *funcType;
1982 symbols.moduleFuncTypes.push_back(*funcType);
1988WasmBinaryParser::parseSectionItem<WasmSectionType::MEMORY>(ParserHead &ph,
1990 FileLineColLoc opLocation = ph.getLocation();
1991 FailureOr<LimitType> memory = ph.parseLimit(ctx);
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)});
2004WasmBinaryParser::parseSectionItem<WasmSectionType::GLOBAL>(ParserHead &ph,
2006 FileLineColLoc globalLocation = ph.getLocation();
2007 auto globalTypeParsed = ph.parseGlobalType(ctx);
2008 if (
failed(globalTypeParsed))
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(
2017 OpBuilder::InsertionGuard guard{builder};
2020 parsed_inst_t expr = ph.parseExpression(builder, symbols);
2024 return emitError(globalLocation,
"global with empty initializer");
2025 if (expr->size() != 1 && (*expr)[0].getType() != globalType.type)
2028 "initializer result type does not match global declaration type");
2029 ReturnOp::create(builder, globalLocation, *expr);
2034LogicalResult WasmBinaryParser::parseSectionItem<WasmSectionType::CODE>(
2035 ParserHead &ph,
size_t innerFunctionId) {
2036 unsigned long funcId = innerFunctionId + firstInternalFuncID;
2037 FunctionSymbolRefContainer symRef = symbols.funcSymbols[funcId];
2041 if (
failed(ph.parseCodeFor(funcOp, symbols)))
2047namespace mlir::wasm {
2050 WasmBinaryParser wBN{source, context};
2051 ModuleOp mOp = wBN.getModule();
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 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)
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
OpListType & getOperations()
BlockArgListType getArguments()
FunctionType getFunctionType(TypeRange inputs, TypeRange results)
StringAttr getStringAttr(const Twine &bytes)
MLIRContext * getContext() const
IntegerAttr getUI32IntegerAttr(uint32_t value)
HandlerID registerHandler(HandlerTy handler)
Register a new handler for diagnostics to the engine.
static FileLineColLoc get(StringAttr filename, unsigned line, unsigned column)
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.
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.
InsertPoint saveInsertionPoint() const
Return a saved insertion point.
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.
Block * getBlock() const
Returns the current block of the builder.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
void restoreInsertionPoint(InsertPoint ip)
Restore the insert point to a previously saved point.
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
MLIRContext * getContext()
Return the context this operation is associated with.
This class acts as an owning reference to an op, and will automatically destroy the held op on destru...
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.
QueryRef parse(llvm::StringRef line, const QuerySession &qs)
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.
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.
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