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"
34#define DEBUG_TYPE "wasm-translate"
36static_assert(CHAR_BIT == 8,
37 "This code expects std::byte to be exactly 8 bits");
44using section_id_t = uint8_t;
45enum struct WasmSectionType : section_id_t {
61constexpr section_id_t highestWasmSectionID{
62 static_cast<section_id_t
>(WasmSectionType::DATACOUNT)};
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)
79template <WasmSectionType>
80constexpr const char *wasmSectionName =
"";
82#define WASM_SEC_TRANSFORM(section) \
84 [[maybe_unused]] constexpr const char \
85 *wasmSectionName<WasmSectionType::section> = #section;
87#undef WASM_SEC_TRANSFORM
89constexpr bool sectionShouldBeUnique(WasmSectionType secType) {
90 return secType != WasmSectionType::CUSTOM;
93template <std::byte... Bytes>
94struct ByteSequence {};
97template <std::
byte Byte>
98struct UniqueByte : ByteSequence<Byte> {};
100[[maybe_unused]]
constexpr ByteSequence<
105template <std::byte... allowedFlags>
106constexpr bool isValueOneOf(std::byte value,
107 ByteSequence<allowedFlags...> = {}) {
108 return ((value == allowedFlags) | ... |
false);
111template <std::byte... flags>
112constexpr bool isNotIn(std::byte value, ByteSequence<flags...> = {}) {
113 return !isValueOneOf<flags...>(value);
116struct GlobalTypeRecord {
121struct TypeIdxRecord {
125struct SymbolRefContainer {
126 FlatSymbolRefAttr symbol;
129struct GlobalSymbolRefContainer : SymbolRefContainer {
133struct FunctionSymbolRefContainer : SymbolRefContainer {
134 FunctionType functionType;
138 std::variant<TypeIdxRecord, TableType, LimitType, GlobalTypeRecord>;
140using parsed_inst_t = FailureOr<SmallVector<Value>>;
142struct EmptyBlockMarker {};
143using BlockTypeParseResult =
144 std::variant<EmptyBlockMarker, TypeIdxRecord, Type>;
146struct WasmModuleSymbolTables {
147 SmallVector<FunctionSymbolRefContainer> funcSymbols;
148 SmallVector<GlobalSymbolRefContainer> globalSymbols;
149 SmallVector<SymbolRefContainer> memSymbols;
150 SmallVector<SymbolRefContainer> tableSymbols;
151 SmallVector<FunctionType> moduleFuncTypes;
153 std::string getNewSymbolName(StringRef prefix,
size_t id)
const {
154 return (prefix + Twine{
id}).str();
157 std::string getNewFuncSymbolName()
const {
158 size_t id = funcSymbols.size();
159 return getNewSymbolName(
"func_",
id);
162 std::string getNewGlobalSymbolName()
const {
163 size_t id = globalSymbols.size();
164 return getNewSymbolName(
"global_",
id);
167 std::string getNewMemorySymbolName()
const {
168 size_t id = memSymbols.size();
169 return getNewSymbolName(
"mem_",
id);
172 std::string getNewTableSymbolName()
const {
173 size_t id = tableSymbols.size();
174 return getNewSymbolName(
"table_",
id);
190 LabelLevelOpInterface levelOp;
194 bool empty()
const {
return values.empty(); }
196 size_t size()
const {
return values.size(); }
206 FailureOr<SmallVector<Value>> popOperands(
TypeRange operandTypes,
215 LogicalResult pushResults(
ValueRange results, Location *opLoc);
217 void addLabelLevel(LabelLevelOpInterface levelOp) {
218 labelLevel.push_back({values.size(), levelOp});
219 LDBG() <<
"Adding a new frame context to ValueStack";
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);
227#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
230 LLVM_DUMP_METHOD
void dump()
const;
234 SmallVector<Value> values;
235 SmallVector<LabelLevel> labelLevel;
240class ExpressionParser {
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} {}
248 template <std::
byte opCode>
249 inline parsed_inst_t parseSpecificInstruction(OpBuilder &builder);
251 template <
typename valueT>
253 parseConstInst(OpBuilder &builder,
254 std::enable_if_t<std::is_arithmetic_v<valueT>> * =
nullptr);
265 template <
typename opcode,
typename valueType,
unsigned int numOperands>
267 buildNumericOp(OpBuilder &builder,
268 std::enable_if_t<std::is_arithmetic_v<valueType>> * =
nullptr);
279 template <
typename opType,
typename inputType,
typename outputType,
280 typename... extraArgsT>
281 inline parsed_inst_t buildConvertOp(OpBuilder &builder, extraArgsT...);
294 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}>(
308 return dispatchToInstParser<nextPatternBitSize, nextHighBitPatternStem>(
311 return parseSpecificInstruction<highBitPattern>(builder);
318 struct NestingContextGuard {
319 NestingContextGuard(ExpressionParser &parser, LabelLevelOpInterface levelOp)
321 parser.addNestingContextLevel(levelOp);
323 NestingContextGuard(NestingContextGuard &&other) : parser{other.parser} {
324 other.shouldDropOnDestruct =
false;
326 NestingContextGuard(NestingContextGuard
const &) =
delete;
327 ~NestingContextGuard() {
328 if (shouldDropOnDestruct)
329 parser.dropNestingContextLevel();
331 ExpressionParser &parser;
332 bool shouldDropOnDestruct =
true;
335 void addNestingContextLevel(LabelLevelOpInterface levelOp) {
336 valueStack.addLabelLevel(levelOp);
339 void dropNestingContextLevel() {
342 valueStack.dropLabelLevel();
345 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
350 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
351 TypeIdxRecord type) {
352 if (type.id >= symbols.moduleFuncTypes.size())
354 "type index references nonexistent type (")
355 << type.id <<
"). Only " << symbols.moduleFuncTypes.size()
356 <<
" types are registered";
357 return symbols.moduleFuncTypes[type.id];
360 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
365 llvm::FailureOr<FunctionType>
366 getFuncTypeFor(OpBuilder &builder, BlockTypeParseResult parseResult) {
368 [
this, &builder](
auto value) {
return getFuncTypeFor(builder, value); },
372 llvm::FailureOr<FunctionType>
373 getFuncTypeFor(OpBuilder &builder,
374 llvm::FailureOr<BlockTypeParseResult> parseResult) {
375 if (llvm::failed(parseResult))
377 return getFuncTypeFor(builder, *parseResult);
380 llvm::FailureOr<FunctionType> parseBlockFuncType(OpBuilder &builder);
382 struct ParseResultWithInfo {
383 SmallVector<Value> opResults;
384 std::byte endingByte;
387 template <
typename FilterT = ByteSequence<WasmBinaryEncoding::endByte>>
390 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};
396 LDBG() <<
"parsing a block of type "
399 auto nC = addNesting(levelOp);
403 auto bodyParsingRes =
parse(builder, parseEndBytes);
404 if (
failed(bodyParsingRes))
406 auto returnOperands = popOperands(resTypes);
407 if (
failed(returnOperands))
409 BlockReturnOp::create(builder, opLoc, *returnOperands);
410 LDBG() <<
"end of parsing of a block";
411 return bodyParsingRes->endingByte;
415 template <std::
byte ParseEndByte = WasmBinaryEncoding::endByte>
416 parsed_inst_t
parse(OpBuilder &builder, UniqueByte<ParseEndByte> = {});
418 template <std::byte... ExpressionParseEnd>
419 FailureOr<ParseResultWithInfo>
420 parse(OpBuilder &builder,
421 ByteSequence<ExpressionParseEnd...> parsingEndFilters);
423 NestingContextGuard addNesting(LabelLevelOpInterface levelOp) {
424 return NestingContextGuard{*
this, levelOp};
427 FailureOr<llvm::SmallVector<Value>> popOperands(
TypeRange operandTypes) {
428 return valueStack.popOperands(operandTypes, ¤tOpLoc.value());
431 LogicalResult pushResults(
ValueRange results) {
432 return valueStack.pushResults(results, ¤tOpLoc.value());
438 template <
typename OpToCreate>
439 parsed_inst_t parseSetOrTee(OpBuilder &);
444 template <
typename OpToCreate>
445 parsed_inst_t parseBlockLikeOp(OpBuilder &);
448 std::optional<Location> currentOpLoc;
450 WasmModuleSymbolTables
const &symbols;
452 ValueStack valueStack;
457 ParserHead(StringRef src, StringAttr name) : head{src}, locName{name} {}
458 ParserHead(ParserHead &&) =
default;
461 ParserHead(ParserHead
const &other) =
default;
464 auto getLocation()
const {
468 FailureOr<StringRef> consumeNBytes(
size_t nBytes) {
469 LDBG() <<
"Consume " << nBytes <<
" bytes";
470 LDBG() <<
" Bytes remaining: " << size();
471 LDBG() <<
" Current offset: " << offset;
473 return emitError(getLocation(),
"trying to extract ")
474 << nBytes <<
"bytes when only " << size() <<
"are available";
476 StringRef res = head.slice(offset, offset + nBytes);
478 LDBG() <<
" Updated offset (+" << nBytes <<
"): " << offset;
482 FailureOr<std::byte> consumeByte() {
483 FailureOr<StringRef> res = consumeNBytes(1);
486 return std::byte{*res->bytes_begin()};
489 template <
typename T>
490 FailureOr<T> parseLiteral();
492 FailureOr<uint32_t> parseVectorSize();
498 inline FailureOr<uint32_t> parseUI32();
499 inline FailureOr<int64_t> parseI64();
502 FailureOr<StringRef> parseName() {
503 FailureOr<uint32_t> size = parseVectorSize();
507 return consumeNBytes(*size);
510 FailureOr<WasmSectionType> parseWasmSectionType() {
511 FailureOr<std::byte>
id = consumeByte();
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);
520 FailureOr<LimitType> parseLimit(MLIRContext *ctx) {
521 using WasmLimits = WasmBinaryEncoding::LimitHeader;
522 FileLineColLoc limitLocation = getLocation();
523 FailureOr<std::byte> limitHeader = consumeByte();
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();
533 std::optional<uint32_t>
max{std::nullopt};
534 if (*limitHeader == WasmLimits::bothLimits) {
535 FailureOr<uint32_t> maxParse = parseUI32();
540 return LimitType::get(ctx, *minParse,
max);
543 FailureOr<Type> parseValueType(MLIRContext *ctx) {
544 FileLineColLoc typeLoc = getLocation();
545 FailureOr<std::byte> typeEncoding = consumeByte();
548 switch (*typeEncoding) {
550 return IntegerType::get(ctx, 32);
552 return IntegerType::get(ctx, 64);
554 return Float32Type::get(ctx);
556 return Float64Type::get(ctx);
558 return IntegerType::get(ctx, 128);
560 return wasmssa::FuncRefType::get(ctx);
562 return wasmssa::ExternRefType::get(ctx);
564 return emitError(typeLoc,
"invalid value type encoding: ")
565 <<
static_cast<int>(*typeEncoding);
569 FailureOr<GlobalTypeRecord> parseGlobalType(MLIRContext *ctx) {
570 using WasmGlobalMut = WasmBinaryEncoding::GlobalMutability;
571 FailureOr<Type> typeParsed = parseValueType(ctx);
574 FileLineColLoc mutLoc = getLocation();
575 FailureOr<std::byte> mutSpec = consumeByte();
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};
584 FailureOr<TupleType> parseResultType(MLIRContext *ctx) {
585 FailureOr<uint32_t> nParamsParsed = parseVectorSize();
586 if (
failed(nParamsParsed))
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);
595 res.push_back(*parsedType);
597 return TupleType::get(ctx, res);
600 FailureOr<FunctionType> parseFunctionType(MLIRContext *ctx) {
601 FileLineColLoc typeLoc = getLocation();
602 FailureOr<std::byte> funcTypeHeader = consumeByte();
603 if (
failed(funcTypeHeader))
606 return emitError(typeLoc,
"invalid function type header byte. Expecting ")
608 <<
" got " << std::to_integer<unsigned>(*funcTypeHeader);
609 FailureOr<TupleType> inputTypes = parseResultType(ctx);
613 FailureOr<TupleType> resTypes = parseResultType(ctx);
617 return FunctionType::get(ctx, inputTypes->getTypes(), resTypes->getTypes());
620 FailureOr<TypeIdxRecord> parseTypeIndex() {
621 FailureOr<uint32_t> res = parseUI32();
624 return TypeIdxRecord{*res};
627 FailureOr<TableType> parseTableType(MLIRContext *ctx) {
628 FailureOr<Type> elmTypeParse = parseValueType(ctx);
631 if (!isWasmRefType(*elmTypeParse))
632 return emitError(getLocation(),
"invalid element type for table");
633 FailureOr<LimitType> limitParse = parseLimit(ctx);
636 return TableType::get(ctx, *elmTypeParse, *limitParse);
639 FailureOr<ImportDesc> parseImportDesc(MLIRContext *ctx) {
640 FileLineColLoc importLoc = getLocation();
641 FailureOr<std::byte> importType = consumeByte();
642 auto packager = [](
auto parseResult) -> FailureOr<ImportDesc> {
645 return {*parseResult};
649 switch (*importType) {
651 return packager(parseTypeIndex());
653 return packager(parseTableType(ctx));
655 return packager(parseLimit(ctx));
657 return packager(parseGlobalType(ctx));
659 return emitError(importLoc,
"invalid import type descriptor: ")
660 <<
static_cast<int>(*importType);
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);
671 LogicalResult parseCodeFor(FuncOp func,
672 WasmModuleSymbolTables
const &symbols) {
673 SmallVector<local_val_t> locals{};
675 Block &block = func.getBody().front();
677 assert(func.getBody().getBlocks().size() == 1 &&
678 "Function should only have its default created block at this point");
680 "Only the placeholder return op should be present at this point");
681 auto returnOp = cast<ReturnOp>(&block.
back());
684 FailureOr<uint32_t> codeSizeInBytes = parseUI32();
685 if (
failed(codeSizeInBytes))
687 FailureOr<StringRef> codeContent = consumeNBytes(*codeSizeInBytes);
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();
696 OpBuilder builder{&func.getBody().front().back()};
700 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();
706 FailureOr<Type> varT = cParser.parseValueType(func->getContext());
709 for (
size_t j = 0; j < *nSubVar; ++j) {
710 auto local = LocalOp::create(builder, varLoc, *varT);
711 locals.push_back(local.getResult());
714 parsed_inst_t res = cParser.parseExpression(builder, symbols, locals);
719 "unparsed garbage remaining at end of code block");
720 ReturnOp::create(builder, func->getLoc(), *res);
725 llvm::FailureOr<BlockTypeParseResult> parseBlockType(MLIRContext *ctx) {
726 auto loc = getLocation();
727 auto blockIndicator = peek();
728 if (
failed(blockIndicator))
732 return {EmptyBlockMarker{}};
734 if (isValueOneOf(*blockIndicator, valueTypesEncodings))
735 return parseValueType(ctx);
738 auto typeIdx = parseI64();
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 ")
745 return {TypeIdxRecord{
static_cast<uint32_t
>(*typeIdx)}};
748 bool end()
const {
return curHead().empty(); }
750 ParserHead
copy()
const {
return *
this; }
753 StringRef curHead()
const {
return head.drop_front(offset); }
755 FailureOr<std::byte> peek()
const {
759 "trying to peek at next byte, but input stream is empty");
760 return static_cast<std::byte
>(curHead().front());
763 size_t size()
const {
return head.size() - offset; }
767 unsigned anchorOffset{0};
772FailureOr<float> ParserHead::parseLiteral<float>() {
773 FailureOr<StringRef> bytes = consumeNBytes(4);
776 return llvm::support::endian::read<float>(bytes->bytes_begin(),
777 llvm::endianness::little);
781FailureOr<double> ParserHead::parseLiteral<double>() {
782 FailureOr<StringRef> bytes = consumeNBytes(8);
785 return llvm::support::endian::read<double>(bytes->bytes_begin(),
786 llvm::endianness::little);
790FailureOr<uint32_t> ParserHead::parseLiteral<uint32_t>() {
791 char const *error =
nullptr;
793 unsigned encodingSize{0};
794 StringRef src = curHead();
795 uint64_t decoded = llvm::decodeULEB128(src.bytes_begin(), &encodingSize,
796 src.bytes_end(), &error);
800 if (std::isgreater(decoded, std::numeric_limits<uint32_t>::max()))
801 return emitError(getLocation()) <<
"literal does not fit on 32 bits";
803 res =
static_cast<uint32_t
>(decoded);
804 offset += encodingSize;
809FailureOr<int32_t> ParserHead::parseLiteral<int32_t>() {
810 char const *error =
nullptr;
812 unsigned encodingSize{0};
813 StringRef src = curHead();
814 int64_t decoded = llvm::decodeSLEB128(src.bytes_begin(), &encodingSize,
815 src.bytes_end(), &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";
822 res =
static_cast<int32_t
>(decoded);
823 offset += encodingSize;
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);
837 offset += encodingSize;
841FailureOr<uint32_t> ParserHead::parseVectorSize() {
842 return parseLiteral<uint32_t>();
845inline FailureOr<uint32_t> ParserHead::parseUI32() {
846 return parseLiteral<uint32_t>();
849inline FailureOr<int64_t> ParserHead::parseI64() {
850 return parseLiteral<int64_t>();
853template <std::
byte opCode>
854inline parsed_inst_t ExpressionParser::parseSpecificInstruction(OpBuilder &) {
855 return emitError(*currentOpLoc,
"unknown instruction opcode: ")
856 <<
static_cast<int>(opCode);
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>"
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';
875 auto frameId = idx - 1;
876 auto frameLimit = labelLevel[frameId].stackIdx;
878 return {{frameId, frameLimit}};
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->first
889 nextFrameIdx = getNextFrameIndex();
892 values[actualIdx].dump();
894 while (nextFrameIdx) {
895 llvm::dbgs() <<
" --------------- Frame (" << nextFrameIdx->first <<
")\n";
896 nextFrameIdx = getNextFrameIndex();
898 llvm::dbgs() <<
"<Bottom>"
900 llvm::dbgs() <<
"=========================================================\n";
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())
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);
925 values.resize(values.size() - operandTypes.size());
926 LDBG() <<
" Updated stack size: " << values.size();
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: ")
938 LDBG() <<
" PUSH: " << val;
939 values.push_back(val);
942 LDBG() <<
" Updated stack size: " << values.size();
946template <std::
byte EndParseByte>
947parsed_inst_t ExpressionParser::parse(OpBuilder &builder,
948 UniqueByte<EndParseByte> endByte) {
949 auto res =
parse(builder, ByteSequence<EndParseByte>{});
952 return res->opResults;
955template <std::byte... ExpressionParseEnd>
956FailureOr<ExpressionParser::ParseResultWithInfo>
957ExpressionParser::parse(OpBuilder &builder,
958 ByteSequence<ExpressionParseEnd...> parsingEndFilters) {
959 SmallVector<Value> res;
961 currentOpLoc = parser.getLocation();
962 FailureOr<std::byte> opCode = parser.consumeByte();
965 if (isValueOneOf(*opCode, parsingEndFilters))
966 return {{res, *opCode}};
967 parsed_inst_t resParsed;
968 resParsed = dispatchToInstParser(*opCode, builder);
971 std::swap(res, *resParsed);
972 if (
failed(pushResults(res)))
977llvm::FailureOr<FunctionType>
978ExpressionParser::parseBlockFuncType(OpBuilder &builder) {
979 return getFuncTypeFor(builder, parser.parseBlockType(builder.
getContext()));
982template <
typename OpToCreate>
983parsed_inst_t ExpressionParser::parseBlockLikeOp(OpBuilder &builder) {
984 auto opLoc = currentOpLoc;
985 auto funcType = parseBlockFuncType(builder);
989 auto inputTypes = funcType->getInputs();
990 auto inputOps = popOperands(inputTypes);
995 Region *curRegion = curBlock->
getParent();
996 auto resTypes = funcType->getResults();
997 llvm::SmallVector<Location> locations{};
998 locations.resize(resTypes.size(), *currentOpLoc);
1000 builder.
createBlock(curRegion, curRegion->
end(), resTypes, locations);
1003 OpToCreate::create(builder, *currentOpLoc, *inputOps, successor);
1004 auto *blockBody = blockOp.createBlock();
1005 if (
failed(parseBlockContent(builder, blockBody, resTypes, *opLoc, blockOp)))
1008 return {
ValueRange{successor->getArguments()}};
1013ExpressionParser::parseSpecificInstruction<WasmBinaryEncoding::OpCode::block>(
1014 OpBuilder &builder) {
1015 return parseBlockLikeOp<BlockOp>(builder);
1020ExpressionParser::parseSpecificInstruction<WasmBinaryEncoding::OpCode::loop>(
1021 OpBuilder &builder) {
1022 return parseBlockLikeOp<LoopOp>(builder);
1026inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1028 auto opLoc = currentOpLoc;
1029 auto funcType = parseBlockFuncType(builder);
1033 LDBG() <<
"Parsing an if instruction of type " << *funcType;
1034 auto inputTypes = funcType->getInputs();
1035 auto conditionValue = popOperands(builder.
getI32Type());
1036 if (
failed(conditionValue))
1038 auto inputOps = popOperands(inputTypes);
1043 Region *curRegion = curBlock->
getParent();
1044 auto resTypes = funcType->getResults();
1045 llvm::SmallVector<Location> locations{};
1046 locations.resize(resTypes.size(), *currentOpLoc);
1048 builder.
createBlock(curRegion, curRegion->
end(), resTypes, locations);
1050 auto ifOp = IfOp::create(builder, *currentOpLoc, conditionValue->front(),
1051 *inputOps, successor);
1052 auto *ifEntryBlock = ifOp.createIfBlock();
1053 constexpr auto ifElseFilter =
1056 auto parseIfRes = parseBlockContent(builder, ifEntryBlock, resTypes, *opLoc,
1057 ifOp, ifElseFilter);
1061 LDBG() <<
" else block is present.";
1062 Block *elseEntryBlock = ifOp.createElseBlock();
1064 parseBlockContent(builder, elseEntryBlock, resTypes, *opLoc, ifOp);
1065 if (
failed(parseElseRes))
1069 return {
ValueRange{successor->getArguments()}};
1073inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1075 auto level = parser.parseLiteral<uint32_t>();
1079 Region *curRegion = curBlock->
getParent();
1082 auto condition = popOperands(builder.
getI32Type());
1087 LabelBranchingOpInterface::getTargetOpFromBlock(curBlock, *level);
1090 auto inputTypes = targetOp->getLabelTarget()->getArgumentTypes();
1091 auto branchArgs = popOperands(inputTypes);
1094 BranchIfOp::create(builder, *currentOpLoc, condition->front(),
1098 return {*branchArgs};
1103ExpressionParser::parseSpecificInstruction<WasmBinaryEncoding::OpCode::call>(
1104 OpBuilder &builder) {
1105 auto loc = *currentOpLoc;
1106 auto funcIdx = parser.parseLiteral<uint32_t>();
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);
1118 FuncCallOp::create(builder, loc, resTypes, callee.symbol, *inOperands);
1119 return {callOp.getResults()};
1123inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1125 FailureOr<uint32_t>
id = parser.parseLiteral<uint32_t>();
1126 Location instLoc = *currentOpLoc;
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()}};
1136inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1138 FailureOr<uint32_t>
id = parser.parseLiteral<uint32_t>();
1139 Location instLoc = *currentOpLoc;
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,
1150 return {{globalOp.getResult()}};
1153template <
typename OpToCreate>
1154parsed_inst_t ExpressionParser::parseSetOrTee(OpBuilder &builder) {
1155 FailureOr<uint32_t>
id = parser.parseLiteral<uint32_t>();
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())
1164 "invalid stack access, trying to access a value on an empty stack");
1170 OpToCreate::create(builder, *currentOpLoc, locals[*
id], poppedOp->front())
1175inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1177 return parseSetOrTee<LocalSetOp>(builder);
1181inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1183 return parseSetOrTee<LocalTeeOp>(builder);
1186template <
typename T>
1187inline Type buildLiteralType(OpBuilder &);
1190inline Type buildLiteralType<int32_t>(OpBuilder &builder) {
1195inline Type buildLiteralType<int64_t>(OpBuilder &builder) {
1200[[maybe_unused]]
inline Type buildLiteralType<uint32_t>(OpBuilder &builder) {
1205[[maybe_unused]]
inline Type buildLiteralType<uint64_t>(OpBuilder &builder) {
1210inline Type buildLiteralType<float>(OpBuilder &builder) {
1215inline Type buildLiteralType<double>(OpBuilder &builder) {
1219template <
typename ValT,
1220 typename E = std::enable_if_t<std::is_arithmetic_v<ValT>>>
1223template <
typename ValT>
1224struct AttrHolder<ValT, std::enable_if_t<std::is_integral_v<ValT>>> {
1225 using type = IntegerAttr;
1228template <
typename ValT>
1229struct AttrHolder<ValT, std::enable_if_t<std::is_floating_point_v<ValT>>> {
1230 using type = FloatAttr;
1233template <
typename ValT>
1234using attr_holder_t =
typename AttrHolder<ValT>::type;
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);
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))
1249 ConstOp::create(builder, *currentOpLoc,
1250 buildLiteralAttr<valueT>(builder, *parsedConstant));
1251 return {{constOp.getResult()}};
1255inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1257 return parseConstInst<int32_t>(builder);
1261inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1263 return parseConstInst<int64_t>(builder);
1267inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1269 return parseConstInst<float>(builder);
1273inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1275 return parseConstInst<double>(builder);
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 = " << numOperands
1283 <<
", type = " << ty <<
" ***";
1284 auto tysToPop = SmallVector<Type, numOperands>();
1285 tysToPop.resize(numOperands);
1286 llvm::fill(tysToPop, ty);
1287 auto operands = popOperands(tysToPop);
1290 auto op = opcode::create(builder, *currentOpLoc, *operands).getResult();
1291 LDBG() <<
"Built operation: " << op;
1296#define BUILD_NUMERIC_OP(OP_NAME, N_ARGS, PREFIX, SUFFIX, TYPE) \
1298 inline parsed_inst_t ExpressionParser::parseSpecificInstruction< \
1299 WasmBinaryEncoding::OpCode::PREFIX##SUFFIX>(OpBuilder & builder) { \
1300 return buildNumericOp<OP_NAME, TYPE, N_ARGS>(builder); \
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)
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)
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)
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)
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)
1374#undef BUILD_NUMERIC_BINOP_FP
1375#undef BUILD_NUMERIC_BINOP_INT
1376#undef BUILD_NUMERIC_BINOP_INTFP
1377#undef BUILD_NUMERIC_UNARY_OP_FP
1378#undef BUILD_NUMERIC_UNARY_OP_INT
1379#undef BUILD_NUMERIC_OP
1380#undef BUILD_NUMERIC_CAST_OP
1382template <
typename opType,
typename inputType,
typename outputType,
1383 typename... extraArgsT>
1384inline parsed_inst_t ExpressionParser::buildConvertOp(
OpBuilder &builder,
1385 extraArgsT... extraArgs) {
1386 static_assert(std::is_arithmetic_v<inputType>,
1387 "InputType should be an arithmetic type");
1388 static_assert(std::is_arithmetic_v<outputType>,
1389 "OutputType should be an arithmetic type");
1390 auto intype = buildLiteralType<inputType>(builder);
1391 auto outType = buildLiteralType<outputType>(builder);
1392 auto operand = popOperands(intype);
1393 if (failed(operand))
1395 auto op = opType::create(builder, *currentOpLoc, outType, operand->front(),
1397 LDBG() <<
"Built operation: " << op;
1398 return {{op.getResult()}};
1402inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1404 return buildConvertOp<DemoteOp, double, float>(builder);
1409ExpressionParser::parseSpecificInstruction<WasmBinaryEncoding::OpCode::wrap>(
1410 OpBuilder &builder) {
1411 return buildConvertOp<WrapOp, int64_t, int32_t>(builder);
1414#define BUILD_CONVERSION_OP(IN_T, OUT_T, SOURCE_OP, TARGET_OP) \
1416 inline parsed_inst_t ExpressionParser::parseSpecificInstruction< \
1417 WasmBinaryEncoding::OpCode::SOURCE_OP>(OpBuilder & builder) { \
1418 return buildConvertOp<TARGET_OP, IN_T, OUT_T>(builder); \
1421#define BUILD_CONVERT_OP_FOR(DEST_T, WIDTH) \
1422 BUILD_CONVERSION_OP(uint32_t, DEST_T, convertUI32F##WIDTH, ConvertUOp) \
1423 BUILD_CONVERSION_OP(int32_t, DEST_T, convertSI32F##WIDTH, ConvertSOp) \
1424 BUILD_CONVERSION_OP(uint64_t, DEST_T, convertUI64F##WIDTH, ConvertUOp) \
1425 BUILD_CONVERSION_OP(int64_t, DEST_T, convertSI64F##WIDTH, ConvertSOp)
1430#undef BUILD_CONVERT_OP_FOR
1432#define BUILD_TRUNC_OP_FOR(SRC_T, WIDTH) \
1433 BUILD_CONVERSION_OP(SRC_T, int32_t, truncSI32F##WIDTH, TruncSIOp) \
1434 BUILD_CONVERSION_OP(SRC_T, uint32_t, truncUI32F##WIDTH, TruncUIOp) \
1435 BUILD_CONVERSION_OP(SRC_T, int64_t, truncSI64F##WIDTH, TruncSIOp) \
1436 BUILD_CONVERSION_OP(SRC_T, uint64_t, truncUI64F##WIDTH, TruncUIOp)
1441#undef BUILD_TRUNC_OP_FOR
1446#undef BUILD_CONVERSION_OP
1448#define BUILD_SLICE_EXTEND_PARSER(IT_WIDTH, EXTRACT_WIDTH) \
1450 parsed_inst_t ExpressionParser::parseSpecificInstruction< \
1451 WasmBinaryEncoding::OpCode::extendI##IT_WIDTH##EXTRACT_WIDTH##S>( \
1452 OpBuilder & builder) { \
1453 using inout_t = int##IT_WIDTH##_t; \
1454 auto attr = builder.getUI32IntegerAttr(EXTRACT_WIDTH); \
1455 return buildConvertOp<ExtendLowBitsSOp, inout_t, inout_t>(builder, attr); \
1464#undef BUILD_SLICE_EXTEND_PARSER
1467inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
1469 return buildConvertOp<PromoteOp, float, double>(builder);
1472#define BUILD_REINTERPRET_PARSER(WIDTH, FP_TYPE) \
1474 inline parsed_inst_t ExpressionParser::parseSpecificInstruction< \
1475 WasmBinaryEncoding::OpCode::reinterpretF##WIDTH##AsI##WIDTH>(OpBuilder & \
1477 return buildConvertOp<ReinterpretOp, FP_TYPE, int##WIDTH##_t>(builder); \
1481 inline parsed_inst_t ExpressionParser::parseSpecificInstruction< \
1482 WasmBinaryEncoding::OpCode::reinterpretI##WIDTH##AsF##WIDTH>(OpBuilder & \
1484 return buildConvertOp<ReinterpretOp, int##WIDTH##_t, FP_TYPE>(builder); \
1490#undef BUILD_REINTERPRET_PARSER
1492class WasmBinaryParser {
1494 struct SectionRegistry {
1495 using section_location_t = StringRef;
1497 std::array<SmallVector<section_location_t>, highestWasmSectionID + 1>
1500 template <WasmSectionType SecType>
1501 std::conditional_t<sectionShouldBeUnique(SecType),
1502 std::optional<section_location_t>,
1504 getContentForSection()
const {
1505 constexpr auto idx =
static_cast<size_t>(SecType);
1506 if constexpr (sectionShouldBeUnique(SecType)) {
1507 return registry[idx].empty() ? std::nullopt
1508 : std::make_optional(registry[idx][0]);
1510 return registry[idx];
1514 bool hasSection(WasmSectionType secType)
const {
1515 return !registry[
static_cast<size_t>(secType)].empty();
1523 LogicalResult registerSection(WasmSectionType secType,
1524 section_location_t location,
Location loc) {
1525 if (sectionShouldBeUnique(secType) && hasSection(secType))
1527 "trying to add a second instance of unique section");
1529 registry[
static_cast<size_t>(secType)].
push_back(location);
1530 emitRemark(loc,
"Adding section with section ID ")
1531 <<
static_cast<uint8_t
>(secType);
1535 LogicalResult populateFromBody(ParserHead ph) {
1538 FailureOr<WasmSectionType> secType = ph.parseWasmSectionType();
1539 if (failed(secType))
1542 FailureOr<uint32_t> secSizeParsed = ph.parseLiteral<uint32_t>();
1543 if (failed(secSizeParsed))
1546 uint32_t secSize = *secSizeParsed;
1547 FailureOr<StringRef> sectionContent = ph.consumeNBytes(secSize);
1548 if (failed(sectionContent))
1551 LogicalResult registration =
1552 registerSection(*secType, *sectionContent, sectionLoc);
1554 if (failed(registration))
1561 auto getLocation(
int offset = 0)
const {
1565 template <WasmSectionType>
1566 LogicalResult parseSectionItem(ParserHead &,
size_t);
1568 template <WasmSectionType section>
1569 LogicalResult parseSection() {
1570 auto secName = std::string{wasmSectionName<section>};
1571 auto sectionNameAttr =
1572 StringAttr::get(ctx, srcName.strref() +
":" + secName +
"-SECTION");
1573 unsigned offset = 0;
1574 auto getLocation = [sectionNameAttr, &offset]() {
1577 auto secContent = registry.getContentForSection<section>();
1579 LDBG() << secName <<
" section is not present in file.";
1583 auto secSrc = secContent.value();
1584 ParserHead ph{secSrc, sectionNameAttr};
1585 FailureOr<uint32_t> nElemsParsed = ph.parseVectorSize();
1586 if (failed(nElemsParsed))
1588 uint32_t nElems = *nElemsParsed;
1589 LDBG() <<
"starting to parse " << nElems <<
" items for section "
1591 for (
size_t i = 0; i < nElems; ++i) {
1592 if (failed(parseSectionItem<section>(ph, i)))
1597 return emitError(getLocation(),
"unparsed garbage at end of section ")
1603 LogicalResult visitImport(
Location loc, StringRef moduleName,
1604 StringRef importName, TypeIdxRecord tid) {
1606 if (tid.id >= symbols.moduleFuncTypes.size())
1607 return emitError(loc,
"invalid type id: ")
1608 << tid.id <<
". Only " << symbols.moduleFuncTypes.size()
1609 <<
" type registrations";
1610 FunctionType type = symbols.moduleFuncTypes[tid.id];
1611 std::string symbol = symbols.getNewFuncSymbolName();
1612 auto funcOp = FuncImportOp::create(builder, loc, symbol, moduleName,
1615 return funcOp.verify();
1619 LogicalResult visitImport(
Location loc, StringRef moduleName,
1620 StringRef importName, LimitType limitType) {
1621 std::string symbol = symbols.getNewMemorySymbolName();
1622 auto memOp = MemImportOp::create(builder, loc, symbol, moduleName,
1623 importName, limitType);
1625 return memOp.verify();
1629 LogicalResult visitImport(
Location loc, StringRef moduleName,
1630 StringRef importName, TableType tableType) {
1631 std::string symbol = symbols.getNewTableSymbolName();
1632 auto tableOp = TableImportOp::create(builder, loc, symbol, moduleName,
1633 importName, tableType);
1635 return tableOp.verify();
1639 LogicalResult visitImport(
Location loc, StringRef moduleName,
1640 StringRef importName, GlobalTypeRecord globalType) {
1641 std::string symbol = symbols.getNewGlobalSymbolName();
1643 GlobalImportOp::create(builder, loc, symbol, moduleName, importName,
1644 globalType.type, globalType.isMutable);
1645 symbols.globalSymbols.push_back(
1647 return giOp.verify();
1658 WasmBinaryParser(llvm::SourceMgr &sourceMgr,
MLIRContext *ctx)
1659 : builder{ctx}, ctx{ctx} {
1663 if (sourceMgr.getNumBuffers() != 1) {
1664 emitError(UnknownLoc::get(ctx),
"one source file should be provided");
1667 uint32_t sourceBufId = sourceMgr.getMainFileID();
1668 StringRef source = sourceMgr.getMemoryBuffer(sourceBufId)->getBuffer();
1669 srcName = StringAttr::get(
1670 ctx, sourceMgr.getMemoryBuffer(sourceBufId)->getBufferIdentifier());
1672 auto parser = ParserHead{source, srcName};
1673 auto const wasmHeader = StringRef{
"\0asm", 4};
1675 FailureOr<StringRef> magic = parser.consumeNBytes(wasmHeader.size());
1676 if (failed(magic) || magic->compare(wasmHeader)) {
1677 emitError(magicLoc,
"source file does not contain valid Wasm header");
1680 auto const expectedVersionString = StringRef{
"\1\0\0\0", 4};
1682 FailureOr<StringRef> version =
1683 parser.consumeNBytes(expectedVersionString.size());
1684 if (failed(version))
1686 if (version->compare(expectedVersionString)) {
1688 "unsupported Wasm version. only version 1 is supported");
1691 LogicalResult fillRegistry = registry.populateFromBody(parser.copy());
1692 if (failed(fillRegistry))
1695 mOp = ModuleOp::create(builder, getLocation());
1697 LogicalResult parsingTypes = parseSection<WasmSectionType::TYPE>();
1698 if (failed(parsingTypes))
1701 LogicalResult parsingImports = parseSection<WasmSectionType::IMPORT>();
1702 if (failed(parsingImports))
1705 firstInternalFuncID = symbols.funcSymbols.size();
1707 LogicalResult parsingFunctions = parseSection<WasmSectionType::FUNCTION>();
1708 if (failed(parsingFunctions))
1711 LogicalResult parsingTables = parseSection<WasmSectionType::TABLE>();
1712 if (failed(parsingTables))
1715 LogicalResult parsingMems = parseSection<WasmSectionType::MEMORY>();
1716 if (failed(parsingMems))
1719 LogicalResult parsingGlobals = parseSection<WasmSectionType::GLOBAL>();
1720 if (failed(parsingGlobals))
1723 LogicalResult parsingCode = parseSection<WasmSectionType::CODE>();
1724 if (failed(parsingCode))
1727 LogicalResult parsingExports = parseSection<WasmSectionType::EXPORT>();
1728 if (failed(parsingExports))
1732 LDBG() <<
"WASM Imports:"
1734 <<
" - Num functions: " << symbols.funcSymbols.size() <<
"\n"
1735 <<
" - Num globals: " << symbols.globalSymbols.size() <<
"\n"
1736 <<
" - Num memories: " << symbols.memSymbols.size() <<
"\n"
1737 <<
" - Num tables: " << symbols.tableSymbols.size();
1740 ModuleOp getModule() {
1749 mlir::StringAttr srcName;
1751 WasmModuleSymbolTables symbols;
1754 SectionRegistry registry;
1755 size_t firstInternalFuncID{0};
1761WasmBinaryParser::parseSectionItem<WasmSectionType::IMPORT>(ParserHead &ph,
1763 FileLineColLoc importLoc = ph.getLocation();
1764 auto moduleName = ph.parseName();
1768 auto importName = ph.parseName();
1772 FailureOr<ImportDesc>
import = ph.parseImportDesc(ctx);
1777 [
this, importLoc, &moduleName, &importName](
auto import) {
1778 return visitImport(importLoc, *moduleName, *importName,
import);
1785WasmBinaryParser::parseSectionItem<WasmSectionType::EXPORT>(ParserHead &ph,
1787 FileLineColLoc exportLoc = ph.getLocation();
1789 auto exportName = ph.parseName();
1793 FailureOr<std::byte> opcode = ph.consumeByte();
1797 FailureOr<uint32_t> idx = ph.parseLiteral<uint32_t>();
1801 using SymbolRefDesc = std::variant<SmallVector<SymbolRefContainer>,
1802 SmallVector<GlobalSymbolRefContainer>,
1803 SmallVector<FunctionSymbolRefContainer>>;
1805 SymbolRefDesc currentSymbolList;
1806 std::string symbolType =
"";
1809 symbolType =
"function";
1810 currentSymbolList = symbols.funcSymbols;
1813 symbolType =
"table";
1814 currentSymbolList = symbols.tableSymbols;
1817 symbolType =
"memory";
1818 currentSymbolList = symbols.memSymbols;
1821 symbolType =
"global";
1822 currentSymbolList = symbols.globalSymbols;
1825 return emitError(exportLoc,
"invalid value for export type: ")
1826 << std::to_integer<unsigned>(*opcode);
1829 auto currentSymbol = std::visit(
1830 [&](
const auto &list) -> FailureOr<FlatSymbolRefAttr> {
1831 if (*idx > list.size()) {
1835 "trying to export {0} {1} which is undefined in this scope",
1839 return list[*idx].symbol;
1843 if (
failed(currentSymbol))
1849 return SymbolTable{mOp}.rename(symName, *exportName);
1854WasmBinaryParser::parseSectionItem<WasmSectionType::TABLE>(ParserHead &ph,
1856 FileLineColLoc opLocation = ph.getLocation();
1857 FailureOr<TableType> tableType = ph.parseTableType(ctx);
1860 LDBG() <<
" Parsed table description: " << *tableType;
1861 StringAttr symbol = builder.
getStringAttr(symbols.getNewTableSymbolName());
1863 TableOp::create(builder, opLocation, symbol.strref(), *tableType);
1864 symbols.tableSymbols.push_back({SymbolRefAttr::get(tableOp)});
1870WasmBinaryParser::parseSectionItem<WasmSectionType::FUNCTION>(ParserHead &ph,
1872 FileLineColLoc opLoc = ph.getLocation();
1873 auto typeIdxParsed = ph.parseLiteral<uint32_t>();
1874 if (
failed(typeIdxParsed))
1876 uint32_t typeIdx = *typeIdxParsed;
1877 if (typeIdx >= symbols.moduleFuncTypes.size())
1878 return emitError(getLocation(),
"invalid type index: ") << typeIdx;
1879 std::string symbol = symbols.getNewFuncSymbolName();
1881 FuncOp::create(builder, opLoc, symbol, symbols.moduleFuncTypes[typeIdx]);
1882 Block *block = funcOp.addEntryBlock();
1883 OpBuilder::InsertionGuard guard{builder};
1885 ReturnOp::create(builder, opLoc);
1886 symbols.funcSymbols.push_back(
1888 symbols.moduleFuncTypes[typeIdx]});
1889 return funcOp.verify();
1894WasmBinaryParser::parseSectionItem<WasmSectionType::TYPE>(ParserHead &ph,
1896 FailureOr<FunctionType> funcType = ph.parseFunctionType(ctx);
1899 LDBG() <<
"Parsed function type " << *funcType;
1900 symbols.moduleFuncTypes.push_back(*funcType);
1906WasmBinaryParser::parseSectionItem<WasmSectionType::MEMORY>(ParserHead &ph,
1908 FileLineColLoc opLocation = ph.getLocation();
1909 FailureOr<LimitType> memory = ph.parseLimit(ctx);
1913 LDBG() <<
" Registering memory " << *memory;
1914 std::string symbol = symbols.getNewMemorySymbolName();
1915 auto memOp = MemOp::create(builder, opLocation, symbol, *memory);
1916 symbols.memSymbols.push_back({SymbolRefAttr::get(memOp)});
1922WasmBinaryParser::parseSectionItem<WasmSectionType::GLOBAL>(ParserHead &ph,
1924 FileLineColLoc globalLocation = ph.getLocation();
1925 auto globalTypeParsed = ph.parseGlobalType(ctx);
1926 if (
failed(globalTypeParsed))
1929 GlobalTypeRecord globalType = *globalTypeParsed;
1930 auto symbol = builder.
getStringAttr(symbols.getNewGlobalSymbolName());
1931 auto globalOp = wasmssa::GlobalOp::create(
1932 builder, globalLocation, symbol, globalType.type, globalType.isMutable);
1933 symbols.globalSymbols.push_back(
1935 OpBuilder::InsertionGuard guard{builder};
1938 parsed_inst_t expr = ph.parseExpression(builder, symbols);
1942 return emitError(globalLocation,
"global with empty initializer");
1943 if (expr->size() != 1 && (*expr)[0].getType() != globalType.type)
1946 "initializer result type does not match global declaration type");
1947 ReturnOp::create(builder, globalLocation, *expr);
1952LogicalResult WasmBinaryParser::parseSectionItem<WasmSectionType::CODE>(
1953 ParserHead &ph,
size_t innerFunctionId) {
1954 unsigned long funcId = innerFunctionId + firstInternalFuncID;
1955 FunctionSymbolRefContainer symRef = symbols.funcSymbols[funcId];
1959 if (
failed(ph.parseCodeFor(funcOp, symbols)))
1965namespace mlir::wasm {
1968 WasmBinaryParser wBN{source, context};
1969 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 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.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
An instance of this location represents a tuple of file, line number, and column number.
static FileLineColLoc get(StringAttr filename, unsigned line, unsigned column)
static FlatSymbolRefAttr get(StringAttr value)
Construct a symbol reference for the given value name.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
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 localGet
static constexpr std::byte branchIf
static constexpr std::byte localSet
static constexpr std::byte constI32
static constexpr std::byte constFP32
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