24#include "llvm/Support/Debug.h"
25#include "llvm/Support/DebugLog.h"
26#include "llvm/Support/Endian.h"
27#include "llvm/Support/FormatVariadic.h"
28#include "llvm/Support/LEB128.h"
29#include "llvm/Support/LogicalResult.h"
37#define DEBUG_TYPE "wasm-translate"
39static_assert(CHAR_BIT == 8,
40 "This code expects std::byte to be exactly 8 bits");
47using section_id_t = uint8_t;
48enum struct WasmSectionType : section_id_t {
64constexpr section_id_t highestWasmSectionID{
65 static_cast<section_id_t
>(WasmSectionType::DATACOUNT)};
67#define APPLY_WASM_SEC_TRANSFORM \
68 WASM_SEC_TRANSFORM(CUSTOM) \
69 WASM_SEC_TRANSFORM(TYPE) \
70 WASM_SEC_TRANSFORM(IMPORT) \
71 WASM_SEC_TRANSFORM(FUNCTION) \
72 WASM_SEC_TRANSFORM(TABLE) \
73 WASM_SEC_TRANSFORM(MEMORY) \
74 WASM_SEC_TRANSFORM(GLOBAL) \
75 WASM_SEC_TRANSFORM(EXPORT) \
76 WASM_SEC_TRANSFORM(START) \
77 WASM_SEC_TRANSFORM(ELEMENT) \
78 WASM_SEC_TRANSFORM(CODE) \
79 WASM_SEC_TRANSFORM(DATA) \
80 WASM_SEC_TRANSFORM(DATACOUNT)
82template <WasmSectionType>
83constexpr const char *wasmSectionName =
"";
85#define WASM_SEC_TRANSFORM(section) \
87 [[maybe_unused]] constexpr const char \
88 *wasmSectionName<WasmSectionType::section> = #section;
90#undef WASM_SEC_TRANSFORM
92constexpr bool sectionShouldBeUnique(WasmSectionType secType) {
93 return secType != WasmSectionType::CUSTOM;
96template <std::byte... Bytes>
97struct ByteSequence {};
100template <std::
byte Byte>
101struct UniqueByte : ByteSequence<Byte> {};
103[[maybe_unused]]
constexpr ByteSequence<
108template <std::byte... allowedFlags>
109constexpr bool isValueOneOf(std::byte value,
110 ByteSequence<allowedFlags...> = {}) {
111 return ((value == allowedFlags) | ... |
false);
114template <std::byte... flags>
115constexpr bool isNotIn(std::byte value, ByteSequence<flags...> = {}) {
116 return !isValueOneOf<flags...>(value);
119struct GlobalTypeRecord {
124struct TypeIdxRecord {
128struct SymbolRefContainer {
129 FlatSymbolRefAttr symbol;
132struct GlobalSymbolRefContainer : SymbolRefContainer {
136struct FunctionSymbolRefContainer : SymbolRefContainer {
137 FunctionType functionType;
141 std::variant<TypeIdxRecord, TableType, LimitType, GlobalTypeRecord>;
143using parsed_inst_t = FailureOr<SmallVector<Value>>;
145struct EmptyBlockMarker {};
146using BlockTypeParseResult =
147 std::variant<EmptyBlockMarker, TypeIdxRecord, Type>;
149struct WasmModuleSymbolTables {
150 SmallVector<FunctionSymbolRefContainer> funcSymbols;
151 SmallVector<GlobalSymbolRefContainer> globalSymbols;
152 SmallVector<SymbolRefContainer> memSymbols;
153 SmallVector<SymbolRefContainer> tableSymbols;
154 SmallVector<FunctionType> moduleFuncTypes;
156 std::string getNewSymbolName(StringRef prefix,
size_t id)
const {
157 return (prefix + Twine{
id}).str();
160 std::string getNewFuncSymbolName()
const {
161 size_t id = funcSymbols.size();
162 return getNewSymbolName(
"func_",
id);
165 std::string getNewGlobalSymbolName()
const {
166 size_t id = globalSymbols.size();
167 return getNewSymbolName(
"global_",
id);
170 std::string getNewMemorySymbolName()
const {
171 size_t id = memSymbols.size();
172 return getNewSymbolName(
"mem_",
id);
175 std::string getNewTableSymbolName()
const {
176 size_t id = tableSymbols.size();
177 return getNewSymbolName(
"table_",
id);
193 LabelLevelOpInterface levelOp;
197 bool empty()
const {
return values.empty(); }
199 size_t size()
const {
return values.size(); }
209 FailureOr<SmallVector<Value>> popOperands(
TypeRange operandTypes,
218 LogicalResult pushResults(
ValueRange results, Location *opLoc);
220 void addLabelLevel(LabelLevelOpInterface levelOp) {
221 labelLevel.push_back({values.size(), levelOp});
222 LDBG() <<
"Adding a new frame context to ValueStack";
225 void dropLabelLevel() {
226 assert(!labelLevel.empty() &&
"Trying to drop a frame from empty context");
227 auto newSize = labelLevel.pop_back_val().stackIdx;
228 values.truncate(newSize);
230#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
233 LLVM_DUMP_METHOD
void dump()
const;
237 SmallVector<Value> values;
238 SmallVector<LabelLevel> labelLevel;
243template <
size_t... IS>
244constexpr ByteSequence<std::byte{IS}...>
245 castIndexSequenceToBytes(std::index_sequence<IS...>) {
249constexpr auto all8bitsBytes =
250 castIndexSequenceToBytes(std::make_index_sequence<256>());
255class ExpressionParser {
257 using locals_t = SmallVector<local_val_t>;
258 ExpressionParser(ParserHead &parser, WasmModuleSymbolTables
const &symbols,
259 ArrayRef<local_val_t> initLocal)
260 : parser{parser}, symbols{symbols}, locals{initLocal} {}
263 inline parsed_inst_t dispatchToInstParser(std::byte opCode,
268 struct NestingContextGuard {
269 NestingContextGuard(ExpressionParser &parser, LabelLevelOpInterface levelOp)
271 parser.addNestingContextLevel(levelOp);
273 NestingContextGuard(NestingContextGuard &&other) : parser{other.parser} {
274 other.shouldDropOnDestruct =
false;
276 NestingContextGuard(NestingContextGuard
const &) =
delete;
277 ~NestingContextGuard() {
278 if (shouldDropOnDestruct)
279 parser.dropNestingContextLevel();
281 ExpressionParser &parser;
282 bool shouldDropOnDestruct =
true;
285 void addNestingContextLevel(LabelLevelOpInterface levelOp) {
286 valueStack.addLabelLevel(levelOp);
289 void dropNestingContextLevel() {
292 valueStack.dropLabelLevel();
295 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
300 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
301 TypeIdxRecord type) {
302 if (type.id >= symbols.moduleFuncTypes.size())
304 "type index references nonexistent type (")
305 << type.id <<
"). Only " << symbols.moduleFuncTypes.size()
306 <<
" types are registered";
307 return symbols.moduleFuncTypes[type.id];
310 llvm::FailureOr<FunctionType> getFuncTypeFor(OpBuilder &builder,
315 llvm::FailureOr<FunctionType>
316 getFuncTypeFor(OpBuilder &builder, BlockTypeParseResult parseResult) {
318 [
this, &builder](
auto value) {
return getFuncTypeFor(builder, value); },
322 llvm::FailureOr<FunctionType>
323 getFuncTypeFor(OpBuilder &builder,
324 llvm::FailureOr<BlockTypeParseResult> parseResult) {
325 if (llvm::failed(parseResult))
327 return getFuncTypeFor(builder, *parseResult);
330 struct ParseResultWithInfo {
331 SmallVector<Value> opResults;
332 std::byte endingByte;
335 llvm::FailureOr<FunctionType> parseBlockFuncType(OpBuilder &builder);
338 template <std::
byte ParseEndByte = WasmBinaryEncoding::endByte>
339 parsed_inst_t
parse(OpBuilder &builder, UniqueByte<ParseEndByte> = {});
341 template <std::byte... ExpressionParseEnd>
342 FailureOr<ParseResultWithInfo>
343 parse(OpBuilder &builder,
344 ByteSequence<ExpressionParseEnd...> parsingEndFilters);
346 NestingContextGuard addNesting(LabelLevelOpInterface levelOp) {
347 return NestingContextGuard{*
this, levelOp};
350 FailureOr<llvm::SmallVector<Value>> popOperands(
TypeRange operandTypes) {
351 return valueStack.popOperands(operandTypes, ¤tOpLoc.value());
354 LogicalResult pushResults(
ValueRange results) {
355 return valueStack.pushResults(results, ¤tOpLoc.value());
361 template <
typename OpToCreate>
362 parsed_inst_t parseSetOrTee(OpBuilder &);
364 Location getCurrentOpLoc() {
365 assert(currentOpLoc.has_value() &&
366 "expects current opcode location to be set");
367 return *currentOpLoc;
370 class ExprParserProxy {
372 friend ExpressionParser;
373 inline auto parseBlockFuncType(OpBuilder &builder) {
374 return exprParser.parseBlockFuncType(builder);
379 template <
typename FilterT = ByteSequence<WasmBinaryEncoding::endByte>>
380 llvm::FailureOr<std::byte>
381 parseBlockContent(OpBuilder &builder,
Block *blockToFill,
383 LabelLevelOpInterface levelOp,
384 FilterT parseEndBytes = {}) {
385 OpBuilder::InsertionGuard guard(builder);
386 assert(blockToFill && blockToFill->
empty() &&
"expected an empty block");
388 LDBG() <<
"parsing a block of type "
391 auto nestingGuard = exprParser.addNesting(levelOp);
395 auto bodyParsingRes = exprParser.parse(builder, parseEndBytes);
396 if (
failed(bodyParsingRes))
398 auto returnOperands = exprParser.popOperands(resTypes);
399 if (
failed(returnOperands))
401 BlockReturnOp::create(builder, opLoc, *returnOperands);
402 LDBG() <<
"end of parsing of a block";
403 return bodyParsingRes->endingByte;
406 inline ParserHead &parser() {
return exprParser.parser; }
411 template <
typename OpToCreate>
412 parsed_inst_t parseBlockLikeOp(OpBuilder &);
414 inline auto getCurrentOpLoc() {
return exprParser.getCurrentOpLoc(); }
416 inline auto popOperands(
TypeRange operandTypes) {
417 return exprParser.popOperands(operandTypes);
420 inline auto &symbols() {
return exprParser.symbols; }
422 inline auto &locals() {
return exprParser.locals; }
424 template <
typename OpToCreate>
425 parsed_inst_t parseSetOrTee(OpBuilder &builder) {
426 return exprParser.parseSetOrTee<OpToCreate>(builder);
429 template <
typename valueT>
431 parseConstInst(OpBuilder &builder,
432 std::enable_if_t<std::is_arithmetic_v<valueT>> * =
nullptr);
443 template <
typename opcode,
typename valueType,
unsigned int numOperands>
444 inline parsed_inst_t buildNumericOp(
446 std::enable_if_t<std::is_arithmetic_v<valueType>> * =
nullptr);
458 template <
typename opType,
typename inputType,
typename outputType,
459 typename... extraArgsT>
460 inline parsed_inst_t buildConvertOp(OpBuilder &builder, extraArgsT...);
463 explicit ExprParserProxy(ExpressionParser &exprParser)
464 : exprParser{exprParser} {};
467 ExpressionParser &exprParser;
471 std::optional<Location> currentOpLoc;
473 WasmModuleSymbolTables
const &symbols;
475 ValueStack valueStack;
480 ParserHead(StringRef src, StringAttr name) : head{src}, locName{name} {}
481 ParserHead(ParserHead &&) =
default;
484 ParserHead(ParserHead
const &other) =
default;
487 auto getLocation()
const {
491 FailureOr<StringRef> consumeNBytes(
size_t nBytes) {
492 LDBG() <<
"Consume " << nBytes <<
" bytes";
493 LDBG() <<
" Bytes remaining: " << size();
494 LDBG() <<
" Current offset: " << offset;
496 return emitError(getLocation(),
"trying to extract ")
497 << nBytes <<
"bytes when only " << size() <<
"are available";
499 StringRef res = head.slice(offset, offset + nBytes);
501 LDBG() <<
" Updated offset (+" << nBytes <<
"): " << offset;
505 FailureOr<std::byte> consumeByte() {
506 FailureOr<StringRef> res = consumeNBytes(1);
509 return std::byte{*res->bytes_begin()};
512 template <
typename T>
513 FailureOr<T> parseLiteral();
515 FailureOr<uint32_t> parseVectorSize();
521 inline FailureOr<uint32_t> parseUI32();
522 inline FailureOr<int64_t> parseI64();
525 FailureOr<StringRef> parseName() {
526 FailureOr<uint32_t> size = parseVectorSize();
530 return consumeNBytes(*size);
533 FailureOr<WasmSectionType> parseWasmSectionType() {
534 FailureOr<std::byte>
id = consumeByte();
537 if (std::to_integer<unsigned>(*
id) > highestWasmSectionID)
538 return emitError(getLocation(),
"invalid section ID: ")
539 <<
static_cast<int>(*id);
540 return static_cast<WasmSectionType
>(*id);
543 FailureOr<LimitType> parseLimit(MLIRContext *ctx) {
544 using WasmLimits = WasmBinaryEncoding::LimitHeader;
545 FileLineColLoc limitLocation = getLocation();
546 FailureOr<std::byte> limitHeader = consumeByte();
550 if (isNotIn<WasmLimits::bothLimits, WasmLimits::lowLimitOnly>(*limitHeader))
551 return emitError(limitLocation,
"invalid limit header: ")
552 <<
static_cast<int>(*limitHeader);
553 FailureOr<uint32_t> minParse = parseUI32();
556 std::optional<uint32_t>
max{std::nullopt};
557 if (*limitHeader == WasmLimits::bothLimits) {
558 FailureOr<uint32_t> maxParse = parseUI32();
563 return LimitType::get(ctx, *minParse,
max);
566 FailureOr<Type> parseValueType(MLIRContext *ctx) {
567 FileLineColLoc typeLoc = getLocation();
568 FailureOr<std::byte> typeEncoding = consumeByte();
571 switch (*typeEncoding) {
573 return IntegerType::get(ctx, 32);
575 return IntegerType::get(ctx, 64);
577 return Float32Type::get(ctx);
579 return Float64Type::get(ctx);
581 return IntegerType::get(ctx, 128);
583 return wasmssa::FuncRefType::get(ctx);
585 return wasmssa::ExternRefType::get(ctx);
587 return emitError(typeLoc,
"invalid value type encoding: ")
588 <<
static_cast<int>(*typeEncoding);
592 FailureOr<GlobalTypeRecord> parseGlobalType(MLIRContext *ctx) {
593 using WasmGlobalMut = WasmBinaryEncoding::GlobalMutability;
594 FailureOr<Type> typeParsed = parseValueType(ctx);
597 FileLineColLoc mutLoc = getLocation();
598 FailureOr<std::byte> mutSpec = consumeByte();
601 if (isNotIn<WasmGlobalMut::isConst, WasmGlobalMut::isMutable>(*mutSpec))
602 return emitError(mutLoc,
"invalid global mutability specifier: ")
603 <<
static_cast<int>(*mutSpec);
604 return GlobalTypeRecord{*typeParsed, *mutSpec == WasmGlobalMut::isMutable};
607 FailureOr<TupleType> parseResultType(MLIRContext *ctx) {
608 FailureOr<uint32_t> nParamsParsed = parseVectorSize();
609 if (
failed(nParamsParsed))
611 uint32_t nParams = *nParamsParsed;
612 SmallVector<Type> res{};
613 res.reserve(nParams);
614 for (
size_t i = 0; i < nParams; ++i) {
615 FailureOr<Type> parsedType = parseValueType(ctx);
618 res.push_back(*parsedType);
620 return TupleType::get(ctx, res);
623 FailureOr<FunctionType> parseFunctionType(MLIRContext *ctx) {
624 FileLineColLoc typeLoc = getLocation();
625 FailureOr<std::byte> funcTypeHeader = consumeByte();
626 if (
failed(funcTypeHeader))
629 return emitError(typeLoc,
"invalid function type header byte. Expecting ")
631 <<
" got " << std::to_integer<unsigned>(*funcTypeHeader);
632 FailureOr<TupleType> inputTypes = parseResultType(ctx);
636 FailureOr<TupleType> resTypes = parseResultType(ctx);
640 return FunctionType::get(ctx, inputTypes->getTypes(), resTypes->getTypes());
643 FailureOr<TypeIdxRecord> parseTypeIndex() {
644 FailureOr<uint32_t> res = parseUI32();
647 return TypeIdxRecord{*res};
650 FailureOr<TableType> parseTableType(MLIRContext *ctx) {
651 FailureOr<Type> elmTypeParse = parseValueType(ctx);
654 if (!isWasmRefType(*elmTypeParse))
655 return emitError(getLocation(),
"invalid element type for table");
656 FailureOr<LimitType> limitParse = parseLimit(ctx);
659 return TableType::get(ctx, *elmTypeParse, *limitParse);
662 FailureOr<ImportDesc> parseImportDesc(MLIRContext *ctx) {
663 FileLineColLoc importLoc = getLocation();
664 FailureOr<std::byte> importType = consumeByte();
665 auto packager = [](
auto parseResult) -> FailureOr<ImportDesc> {
668 return {*parseResult};
672 switch (*importType) {
674 return packager(parseTypeIndex());
676 return packager(parseTableType(ctx));
678 return packager(parseLimit(ctx));
680 return packager(parseGlobalType(ctx));
682 return emitError(importLoc,
"invalid import type descriptor: ")
683 <<
static_cast<int>(*importType);
687 parsed_inst_t parseExpression(OpBuilder &builder,
688 WasmModuleSymbolTables
const &symbols,
689 ArrayRef<local_val_t> locals = {}) {
690 auto eParser = ExpressionParser{*
this, symbols, locals};
691 return eParser.parse(builder);
694 LogicalResult parseCodeFor(FuncOp func,
695 WasmModuleSymbolTables
const &symbols) {
696 SmallVector<local_val_t> locals{};
698 Block &block = func.getBody().front();
700 assert(func.getBody().getBlocks().size() == 1 &&
701 "Function should only have its default created block at this point");
703 "Only the placeholder return op should be present at this point");
704 auto returnOp = cast<ReturnOp>(&block.
back());
707 FailureOr<uint32_t> codeSizeInBytes = parseUI32();
708 if (
failed(codeSizeInBytes))
710 FailureOr<StringRef> codeContent = consumeNBytes(*codeSizeInBytes);
713 auto name = StringAttr::get(func->getContext(),
714 locName.str() +
"::" + func.getSymName());
715 auto cParser = ParserHead{*codeContent, name};
716 FailureOr<uint32_t> localVecSize = cParser.parseVectorSize();
719 OpBuilder builder{&func.getBody().front().back()};
723 uint32_t nVarVec = *localVecSize;
724 for (
size_t i = 0; i < nVarVec; ++i) {
725 FileLineColLoc varLoc = cParser.getLocation();
726 FailureOr<uint32_t> nSubVar = cParser.parseUI32();
729 FailureOr<Type> varT = cParser.parseValueType(func->getContext());
732 for (
size_t j = 0; j < *nSubVar; ++j) {
733 auto local = LocalOp::create(builder, varLoc, *varT);
734 locals.push_back(local.getResult());
737 parsed_inst_t res = cParser.parseExpression(builder, symbols, locals);
742 "unparsed garbage remaining at end of code block");
743 ReturnOp::create(builder, func->getLoc(), *res);
748 llvm::FailureOr<BlockTypeParseResult> parseBlockType(MLIRContext *ctx) {
749 auto loc = getLocation();
750 auto blockIndicator = peek();
751 if (
failed(blockIndicator))
755 return {EmptyBlockMarker{}};
757 if (isValueOneOf(*blockIndicator, valueTypesEncodings))
758 return parseValueType(ctx);
761 auto typeIdx = parseI64();
764 if (*typeIdx < 0 || *typeIdx > std::numeric_limits<uint32_t>::max())
765 return emitError(loc,
"type ID should be representable with an unsigned "
766 "32 bits integer. Got ")
768 return {TypeIdxRecord{
static_cast<uint32_t
>(*typeIdx)}};
771 bool end()
const {
return curHead().empty(); }
773 ParserHead
copy()
const {
return *
this; }
776 StringRef curHead()
const {
return head.drop_front(offset); }
778 FailureOr<std::byte> peek()
const {
782 "trying to peek at next byte, but input stream is empty");
783 return static_cast<std::byte
>(curHead().front());
786 size_t size()
const {
return head.size() - offset; }
790 unsigned anchorOffset{0};
795FailureOr<float> ParserHead::parseLiteral<float>() {
796 FailureOr<StringRef> bytes = consumeNBytes(4);
799 return llvm::support::endian::read<float>(bytes->bytes_begin(),
800 llvm::endianness::little);
804FailureOr<double> ParserHead::parseLiteral<double>() {
805 FailureOr<StringRef> bytes = consumeNBytes(8);
808 return llvm::support::endian::read<double>(bytes->bytes_begin(),
809 llvm::endianness::little);
813FailureOr<uint32_t> ParserHead::parseLiteral<uint32_t>() {
814 char const *error =
nullptr;
816 unsigned encodingSize{0};
817 StringRef src = curHead();
818 uint64_t decoded = llvm::decodeULEB128(src.bytes_begin(), &encodingSize,
819 src.bytes_end(), &error);
823 if (std::isgreater(decoded, std::numeric_limits<uint32_t>::max()))
824 return emitError(getLocation()) <<
"literal does not fit on 32 bits";
826 res =
static_cast<uint32_t
>(decoded);
827 offset += encodingSize;
832FailureOr<int32_t> ParserHead::parseLiteral<int32_t>() {
833 char const *error =
nullptr;
835 unsigned encodingSize{0};
836 StringRef src = curHead();
837 int64_t decoded = llvm::decodeSLEB128(src.bytes_begin(), &encodingSize,
838 src.bytes_end(), &error);
841 if (std::isgreater(decoded, std::numeric_limits<int32_t>::max()) ||
842 std::isgreater(std::numeric_limits<int32_t>::min(), decoded))
843 return emitError(getLocation()) <<
"literal does not fit on 32 bits";
845 res =
static_cast<int32_t
>(decoded);
846 offset += encodingSize;
851FailureOr<int64_t> ParserHead::parseLiteral<int64_t>() {
852 char const *error =
nullptr;
853 unsigned encodingSize{0};
854 StringRef src = curHead();
855 int64_t res = llvm::decodeSLEB128(src.bytes_begin(), &encodingSize,
856 src.bytes_end(), &error);
860 offset += encodingSize;
864FailureOr<uint32_t> ParserHead::parseVectorSize() {
865 return parseLiteral<uint32_t>();
868inline FailureOr<uint32_t> ParserHead::parseUI32() {
869 return parseLiteral<uint32_t>();
872inline FailureOr<int64_t> ParserHead::parseI64() {
873 return parseLiteral<int64_t>();
876#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
877void ValueStack::dump()
const {
878 llvm::dbgs() <<
"================= Wasm ValueStack =======================\n";
879 llvm::dbgs() <<
"size: " << size() <<
"\n";
880 llvm::dbgs() <<
"nbFrames: " << labelLevel.size() <<
'\n';
881 llvm::dbgs() <<
"<Top>"
886 auto indexGetter = [
this]() {
887 size_t idx = labelLevel.size();
888 return [
this, idx]()
mutable -> std::optional<std::pair<size_t, size_t>> {
889 llvm::dbgs() <<
"IDX: " << idx <<
'\n';
892 auto frameId = idx - 1;
893 auto frameLimit = labelLevel[frameId].stackIdx;
895 return {{frameId, frameLimit}};
898 auto getNextFrameIndex = indexGetter();
899 auto nextFrameIdx = getNextFrameIndex();
900 size_t stackSize = size();
901 for (
size_t idx = 0; idx < stackSize; ++idx) {
902 size_t actualIdx = stackSize - 1 - idx;
903 while (nextFrameIdx && (nextFrameIdx->second > actualIdx)) {
904 llvm::dbgs() <<
" --------------- Frame (" << nextFrameIdx->first
906 nextFrameIdx = getNextFrameIndex();
909 values[actualIdx].dump();
911 while (nextFrameIdx) {
912 llvm::dbgs() <<
" --------------- Frame (" << nextFrameIdx->first <<
")\n";
913 nextFrameIdx = getNextFrameIndex();
915 llvm::dbgs() <<
"<Bottom>"
917 llvm::dbgs() <<
"=========================================================\n";
921parsed_inst_t ValueStack::popOperands(
TypeRange operandTypes, Location *opLoc) {
922 LDBG() <<
"Popping from ValueStack\n"
923 <<
" Elements(s) to pop: " << operandTypes.size() <<
"\n"
924 <<
" Current stack size: " << values.size();
925 if (operandTypes.size() > values.size())
927 "stack doesn't contain enough values. trying to get ")
928 << operandTypes.size() <<
" operands on a stack containing only "
929 << values.size() <<
" values";
930 size_t stackIdxOffset = values.size() - operandTypes.size();
931 SmallVector<Value> res{};
932 res.reserve(operandTypes.size());
933 for (
size_t i{0}; i < operandTypes.size(); ++i) {
934 Value operand = values[i + stackIdxOffset];
935 Type stackType = operand.
getType();
936 if (stackType != operandTypes[i])
937 return emitError(*opLoc,
"invalid operand type on stack. expecting ")
938 << operandTypes[i] <<
", value on stack is of type " << stackType;
939 LDBG() <<
" POP: " << operand;
940 res.push_back(operand);
942 values.resize(values.size() - operandTypes.size());
943 LDBG() <<
" Updated stack size: " << values.size();
947LogicalResult ValueStack::pushResults(
ValueRange results, Location *opLoc) {
948 LDBG() <<
"Pushing to ValueStack\n"
949 <<
" Elements(s) to push: " << results.size() <<
"\n"
950 <<
" Current stack size: " << values.size();
951 for (Value val : results) {
952 if (!isWasmValueType(val.getType()))
953 return emitError(*opLoc,
"invalid value type on stack: ")
955 LDBG() <<
" PUSH: " << val;
956 values.push_back(val);
959 LDBG() <<
" Updated stack size: " << values.size();
963template <std::
byte EndParseByte>
964parsed_inst_t ExpressionParser::parse(OpBuilder &builder,
965 UniqueByte<EndParseByte> endByte) {
966 auto res =
parse(builder, ByteSequence<EndParseByte>{});
969 return res->opResults;
972template <std::byte... ExpressionParseEnd>
973FailureOr<ExpressionParser::ParseResultWithInfo>
974ExpressionParser::parse(OpBuilder &builder,
975 ByteSequence<ExpressionParseEnd...> parsingEndFilters) {
976 SmallVector<Value> res;
978 currentOpLoc = parser.getLocation();
979 FailureOr<std::byte> opCode = parser.consumeByte();
982 if (isValueOneOf(*opCode, parsingEndFilters))
983 return {{res, *opCode}};
984 parsed_inst_t resParsed;
985 resParsed = dispatchToInstParser(*opCode, builder);
988 std::swap(res, *resParsed);
989 if (
failed(pushResults(res)))
994llvm::FailureOr<FunctionType>
995ExpressionParser::parseBlockFuncType(OpBuilder &builder) {
996 return getFuncTypeFor(builder, parser.parseBlockType(builder.
getContext()));
999template <
typename OpToCreate>
1001ExpressionParser::ExprParserProxy::parseBlockLikeOp(OpBuilder &builder) {
1002 auto opLoc = getCurrentOpLoc();
1003 auto funcType = parseBlockFuncType(builder);
1007 auto inputTypes = funcType->getInputs();
1008 auto inputOps = popOperands(inputTypes);
1013 Region *curRegion = curBlock->
getParent();
1014 auto resTypes = funcType->getResults();
1015 llvm::SmallVector<Location> locations{};
1016 locations.resize(resTypes.size(), getCurrentOpLoc());
1018 builder.
createBlock(curRegion, curRegion->
end(), resTypes, locations);
1021 OpToCreate::create(builder, getCurrentOpLoc(), *inputOps, successor);
1022 auto *blockBody = blockOp.createBlock();
1023 if (
failed(parseBlockContent(builder, blockBody, resTypes, opLoc, blockOp)))
1026 return {
ValueRange{successor->getArguments()}};
1029parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::block>,
1031 ExpressionParser::ExprParserProxy &exprParser) {
1032 return exprParser.parseBlockLikeOp<BlockOp>(builder);
1035parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::loop>,
1037 ExpressionParser::ExprParserProxy &exprParser) {
1038 return exprParser.parseBlockLikeOp<LoopOp>(builder);
1041parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::ifOpCode>,
1043 ExpressionParser::ExprParserProxy &exprParser) {
1044 auto opLoc = exprParser.getCurrentOpLoc();
1045 auto funcType = exprParser.parseBlockFuncType(builder);
1049 LDBG() <<
"Parsing an if instruction of type " << *funcType;
1050 auto inputTypes = funcType->getInputs();
1051 auto conditionValue = exprParser.popOperands(builder.
getI32Type());
1052 if (
failed(conditionValue))
1054 auto inputOps = exprParser.popOperands(inputTypes);
1059 Region *curRegion = curBlock->
getParent();
1060 auto resTypes = funcType->getResults();
1061 llvm::SmallVector<Location> locations{};
1062 locations.resize(resTypes.size(), exprParser.getCurrentOpLoc());
1064 builder.
createBlock(curRegion, curRegion->
end(), resTypes, locations);
1066 auto ifOp = IfOp::create(builder, exprParser.getCurrentOpLoc(),
1067 conditionValue->front(), *inputOps, successor);
1068 auto *ifEntryBlock = ifOp.createIfBlock();
1069 constexpr auto ifElseFilter =
1072 auto parseIfRes = exprParser.parseBlockContent(
1073 builder, ifEntryBlock, resTypes, opLoc, ifOp, ifElseFilter);
1077 LDBG() <<
" else block is present.";
1078 Block *elseEntryBlock = ifOp.createElseBlock();
1079 auto parseElseRes = exprParser.parseBlockContent(builder, elseEntryBlock,
1080 resTypes, opLoc, ifOp);
1081 if (
failed(parseElseRes))
1085 return {
ValueRange{successor->getArguments()}};
1088parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::branchIf>,
1090 ExpressionParser::ExprParserProxy &exprParser) {
1091 auto level = exprParser.parser().parseLiteral<uint32_t>();
1095 Region *curRegion = curBlock->
getParent();
1098 auto condition = exprParser.popOperands(builder.
getI32Type());
1103 LabelBranchingOpInterface::getTargetOpFromBlock(curBlock, *level);
1106 auto inputTypes = targetOp->getLabelTarget()->getArgumentTypes();
1107 auto branchArgs = exprParser.popOperands(inputTypes);
1110 BranchIfOp::create(builder, exprParser.getCurrentOpLoc(), condition->front(),
1114 return {*branchArgs};
1117parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::call>,
1119 ExpressionParser::ExprParserProxy &exprParser) {
1120 auto loc = exprParser.getCurrentOpLoc();
1121 auto funcIdx = exprParser.parser().parseLiteral<uint32_t>();
1124 if (*funcIdx >= exprParser.symbols().funcSymbols.size())
1125 return emitError(loc,
"Invalid function index: ") << *funcIdx;
1126 auto callee = exprParser.symbols().funcSymbols[*funcIdx];
1127 llvm::ArrayRef<Type> inTypes = callee.functionType.getInputs();
1128 llvm::ArrayRef<Type> resTypes = callee.functionType.getResults();
1129 parsed_inst_t inOperands = exprParser.popOperands(inTypes);
1133 FuncCallOp::create(builder, loc, resTypes, callee.symbol, *inOperands);
1134 return {callOp.getResults()};
1137parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::localGet>,
1139 ExpressionParser::ExprParserProxy &exprParser) {
1140 FailureOr<uint32_t>
id = exprParser.parser().parseLiteral<uint32_t>();
1141 Location instLoc = exprParser.getCurrentOpLoc();
1144 if (*
id >= exprParser.locals().size())
1145 return emitError(instLoc,
"invalid local index. function has ")
1146 << exprParser.locals().size()
1147 <<
" accessible locals, received index " << *id;
1148 return {{LocalGetOp::create(builder, instLoc, exprParser.locals()[*
id])
1152parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::globalGet>,
1154 ExpressionParser::ExprParserProxy &exprParser) {
1155 FailureOr<uint32_t>
id = exprParser.parser().parseLiteral<uint32_t>();
1156 Location instLoc = exprParser.getCurrentOpLoc();
1159 if (*
id >= exprParser.symbols().globalSymbols.size())
1160 return emitError(instLoc,
"invalid global index. function has ")
1161 << exprParser.symbols().globalSymbols.size()
1162 <<
" accessible globals, received index " << *id;
1163 GlobalSymbolRefContainer globalVar = exprParser.symbols().globalSymbols[*id];
1164 auto globalOp = GlobalGetOp::create(builder, instLoc, globalVar.globalType,
1167 return {{globalOp.getResult()}};
1170template <
typename OpToCreate>
1171parsed_inst_t ExpressionParser::parseSetOrTee(OpBuilder &builder) {
1172 FailureOr<uint32_t>
id = parser.parseLiteral<uint32_t>();
1175 if (*
id >= locals.size())
1176 return emitError(*currentOpLoc,
"invalid local index. function has ")
1177 << locals.size() <<
" accessible locals, received index " << *id;
1178 if (valueStack.empty())
1181 "invalid stack access, trying to access a value on an empty stack");
1187 OpToCreate::create(builder, *currentOpLoc, locals[*
id], poppedOp->front())
1191parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::localSet>,
1193 ExpressionParser::ExprParserProxy &exprParser) {
1194 return exprParser.parseSetOrTee<LocalSetOp>(builder);
1197parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::localTee>,
1199 ExpressionParser::ExprParserProxy &exprParser) {
1200 return exprParser.parseSetOrTee<LocalTeeOp>(builder);
1203template <
typename T>
1204inline Type buildLiteralType(OpBuilder &);
1207inline Type buildLiteralType<int32_t>(OpBuilder &builder) {
1212inline Type buildLiteralType<int64_t>(OpBuilder &builder) {
1217[[maybe_unused]]
inline Type buildLiteralType<uint32_t>(OpBuilder &builder) {
1222[[maybe_unused]]
inline Type buildLiteralType<uint64_t>(OpBuilder &builder) {
1227inline Type buildLiteralType<float>(OpBuilder &builder) {
1232inline Type buildLiteralType<double>(OpBuilder &builder) {
1236template <
typename ValT,
1237 typename E = std::enable_if_t<std::is_arithmetic_v<ValT>>>
1240template <
typename ValT>
1241struct AttrHolder<ValT, std::enable_if_t<std::is_integral_v<ValT>>> {
1242 using type = IntegerAttr;
1245template <
typename ValT>
1246struct AttrHolder<ValT, std::enable_if_t<std::is_floating_point_v<ValT>>> {
1247 using type = FloatAttr;
1250template <
typename ValT>
1251using attr_holder_t =
typename AttrHolder<ValT>::type;
1253template <
typename ValT,
1254 typename EnableT = std::enable_if_t<std::is_arithmetic_v<ValT>>>
1255attr_holder_t<ValT> buildLiteralAttr(OpBuilder &builder, ValT val) {
1256 return attr_holder_t<ValT>::get(buildLiteralType<ValT>(builder), val);
1259template <
typename valueT>
1260parsed_inst_t ExpressionParser::ExprParserProxy::parseConstInst(
1261 OpBuilder &builder, std::enable_if_t<std::is_arithmetic_v<valueT>> *) {
1262 auto parsedConstant = parser().parseLiteral<valueT>();
1263 if (
failed(parsedConstant))
1266 ConstOp::create(builder, getCurrentOpLoc(),
1267 buildLiteralAttr<valueT>(builder, *parsedConstant));
1268 return {{constOp.getResult()}};
1271parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::constI32>,
1273 ExpressionParser::ExprParserProxy &exprParser) {
1274 return exprParser.parseConstInst<int32_t>(builder);
1277parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::constI64>,
1279 ExpressionParser::ExprParserProxy &exprParser) {
1280 return exprParser.parseConstInst<int64_t>(builder);
1283parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::constFP32>,
1285 ExpressionParser::ExprParserProxy &exprParser) {
1286 return exprParser.parseConstInst<
float>(builder);
1289parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::constFP64>,
1291 ExpressionParser::ExprParserProxy &exprParser) {
1292 return exprParser.parseConstInst<
double>(builder);
1295template <
typename opcode,
typename valueType,
unsigned int numOperands>
1296inline parsed_inst_t ExpressionParser::ExprParserProxy::buildNumericOp(
1297 OpBuilder &builder, std::enable_if_t<std::is_arithmetic_v<valueType>> *) {
1298 auto ty = buildLiteralType<valueType>(builder);
1299 LDBG() <<
"*** buildNumericOp: numOperands = " << numOperands
1300 <<
", type = " << ty <<
" ***";
1301 auto tysToPop = SmallVector<Type, numOperands>();
1302 tysToPop.resize(numOperands);
1303 llvm::fill(tysToPop, ty);
1304 auto operands = popOperands(tysToPop);
1307 auto op = opcode::create(builder, getCurrentOpLoc(), *operands).getResult();
1308 LDBG() <<
"Built operation: " << op;
1313#define BUILD_NUMERIC_OP(OP_NAME, N_ARGS, PREFIX, SUFFIX, TYPE) \
1314 inline parsed_inst_t parse( \
1315 OpCode<WasmBinaryEncoding::OpCode::PREFIX##SUFFIX>, OpBuilder &builder, \
1316 ExpressionParser::ExprParserProxy &exprParser) { \
1317 return exprParser.buildNumericOp<OP_NAME, TYPE, N_ARGS>(builder); \
1321#define BUILD_NUMERIC_BINOP_INT(OP_NAME, PREFIX) \
1322 BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, I32, int32_t) \
1323 BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, I64, int64_t)
1326#define BUILD_NUMERIC_BINOP_FP(OP_NAME, PREFIX) \
1327 BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, F32, float) \
1328 BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, F64, double)
1331#define BUILD_NUMERIC_BINOP_INTFP(OP_NAME, PREFIX) \
1332 BUILD_NUMERIC_BINOP_INT(OP_NAME, PREFIX) \
1333 BUILD_NUMERIC_BINOP_FP(OP_NAME, PREFIX)
1336#define BUILD_NUMERIC_UNARY_OP_INT(OP_NAME, PREFIX) \
1337 BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, I32, int32_t) \
1338 BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, I64, int64_t)
1341#define BUILD_NUMERIC_UNARY_OP_FP(OP_NAME, PREFIX) \
1342 BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, F32, float) \
1343 BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, F64, double)
1391#undef BUILD_NUMERIC_BINOP_FP
1392#undef BUILD_NUMERIC_BINOP_INT
1393#undef BUILD_NUMERIC_BINOP_INTFP
1394#undef BUILD_NUMERIC_UNARY_OP_FP
1395#undef BUILD_NUMERIC_UNARY_OP_INT
1396#undef BUILD_NUMERIC_OP
1397#undef BUILD_NUMERIC_CAST_OP
1399template <
typename opType,
typename inputType,
typename outputType,
1400 typename... extraArgsT>
1402ExpressionParser::ExprParserProxy::buildConvertOp(
OpBuilder &builder,
1403 extraArgsT... extraArgs) {
1404 static_assert(std::is_arithmetic_v<inputType>,
1405 "InputType should be an arithmetic type");
1406 static_assert(std::is_arithmetic_v<outputType>,
1407 "OutputType should be an arithmetic type");
1408 auto intype = buildLiteralType<inputType>(builder);
1409 auto outType = buildLiteralType<outputType>(builder);
1410 auto operand = popOperands(intype);
1411 if (failed(operand))
1413 auto op = opType::create(builder, getCurrentOpLoc(), outType,
1414 operand->front(), extraArgs...);
1415 LDBG() <<
"Built operation: " << op;
1416 return {{op.getResult()}};
1419parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::demoteF64ToF32>,
1421 ExpressionParser::ExprParserProxy &exprParser) {
1422 return exprParser.buildConvertOp<DemoteOp, double,
float>(builder);
1425parsed_inst_t
parse(OpCode<WasmBinaryEncoding::OpCode::wrap>,
1427 ExpressionParser::ExprParserProxy &exprParser) {
1428 return exprParser.buildConvertOp<WrapOp, int64_t, int32_t>(builder);
1431#define BUILD_CONVERSION_OP(IN_T, OUT_T, SOURCE_OP, TARGET_OP) \
1432 inline parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::SOURCE_OP>, \
1433 OpBuilder &builder, \
1434 ExpressionParser::ExprParserProxy &exprParser) { \
1435 return exprParser.buildConvertOp<TARGET_OP, IN_T, OUT_T>(builder); \
1438#define BUILD_CONVERT_OP_FOR(DEST_T, WIDTH) \
1439 BUILD_CONVERSION_OP(uint32_t, DEST_T, convertUI32F##WIDTH, ConvertUOp) \
1440 BUILD_CONVERSION_OP(int32_t, DEST_T, convertSI32F##WIDTH, ConvertSOp) \
1441 BUILD_CONVERSION_OP(uint64_t, DEST_T, convertUI64F##WIDTH, ConvertUOp) \
1442 BUILD_CONVERSION_OP(int64_t, DEST_T, convertSI64F##WIDTH, ConvertSOp)
1447#undef BUILD_CONVERT_OP_FOR
1449#define BUILD_TRUNC_OP_FOR(SRC_T, WIDTH) \
1450 BUILD_CONVERSION_OP(SRC_T, int32_t, truncSI32F##WIDTH, TruncSIOp) \
1451 BUILD_CONVERSION_OP(SRC_T, uint32_t, truncUI32F##WIDTH, TruncUIOp) \
1452 BUILD_CONVERSION_OP(SRC_T, int64_t, truncSI64F##WIDTH, TruncSIOp) \
1453 BUILD_CONVERSION_OP(SRC_T, uint64_t, truncUI64F##WIDTH, TruncUIOp)
1458#undef BUILD_TRUNC_OP_FOR
1463#undef BUILD_CONVERSION_OP
1465parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::saturatedTruncate>,
1467 ExpressionParser::ExprParserProxy &parser,
1468 std::uint32_t subOpCode) {
1470 return emitError(parser.getCurrentOpLoc())
1471 <<
"invalid sub-opcode for trunc_saturate: " << subOpCode;
1472 LDBG() <<
"Sub subOpcode for operation: " << subOpCode;
1473 bool isDestUnsigned = (subOpCode & 1) != 0;
1474 bool isSrcF64 = (subOpCode & 2) != 0;
1475 bool isDestI64 = (subOpCode & 4) != 0;
1479 auto srcOp = parser.popOperands(srcType);
1483 ? TruncSatUIOp::create(builder, parser.getCurrentOpLoc(),
1484 destType, srcOp->front())
1485 : TruncSatSIOp::create(builder, parser.getCurrentOpLoc(),
1486 destType, srcOp->front());
1487 LDBG() <<
"Built operation: " << op;
1491#define BUILD_SLICE_EXTEND_PARSER(IT_WIDTH, EXTRACT_WIDTH) \
1492 parsed_inst_t parse( \
1493 OpCode<WasmBinaryEncoding::OpCode::extendI##IT_WIDTH##EXTRACT_WIDTH##S>, \
1494 OpBuilder &builder, ExpressionParser::ExprParserProxy &exprParser) { \
1495 using inout_t = int##IT_WIDTH##_t; \
1496 auto attr = builder.getUI32IntegerAttr(EXTRACT_WIDTH); \
1497 return exprParser.buildConvertOp<ExtendLowBitsSOp, inout_t, inout_t>( \
1507#undef BUILD_SLICE_EXTEND_PARSER
1509parsed_inst_t parse(OpCode<WasmBinaryEncoding::OpCode::promoteF32ToF64>,
1511 ExpressionParser::ExprParserProxy &exprParser) {
1512 return exprParser.buildConvertOp<PromoteOp, float,
double>(builder);
1515#define BUILD_REINTERPRET_PARSER(WIDTH, FP_TYPE) \
1516 inline parsed_inst_t parse( \
1517 OpCode<WasmBinaryEncoding::OpCode::reinterpretF##WIDTH##AsI##WIDTH>, \
1518 OpBuilder &builder, ExpressionParser::ExprParserProxy &exprParser) { \
1519 return exprParser.buildConvertOp<ReinterpretOp, FP_TYPE, int##WIDTH##_t>( \
1523 inline parsed_inst_t parse( \
1524 OpCode<WasmBinaryEncoding::OpCode::reinterpretI##WIDTH##AsF##WIDTH>, \
1525 OpBuilder &builder, ExpressionParser::ExprParserProxy &exprParser) { \
1526 return exprParser.buildConvertOp<ReinterpretOp, int##WIDTH##_t, FP_TYPE>( \
1533#undef BUILD_REINTERPRET_PARSER
1535class InstDispatcher {
1537 template <std::
byte OpCode,
typename =
void>
1538 struct HasParserRegistered : std::false_type {};
1540 template <std::
byte opCode>
1541 struct HasParserRegistered<
1542 opCode, std::void_t<decltype(parse(
1543 std::declval<OpCode<opCode>>(), std::declval<OpBuilder &>(),
1544 std::declval<ExpressionParser::ExprParserProxy &>()))>>
1545 : std::true_type {};
1547 template <std::
byte opCode>
1548 static constexpr bool hasParseredRegistered =
1549 HasParserRegistered<opCode>::value;
1551 template <std::
byte OpCode,
typename =
void>
1552 struct HasParserWthSubOpCodeRegistered : std::false_type {};
1554 template <std::
byte opCode>
1555 struct HasParserWthSubOpCodeRegistered<
1556 opCode, std::void_t<decltype(parse(
1557 std::declval<OpCode<opCode>>(), std::declval<OpBuilder &>(),
1558 std::declval<ExpressionParser::ExprParserProxy &>(),
1559 std::declval<std::uint32_t>()))>> : std::true_type {
1560 static_assert(!hasParseredRegistered<opCode>,
1561 "plain parser and parser with sub-opcode can't be registered "
1562 "for the same opcode");
1565 template <std::
byte opCode>
1566 static constexpr bool hasParseredWithSubOpCodeRegistered =
1567 HasParserWthSubOpCodeRegistered<opCode>::value;
1569 using dispatch_t = parsed_inst_t (*)(
OpBuilder &,
1570 ExpressionParser::ExprParserProxy &);
1572 static inline parsed_inst_t
1574 ExpressionParser::ExprParserProxy &expressionParser) {
1575 llvm_unreachable(
"Failure in opcode parser dispatch logic.");
1576 return mlir::failure();
1579 template <std::
byte opCode>
1580 static parsed_inst_t
1582 ExpressionParser::ExprParserProxy &exprParser) {
1583 return parse(OpCode<opCode>{}, builder, exprParser);
1586 template <std::
byte opCode>
1587 static parsed_inst_t
1588 forwardToParserWithSubOpCode(
OpBuilder &builder,
1589 ExpressionParser::ExprParserProxy &exprParser) {
1590 auto loc = exprParser.getCurrentOpLoc();
1591 auto subOpCode = exprParser.parser().parseLiteral<std::uint32_t>();
1592 if (failed(subOpCode))
1593 return emitError(loc) <<
"expecting sub opcode for opcode "
1594 <<
static_cast<uint8_t
>(opCode);
1595 return parse(OpCode<opCode>{}, builder, exprParser, subOpCode.value());
1598 template <std::
byte opCode>
1599 static constexpr dispatch_t getHandlerForOpCode() {
1600 if constexpr (hasParseredRegistered<opCode>)
1601 return forwardToParser<opCode>;
1602 else if constexpr (hasParseredWithSubOpCodeRegistered<opCode>)
1603 return forwardToParserWithSubOpCode<opCode>;
1605 return unreachableHandler;
1609 static inline parsed_inst_t
1611 ExpressionParser::ExprParserProxy &expressionParser,
1613 return emitError(expressionParser.getCurrentOpLoc(),
1614 "unknown instruction opcode: ")
1615 <<
static_cast<int>(opCode);
1618 template <std::byte... opCodes>
1619 static inline parsed_inst_t
1620 dispatchImpl(std::byte opCode,
OpBuilder &builder,
1621 ExpressionParser::ExprParserProxy &exprParser,
1622 ByteSequence<opCodes...>) {
1623 static constexpr std::array<bool, 256> opcodeValidityMap{
1624 (hasParseredRegistered<opCodes> ||
1625 hasParseredWithSubOpCodeRegistered<opCodes>)...};
1626 static constexpr std::array<dispatch_t, 256> dispatchTable{
1627 getHandlerForOpCode<opCodes>()...};
1628 if (opcodeValidityMap[
static_cast<size_t>(opCode)]) {
1629 return dispatchTable[
static_cast<size_t>(opCode)](builder, exprParser);
1631 return invalidOpcodeDiag(builder, exprParser, opCode);
1645 static parsed_inst_t dispatch(std::byte opCode,
OpBuilder &builder,
1646 ExpressionParser::ExprParserProxy &exprParser) {
1647 return dispatchImpl(opCode, builder, exprParser, all8bitsBytes);
1652ExpressionParser::dispatchToInstParser(std::byte opCode, OpBuilder &builder) {
1653 ExpressionParser::ExprParserProxy exprParser{*
this};
1654 return InstDispatcher::dispatch(opCode, builder, exprParser);
1656class WasmBinaryParser {
1658 struct SectionRegistry {
1659 using section_location_t = StringRef;
1661 std::array<SmallVector<section_location_t>, highestWasmSectionID + 1>
1664 template <WasmSectionType SecType>
1665 std::conditional_t<sectionShouldBeUnique(SecType),
1666 std::optional<section_location_t>,
1667 ArrayRef<section_location_t>>
1668 getContentForSection()
const {
1669 constexpr auto idx =
static_cast<size_t>(SecType);
1670 if constexpr (sectionShouldBeUnique(SecType)) {
1671 return registry[idx].empty() ? std::nullopt
1672 : std::make_optional(registry[idx][0]);
1674 return registry[idx];
1678 bool hasSection(WasmSectionType secType)
const {
1679 return !registry[
static_cast<size_t>(secType)].empty();
1687 LogicalResult registerSection(WasmSectionType secType,
1688 section_location_t location, Location loc) {
1689 if (sectionShouldBeUnique(secType) && hasSection(secType))
1691 "trying to add a second instance of unique section");
1693 registry[
static_cast<size_t>(secType)].
push_back(location);
1694 emitRemark(loc,
"Adding section with section ID ")
1695 <<
static_cast<uint8_t
>(secType);
1699 LogicalResult populateFromBody(ParserHead ph) {
1701 FileLineColLoc sectionLoc = ph.getLocation();
1702 FailureOr<WasmSectionType> secType = ph.parseWasmSectionType();
1706 FailureOr<uint32_t> secSizeParsed = ph.parseLiteral<uint32_t>();
1707 if (
failed(secSizeParsed))
1710 uint32_t secSize = *secSizeParsed;
1711 FailureOr<StringRef> sectionContent = ph.consumeNBytes(secSize);
1712 if (
failed(sectionContent))
1715 LogicalResult registration =
1716 registerSection(*secType, *sectionContent, sectionLoc);
1718 if (
failed(registration))
1725 auto getLocation(
int offset = 0)
const {
1729 template <WasmSectionType>
1730 LogicalResult parseSectionItem(ParserHead &,
size_t);
1732 template <WasmSectionType section>
1733 LogicalResult parseSection() {
1734 auto secName = std::string{wasmSectionName<section>};
1735 auto sectionNameAttr =
1736 StringAttr::get(ctx, srcName.strref() +
":" + secName +
"-SECTION");
1737 unsigned offset = 0;
1738 auto getLocation = [sectionNameAttr, &offset]() {
1741 auto secContent = registry.getContentForSection<section>();
1743 LDBG() << secName <<
" section is not present in file.";
1747 auto secSrc = secContent.value();
1748 ParserHead ph{secSrc, sectionNameAttr};
1749 FailureOr<uint32_t> nElemsParsed = ph.parseVectorSize();
1750 if (
failed(nElemsParsed))
1752 uint32_t nElems = *nElemsParsed;
1753 LDBG() <<
"starting to parse " << nElems <<
" items for section "
1755 for (
size_t i = 0; i < nElems; ++i) {
1756 if (
failed(parseSectionItem<section>(ph, i)))
1761 return emitError(getLocation(),
"unparsed garbage at end of section ")
1767 LogicalResult visitImport(Location loc, StringRef moduleName,
1768 StringRef importName, TypeIdxRecord tid) {
1770 if (tid.id >= symbols.moduleFuncTypes.size())
1771 return emitError(loc,
"invalid type id: ")
1772 << tid.id <<
". Only " << symbols.moduleFuncTypes.size()
1773 <<
" type registrations";
1774 FunctionType type = symbols.moduleFuncTypes[tid.id];
1775 std::string symbol = symbols.getNewFuncSymbolName();
1776 auto funcOp = FuncImportOp::create(builder, loc, symbol, moduleName,
1779 return funcOp.verify();
1783 LogicalResult visitImport(Location loc, StringRef moduleName,
1784 StringRef importName, LimitType limitType) {
1785 std::string symbol = symbols.getNewMemorySymbolName();
1786 auto memOp = MemImportOp::create(builder, loc, symbol, moduleName,
1787 importName, limitType);
1789 return memOp.verify();
1793 LogicalResult visitImport(Location loc, StringRef moduleName,
1794 StringRef importName, TableType tableType) {
1795 std::string symbol = symbols.getNewTableSymbolName();
1796 auto tableOp = TableImportOp::create(builder, loc, symbol, moduleName,
1797 importName, tableType);
1799 return tableOp.verify();
1803 LogicalResult visitImport(Location loc, StringRef moduleName,
1804 StringRef importName, GlobalTypeRecord globalType) {
1805 std::string symbol = symbols.getNewGlobalSymbolName();
1807 GlobalImportOp::create(builder, loc, symbol, moduleName, importName,
1808 globalType.type, globalType.isMutable);
1809 symbols.globalSymbols.push_back(
1811 return giOp.verify();
1815 LogicalResult peekDiag(Diagnostic &
diag) {
1816 if (
diag.getSeverity() == DiagnosticSeverity::Error)
1822 WasmBinaryParser(llvm::SourceMgr &sourceMgr, MLIRContext *ctx)
1823 : builder{ctx}, ctx{ctx} {
1825 [
this](Diagnostic &
diag) {
return peekDiag(
diag); });
1827 if (sourceMgr.getNumBuffers() != 1) {
1828 emitError(UnknownLoc::get(ctx),
"one source file should be provided");
1831 uint32_t sourceBufId = sourceMgr.getMainFileID();
1832 StringRef source = sourceMgr.getMemoryBuffer(sourceBufId)->getBuffer();
1833 srcName = StringAttr::get(
1834 ctx, sourceMgr.getMemoryBuffer(sourceBufId)->getBufferIdentifier());
1836 auto parser = ParserHead{source, srcName};
1837 auto const wasmHeader = StringRef{
"\0asm", 4};
1838 FileLineColLoc magicLoc = parser.getLocation();
1839 FailureOr<StringRef> magic = parser.consumeNBytes(wasmHeader.size());
1840 if (
failed(magic) || magic->compare(wasmHeader)) {
1841 emitError(magicLoc,
"source file does not contain valid Wasm header");
1844 auto const expectedVersionString = StringRef{
"\1\0\0\0", 4};
1845 FileLineColLoc versionLoc = parser.getLocation();
1846 FailureOr<StringRef> version =
1847 parser.consumeNBytes(expectedVersionString.size());
1850 if (version->compare(expectedVersionString)) {
1852 "unsupported Wasm version. only version 1 is supported");
1855 LogicalResult fillRegistry = registry.populateFromBody(parser.copy());
1856 if (
failed(fillRegistry))
1859 mOp = ModuleOp::create(builder, getLocation());
1861 LogicalResult parsingTypes = parseSection<WasmSectionType::TYPE>();
1862 if (
failed(parsingTypes))
1865 LogicalResult parsingImports = parseSection<WasmSectionType::IMPORT>();
1866 if (
failed(parsingImports))
1869 firstInternalFuncID = symbols.funcSymbols.size();
1871 LogicalResult parsingFunctions = parseSection<WasmSectionType::FUNCTION>();
1872 if (
failed(parsingFunctions))
1875 LogicalResult parsingTables = parseSection<WasmSectionType::TABLE>();
1876 if (
failed(parsingTables))
1879 LogicalResult parsingMems = parseSection<WasmSectionType::MEMORY>();
1883 LogicalResult parsingGlobals = parseSection<WasmSectionType::GLOBAL>();
1884 if (
failed(parsingGlobals))
1887 LogicalResult parsingCode = parseSection<WasmSectionType::CODE>();
1891 LogicalResult parsingExports = parseSection<WasmSectionType::EXPORT>();
1892 if (
failed(parsingExports))
1896 LDBG() <<
"WASM Imports:"
1898 <<
" - Num functions: " << symbols.funcSymbols.size() <<
"\n"
1899 <<
" - Num globals: " << symbols.globalSymbols.size() <<
"\n"
1900 <<
" - Num memories: " << symbols.memSymbols.size() <<
"\n"
1901 <<
" - Num tables: " << symbols.tableSymbols.size();
1904 ModuleOp getModule() {
1913 mlir::StringAttr srcName;
1915 WasmModuleSymbolTables symbols;
1918 SectionRegistry registry;
1919 size_t firstInternalFuncID{0};
1925WasmBinaryParser::parseSectionItem<WasmSectionType::IMPORT>(ParserHead &ph,
1927 FileLineColLoc importLoc = ph.getLocation();
1928 auto moduleName = ph.parseName();
1932 auto importName = ph.parseName();
1936 FailureOr<ImportDesc>
import = ph.parseImportDesc(ctx);
1941 [
this, importLoc, &moduleName, &importName](
auto import) {
1942 return visitImport(importLoc, *moduleName, *importName,
import);
1949WasmBinaryParser::parseSectionItem<WasmSectionType::EXPORT>(ParserHead &ph,
1951 FileLineColLoc exportLoc = ph.getLocation();
1953 auto exportName = ph.parseName();
1957 FailureOr<std::byte> opcode = ph.consumeByte();
1961 FailureOr<uint32_t> idx = ph.parseLiteral<uint32_t>();
1965 using SymbolRefDesc = std::variant<SmallVector<SymbolRefContainer>,
1966 SmallVector<GlobalSymbolRefContainer>,
1967 SmallVector<FunctionSymbolRefContainer>>;
1969 SymbolRefDesc currentSymbolList;
1970 std::string symbolType =
"";
1973 symbolType =
"function";
1974 currentSymbolList = symbols.funcSymbols;
1977 symbolType =
"table";
1978 currentSymbolList = symbols.tableSymbols;
1981 symbolType =
"memory";
1982 currentSymbolList = symbols.memSymbols;
1985 symbolType =
"global";
1986 currentSymbolList = symbols.globalSymbols;
1989 return emitError(exportLoc,
"invalid value for export type: ")
1990 << std::to_integer<unsigned>(*opcode);
1993 auto currentSymbol = std::visit(
1994 [&](
const auto &list) -> FailureOr<FlatSymbolRefAttr> {
1995 if (*idx > list.size()) {
1999 "trying to export {0} {1} which is undefined in this scope",
2003 return list[*idx].symbol;
2007 if (
failed(currentSymbol))
2013 return SymbolTable{mOp}.rename(symName, *exportName);
2018WasmBinaryParser::parseSectionItem<WasmSectionType::TABLE>(ParserHead &ph,
2020 FileLineColLoc opLocation = ph.getLocation();
2021 FailureOr<TableType> tableType = ph.parseTableType(ctx);
2024 LDBG() <<
" Parsed table description: " << *tableType;
2025 StringAttr symbol = builder.
getStringAttr(symbols.getNewTableSymbolName());
2027 TableOp::create(builder, opLocation, symbol.strref(), *tableType);
2028 symbols.tableSymbols.push_back({SymbolRefAttr::get(tableOp)});
2034WasmBinaryParser::parseSectionItem<WasmSectionType::FUNCTION>(ParserHead &ph,
2036 FileLineColLoc opLoc = ph.getLocation();
2037 auto typeIdxParsed = ph.parseLiteral<uint32_t>();
2038 if (
failed(typeIdxParsed))
2040 uint32_t typeIdx = *typeIdxParsed;
2041 if (typeIdx >= symbols.moduleFuncTypes.size())
2042 return emitError(getLocation(),
"invalid type index: ") << typeIdx;
2043 std::string symbol = symbols.getNewFuncSymbolName();
2045 FuncOp::create(builder, opLoc, symbol, symbols.moduleFuncTypes[typeIdx]);
2046 Block *block = funcOp.addEntryBlock();
2047 OpBuilder::InsertionGuard guard{builder};
2049 ReturnOp::create(builder, opLoc);
2050 symbols.funcSymbols.push_back(
2052 symbols.moduleFuncTypes[typeIdx]});
2053 return funcOp.verify();
2058WasmBinaryParser::parseSectionItem<WasmSectionType::TYPE>(ParserHead &ph,
2060 FailureOr<FunctionType> funcType = ph.parseFunctionType(ctx);
2063 LDBG() <<
"Parsed function type " << *funcType;
2064 symbols.moduleFuncTypes.push_back(*funcType);
2070WasmBinaryParser::parseSectionItem<WasmSectionType::MEMORY>(ParserHead &ph,
2072 FileLineColLoc opLocation = ph.getLocation();
2073 FailureOr<LimitType> memory = ph.parseLimit(ctx);
2077 LDBG() <<
" Registering memory " << *memory;
2078 std::string symbol = symbols.getNewMemorySymbolName();
2079 auto memOp = MemOp::create(builder, opLocation, symbol, *memory);
2080 symbols.memSymbols.push_back({SymbolRefAttr::get(memOp)});
2086WasmBinaryParser::parseSectionItem<WasmSectionType::GLOBAL>(ParserHead &ph,
2088 FileLineColLoc globalLocation = ph.getLocation();
2089 auto globalTypeParsed = ph.parseGlobalType(ctx);
2090 if (
failed(globalTypeParsed))
2093 GlobalTypeRecord globalType = *globalTypeParsed;
2094 auto symbol = builder.
getStringAttr(symbols.getNewGlobalSymbolName());
2095 auto globalOp = wasmssa::GlobalOp::create(
2096 builder, globalLocation, symbol, globalType.type, globalType.isMutable);
2097 symbols.globalSymbols.push_back(
2099 OpBuilder::InsertionGuard guard{builder};
2102 parsed_inst_t expr = ph.parseExpression(builder, symbols);
2106 return emitError(globalLocation,
"global with empty initializer");
2107 if (expr->size() != 1 && (*expr)[0].getType() != globalType.type)
2110 "initializer result type does not match global declaration type");
2111 ReturnOp::create(builder, globalLocation, *expr);
2116LogicalResult WasmBinaryParser::parseSectionItem<WasmSectionType::CODE>(
2117 ParserHead &ph,
size_t innerFunctionId) {
2118 unsigned long funcId = innerFunctionId + firstInternalFuncID;
2119 FunctionSymbolRefContainer symRef = symbols.funcSymbols[funcId];
2123 if (
failed(ph.parseCodeFor(funcOp, symbols)))
2129namespace mlir::wasm {
2132 WasmBinaryParser wBN{source, context};
2133 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.
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.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
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.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
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 elseOpCode
static constexpr std::byte externRef
static constexpr std::byte i32
static constexpr std::byte funcType
static constexpr std::byte i64
static constexpr std::byte emptyBlockType
static constexpr std::byte funcRef
static constexpr std::byte v128
static constexpr std::byte f64
static constexpr std::byte f32
static constexpr std::byte endByte