[Mlir-commits] [mlir] [MLIR][WASM] - Introduce an importer for Wasm binaries (PR #152131)
Mehdi Amini
llvmlistbot at llvm.org
Tue Aug 5 06:33:33 PDT 2025
================
@@ -0,0 +1,1244 @@
+//===- TranslateFromWasm.cpp - Translating to C++ calls -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "mlir/Dialect/WasmSSA/IR/WasmSSA.h"
+#include "mlir/IR/Attributes.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinAttributeInterfaces.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/Location.h"
+#include "mlir/Target/Wasm/WasmBinaryEncoding.h"
+#include "mlir/Target/Wasm/WasmImporter.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/LEB128.h"
+
+#include <cstdint>
+#include <variant>
+
+#define DEBUG_TYPE "wasm-translate"
+
+// Statistics.
+STATISTIC(numFunctionSectionItems, "Parsed functions");
+STATISTIC(numGlobalSectionItems, "Parsed globals");
+STATISTIC(numMemorySectionItems, "Parsed memories");
+STATISTIC(numTableSectionItems, "Parsed tables");
+
+static_assert(CHAR_BIT == 8,
+ "This code expects std::byte to be exactly 8 bits");
+
+using namespace mlir;
+using namespace mlir::wasm;
+using namespace mlir::wasmssa;
+
+namespace {
+using section_id_t = uint8_t;
+enum struct WasmSectionType : section_id_t {
+ CUSTOM = 0,
+ TYPE = 1,
+ IMPORT = 2,
+ FUNCTION = 3,
+ TABLE = 4,
+ MEMORY = 5,
+ GLOBAL = 6,
+ EXPORT = 7,
+ START = 8,
+ ELEMENT = 9,
+ CODE = 10,
+ DATA = 11,
+ DATACOUNT = 12
+};
+
+constexpr section_id_t highestWasmSectionID{
+ static_cast<section_id_t>(WasmSectionType::DATACOUNT)};
+
+#define APPLY_WASM_SEC_TRANSFORM \
+ WASM_SEC_TRANSFORM(CUSTOM) \
+ WASM_SEC_TRANSFORM(TYPE) \
+ WASM_SEC_TRANSFORM(IMPORT) \
+ WASM_SEC_TRANSFORM(FUNCTION) \
+ WASM_SEC_TRANSFORM(TABLE) \
+ WASM_SEC_TRANSFORM(MEMORY) \
+ WASM_SEC_TRANSFORM(GLOBAL) \
+ WASM_SEC_TRANSFORM(EXPORT) \
+ WASM_SEC_TRANSFORM(START) \
+ WASM_SEC_TRANSFORM(ELEMENT) \
+ WASM_SEC_TRANSFORM(CODE) \
+ WASM_SEC_TRANSFORM(DATA) \
+ WASM_SEC_TRANSFORM(DATACOUNT)
+
+template <WasmSectionType>
+constexpr const char *wasmSectionName = "";
+
+#define WASM_SEC_TRANSFORM(section) \
+ template <> \
+ constexpr const char *wasmSectionName<WasmSectionType::section> = #section;
+APPLY_WASM_SEC_TRANSFORM
+#undef WASM_SEC_TRANSFORM
+
+constexpr bool sectionShouldBeUnique(WasmSectionType secType) {
+ return secType != WasmSectionType::CUSTOM;
+}
+
+template <std::byte... Bytes>
+struct ByteSequence {};
+
+template <std::byte... Bytes1, std::byte... Bytes2>
+constexpr ByteSequence<Bytes1..., Bytes2...>
+operator+(ByteSequence<Bytes1...>, ByteSequence<Bytes2...>) {
+ return {};
+}
+
+/// Template class for representing a byte sequence of only one byte
+template <std::byte Byte>
+struct UniqueByte : ByteSequence<Byte> {};
+
+template <typename T, T... Values>
+constexpr ByteSequence<std::byte{Values}...>
+byteSeqFromIntSeq(std::integer_sequence<T, Values...>) {
+ return {};
+}
+
+constexpr auto allOpCodes =
+ byteSeqFromIntSeq(std::make_integer_sequence<int, 256>());
+
+constexpr ByteSequence<
+ WasmBinaryEncoding::Type::i32, WasmBinaryEncoding::Type::i64,
+ WasmBinaryEncoding::Type::f32, WasmBinaryEncoding::Type::f64,
+ WasmBinaryEncoding::Type::v128>
+ valueTypesEncodings{};
+
+template <std::byte... allowedFlags>
+constexpr bool isValueOneOf(std::byte value,
+ ByteSequence<allowedFlags...> = {}) {
+ return ((value == allowedFlags) | ... | false);
+}
+
+template <std::byte... flags>
+constexpr bool isNotIn(std::byte value, ByteSequence<flags...> = {}) {
+ return !isValueOneOf<flags...>(value);
+}
+
+struct GlobalTypeRecord {
+ Type type;
+ bool isMutable;
+};
+
+struct TypeIdxRecord {
+ size_t id;
+};
+
+struct SymbolRefContainer {
+ FlatSymbolRefAttr symbol;
+};
+
+struct GlobalSymbolRefContainer : SymbolRefContainer {
+ Type globalType;
+};
+
+struct FunctionSymbolRefContainer : SymbolRefContainer {
+ FunctionType functionType;
+};
+
+using ImportDesc =
+ std::variant<TypeIdxRecord, TableType, LimitType, GlobalTypeRecord>;
+
+using parsed_inst_t = FailureOr<SmallVector<Value>>;
+
+struct WasmModuleSymbolTables {
+ SmallVector<FunctionSymbolRefContainer> funcSymbols;
+ SmallVector<GlobalSymbolRefContainer> globalSymbols;
+ SmallVector<SymbolRefContainer> memSymbols;
+ SmallVector<SymbolRefContainer> tableSymbols;
+ SmallVector<FunctionType> moduleFuncTypes;
+
+ std::string getNewSymbolName(StringRef prefix, size_t id) const {
+ return (prefix + Twine{id}).str();
+ }
+
+ std::string getNewFuncSymbolName() const {
+ auto id = funcSymbols.size();
+ return getNewSymbolName("func_", id);
+ }
+
+ std::string getNewGlobalSymbolName() const {
+ auto id = globalSymbols.size();
+ return getNewSymbolName("global_", id);
+ }
+
+ std::string getNewMemorySymbolName() const {
+ auto id = memSymbols.size();
+ return getNewSymbolName("mem_", id);
+ }
+
+ std::string getNewTableSymbolName() const {
+ auto id = tableSymbols.size();
+ return getNewSymbolName("table_", id);
+ }
+};
+
+class ParserHead;
+
+/// Wrapper around SmallVector to only allow access as push and pop on the
+/// stack. Makes sure that there are no "free accesses" on the stack to preserve
+/// its state.
+class ValueStack {
+private:
+ struct LabelLevel {
+ size_t stackIdx;
+ LabelLevelOpInterface levelOp;
+ };
+
+public:
+ bool empty() const { return values.empty(); }
+
+ size_t size() const { return values.size(); }
+
+ /// Pops values from the stack because they are being used in an operation.
+ /// @param operandTypes The list of expected types of the operation, used
+ /// to know how many values to pop and check if the types match the
+ /// expectation.
+ /// @param opLoc Location of the caller, used to report accurately the
+ /// location
+ /// if an error occurs.
+ /// @return Failure or the vector of popped values.
+ FailureOr<SmallVector<Value>> popOperands(TypeRange operandTypes,
+ Location *opLoc);
+
+ /// Push the results of an operation to the stack so they can be used in a
+ /// following operation.
+ /// @param results The list of results of the operation
+ /// @param opLoc Location of the caller, used to report accurately the
+ /// location
+ /// if an error occurs.
+ LogicalResult pushResults(ValueRange results, Location *opLoc);
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ /// A simple dump function for debugging.
+ /// Writes output to llvm::dbgs().
+ LLVM_DUMP_METHOD void dump() const;
+#endif
+
+private:
+ SmallVector<Value> values;
+};
+
+using local_val_t = TypedValue<wasmssa::LocalRefType>;
+
+class ExpressionParser {
+public:
+ using locals_t = SmallVector<local_val_t>;
+ ExpressionParser(ParserHead &parser, WasmModuleSymbolTables const &symbols,
+ ArrayRef<local_val_t> initLocal)
+ : parser{parser}, symbols{symbols}, locals{initLocal} {}
+
+private:
+ template <std::byte opCode>
+ inline parsed_inst_t parseSpecificInstruction(OpBuilder &builder);
+
+ template <typename valueT>
+ parsed_inst_t
+ parseConstInst(OpBuilder &builder,
+ std::enable_if_t<std::is_arithmetic_v<valueT>> * = nullptr);
+
+ /// This function generates a dispatch tree to associate an opcode with a
+ /// parser. Parsers are registered by specialising the
+ /// `parseSpecificInstruction` function for the op code to handle.
+ ///
+ /// The dispatcher is generated by recursively creating all possible patterns
+ /// for an opcode and calling the relevant parser on the leaf.
+ ///
+ /// @tparam patternBitSize is the first bit for which the pattern is not fixed
+ ///
+ /// @tparam highBitPattern is the fixed pattern that this instance handles for
+ /// the 8-patternBitSize bits
+ template <size_t patternBitSize = 0, std::byte highBitPattern = std::byte{0}>
+ inline parsed_inst_t dispatchToInstParser(std::byte opCode,
+ OpBuilder &builder) {
+ static_assert(patternBitSize <= 8,
+ "PatternBitSize is outside of range of opcode space! "
+ "(expected at most 8 bits)");
+ if constexpr (patternBitSize < 8) {
+ constexpr std::byte bitSelect{1 << (7 - patternBitSize)};
+ constexpr std::byte nextHighBitPatternStem = highBitPattern << 1;
+ constexpr size_t nextPatternBitSize = patternBitSize + 1;
+ if ((opCode & bitSelect) != std::byte{0})
+ return dispatchToInstParser<nextPatternBitSize,
+ nextHighBitPatternStem | std::byte{1}>(
+ opCode, builder);
+ return dispatchToInstParser<nextPatternBitSize, nextHighBitPatternStem>(
+ opCode, builder);
+ } else {
+ return parseSpecificInstruction<highBitPattern>(builder);
+ }
+ }
+
+ struct ParseResultWithInfo {
+ SmallVector<Value> opResults;
+ std::byte endingByte;
+ };
+
+public:
+ template <std::byte ParseEndByte = WasmBinaryEncoding::endByte>
+ parsed_inst_t parse(OpBuilder &builder, UniqueByte<ParseEndByte> = {});
+
+ template <std::byte... ExpressionParseEnd>
+ FailureOr<ParseResultWithInfo>
+ parse(OpBuilder &builder,
+ ByteSequence<ExpressionParseEnd...> parsingEndFilters);
+
+ FailureOr<SmallVector<Value>> popOperands(TypeRange operandTypes) {
+ return valueStack.popOperands(operandTypes, ¤tOpLoc.value());
+ }
+
+ LogicalResult pushResults(ValueRange results) {
+ return valueStack.pushResults(results, ¤tOpLoc.value());
+ }
+
+private:
+ std::optional<Location> currentOpLoc;
+ ParserHead &parser;
+ WasmModuleSymbolTables const &symbols;
+ locals_t locals;
+ ValueStack valueStack;
+};
+
+class ParserHead {
+public:
+ ParserHead(StringRef src, StringAttr name) : head{src}, locName{name} {}
+ ParserHead(ParserHead &&) = default;
+
+private:
+ ParserHead(ParserHead const &other) = default;
+
+public:
+ auto getLocation() const {
+ return FileLineColLoc::get(locName, 0, anchorOffset + offset);
+ }
+
+ FailureOr<StringRef> consumeNBytes(size_t nBytes) {
+ LLVM_DEBUG(llvm::dbgs() << "Consume " << nBytes << " bytes\n");
+ LLVM_DEBUG(llvm::dbgs() << " Bytes remaining: " << size() << "\n");
+ LLVM_DEBUG(llvm::dbgs() << " Current offset: " << offset << "\n");
+ if (nBytes > size())
+ return emitError(getLocation(), "trying to extract ")
+ << nBytes << "bytes when only " << size() << "are avilables";
+
+ StringRef res = head.slice(offset, offset + nBytes);
+ offset += nBytes;
+ LLVM_DEBUG(llvm::dbgs()
+ << " Updated offset (+" << nBytes << "): " << offset << "\n");
+ return res;
+ }
+
+ FailureOr<std::byte> consumeByte() {
+ auto res = consumeNBytes(1);
+ if (failed(res))
+ return failure();
+ return std::byte{*res->bytes_begin()};
+ }
+
+ template <typename T>
+ FailureOr<T> parseLiteral();
+
+ FailureOr<uint32_t> parseVectorSize();
+
+private:
+ // TODO: This is equivalent to parseLiteral<uint32_t> and could be removed
+ // if parseLiteral specialization were moved here, but default GCC on Ubuntu
+ // 22.04 has bug with template specialization in class declaration
+ inline FailureOr<uint32_t> parseUI32();
+ inline FailureOr<int64_t> parseI64();
+
+public:
+ FailureOr<StringRef> parseName() {
+ FailureOr<uint32_t> size = parseVectorSize();
+ if (failed(size))
+ return failure();
+
+ return consumeNBytes(*size);
+ }
+
+ FailureOr<WasmSectionType> parseWasmSectionType() {
+ FailureOr<std::byte> id = consumeByte();
+ if (failed(id))
+ return failure();
+ if (std::to_integer<unsigned>(*id) > highestWasmSectionID)
+ return emitError(getLocation(), "invalid section ID: ")
+ << static_cast<int>(*id);
+ return static_cast<WasmSectionType>(*id);
+ }
+
+ FailureOr<LimitType> parseLimit(MLIRContext *ctx) {
+ using WasmLimits = WasmBinaryEncoding::LimitHeader;
+ FileLineColLoc limitLocation = getLocation();
+ FailureOr<std::byte> limitHeader = consumeByte();
+ if (failed(limitHeader))
+ return failure();
+
+ if (isNotIn<WasmLimits::bothLimits, WasmLimits::lowLimitOnly>(*limitHeader))
+ return emitError(limitLocation, "invalid limit header: ")
+ << static_cast<int>(*limitHeader);
+ FailureOr<uint32_t> minParse = parseUI32();
+ if (failed(minParse))
+ return failure();
+ std::optional<uint32_t> max{std::nullopt};
+ if (*limitHeader == WasmLimits::bothLimits) {
+ FailureOr<uint32_t> maxParse = parseUI32();
+ if (failed(maxParse))
+ return failure();
+ max = *maxParse;
+ }
+ return LimitType::get(ctx, *minParse, max);
+ }
+
+ FailureOr<Type> parseValueType(MLIRContext *ctx) {
+ FileLineColLoc typeLoc = getLocation();
+ FailureOr<std::byte> typeEncoding = consumeByte();
+ if (failed(typeEncoding))
+ return failure();
+ switch (*typeEncoding) {
+ case WasmBinaryEncoding::Type::i32:
+ return IntegerType::get(ctx, 32);
+ case WasmBinaryEncoding::Type::i64:
+ return IntegerType::get(ctx, 64);
+ case WasmBinaryEncoding::Type::f32:
+ return Float32Type::get(ctx);
+ case WasmBinaryEncoding::Type::f64:
+ return Float64Type::get(ctx);
+ case WasmBinaryEncoding::Type::v128:
+ return IntegerType::get(ctx, 128);
+ case WasmBinaryEncoding::Type::funcRef:
+ return wasmssa::FuncRefType::get(ctx);
+ case WasmBinaryEncoding::Type::externRef:
+ return wasmssa::ExternRefType::get(ctx);
+ default:
+ return emitError(typeLoc, "invalid value type encoding: ")
+ << static_cast<int>(*typeEncoding);
+ }
+ }
+
+ FailureOr<GlobalTypeRecord> parseGlobalType(MLIRContext *ctx) {
+ using WasmGlobalMut = WasmBinaryEncoding::GlobalMutability;
+ FailureOr<Type> typeParsed = parseValueType(ctx);
+ if (failed(typeParsed))
+ return failure();
+ FileLineColLoc mutLoc = getLocation();
+ FailureOr<std::byte> mutSpec = consumeByte();
+ if (failed(mutSpec))
+ return failure();
+ if (isNotIn<WasmGlobalMut::isConst, WasmGlobalMut::isMutable>(*mutSpec))
+ return emitError(mutLoc, "invalid global mutability specifier: ")
+ << static_cast<int>(*mutSpec);
+ return GlobalTypeRecord{*typeParsed, *mutSpec == WasmGlobalMut::isMutable};
+ }
+
+ FailureOr<TupleType> parseResultType(MLIRContext *ctx) {
+ FailureOr<uint32_t> nParamsParsed = parseVectorSize();
+ if (failed(nParamsParsed))
+ return failure();
+ uint32_t nParams = *nParamsParsed;
+ SmallVector<Type> res{};
+ res.reserve(nParams);
+ for (size_t i = 0; i < nParams; ++i) {
+ FailureOr<Type> parsedType = parseValueType(ctx);
+ if (failed(parsedType))
+ return failure();
+ res.push_back(*parsedType);
+ }
+ return TupleType::get(ctx, res);
+ }
+
+ FailureOr<FunctionType> parseFunctionType(MLIRContext *ctx) {
+ FileLineColLoc typeLoc = getLocation();
+ FailureOr<std::byte> funcTypeHeader = consumeByte();
+ if (failed(funcTypeHeader))
+ return failure();
+ if (*funcTypeHeader != WasmBinaryEncoding::Type::funcType)
+ return emitError(typeLoc, "invalid function type header byte. Expecting ")
+ << std::to_integer<unsigned>(WasmBinaryEncoding::Type::funcType)
+ << " got " << std::to_integer<unsigned>(*funcTypeHeader);
+ FailureOr<TupleType> inputTypes = parseResultType(ctx);
+ if (failed(inputTypes))
+ return failure();
+
+ FailureOr<TupleType> resTypes = parseResultType(ctx);
+ if (failed(resTypes))
+ return failure();
+
+ return FunctionType::get(ctx, inputTypes->getTypes(), resTypes->getTypes());
+ }
+
+ FailureOr<TypeIdxRecord> parseTypeIndex() {
+ FailureOr<uint32_t> res = parseUI32();
+ if (failed(res))
+ return failure();
+ return TypeIdxRecord{*res};
+ }
+
+ FailureOr<TableType> parseTableType(MLIRContext *ctx) {
+ FailureOr<Type> elmTypeParse = parseValueType(ctx);
+ if (failed(elmTypeParse))
+ return failure();
+ if (!isWasmRefType(*elmTypeParse))
+ return emitError(getLocation(), "invalid element type for table");
+ FailureOr<LimitType> limitParse = parseLimit(ctx);
+ if (failed(limitParse))
+ return failure();
+ return TableType::get(ctx, *elmTypeParse, *limitParse);
+ }
+
+ FailureOr<ImportDesc> parseImportDesc(MLIRContext *ctx) {
+ FileLineColLoc importLoc = getLocation();
+ FailureOr<std::byte> importType = consumeByte();
+ auto packager = [](auto parseResult) -> FailureOr<ImportDesc> {
+ if (llvm::failed(parseResult))
+ return failure();
+ return {*parseResult};
+ };
+ if (failed(importType))
+ return failure();
+ switch (*importType) {
+ case WasmBinaryEncoding::Import::typeID:
+ return packager(parseTypeIndex());
+ case WasmBinaryEncoding::Import::tableType:
+ return packager(parseTableType(ctx));
+ case WasmBinaryEncoding::Import::memType:
+ return packager(parseLimit(ctx));
+ case WasmBinaryEncoding::Import::globalType:
+ return packager(parseGlobalType(ctx));
+ default:
+ return emitError(importLoc, "invalid import type descriptor: ")
+ << static_cast<int>(*importType);
+ }
+ }
+
+ parsed_inst_t parseExpression(OpBuilder &builder,
+ WasmModuleSymbolTables const &symbols,
+ ArrayRef<local_val_t> locals = {}) {
+ auto eParser = ExpressionParser{*this, symbols, locals};
+ return eParser.parse(builder);
+ }
+
+ bool end() const { return curHead().empty(); }
+
+ ParserHead copy() const { return *this; }
+
+private:
+ StringRef curHead() const { return head.drop_front(offset); }
+
+ FailureOr<std::byte> peek() const {
+ if (end())
+ return emitError(
+ getLocation(),
+ "trying to peek at next byte, but input stream is empty");
+ return static_cast<std::byte>(curHead().front());
+ }
+
+ size_t size() const { return head.size() - offset; }
+
+ StringRef head;
+ StringAttr locName;
+ unsigned anchorOffset{0};
+ unsigned offset{0};
+};
+
+template <>
+FailureOr<float> ParserHead::parseLiteral<float>() {
+ auto bytes = consumeNBytes(4);
+ if (failed(bytes))
+ return failure();
+ float result;
+ std::memcpy(&result, bytes->bytes_begin(), 4);
+ return result;
+}
+
+template <>
+FailureOr<double> ParserHead::parseLiteral<double>() {
+ auto bytes = consumeNBytes(8);
+ if (failed(bytes))
+ return failure();
+ double result;
+ std::memcpy(&result, bytes->bytes_begin(), 8);
+ return result;
+}
+
+template <>
+FailureOr<uint32_t> ParserHead::parseLiteral<uint32_t>() {
+ char const *error = nullptr;
+ uint32_t res{0};
+ unsigned encodingSize{0};
+ StringRef src = curHead();
+ uint64_t decoded = llvm::decodeULEB128(src.bytes_begin(), &encodingSize,
+ src.bytes_end(), &error);
+ if (error)
+ return emitError(getLocation(), error);
+
+ if (std::isgreater(decoded, std::numeric_limits<uint32_t>::max()))
+ return emitError(getLocation()) << "literal does not fit on 32 bits";
+
+ res = static_cast<uint32_t>(decoded);
+ offset += encodingSize;
+ return res;
+}
+
+template <>
+FailureOr<int32_t> ParserHead::parseLiteral<int32_t>() {
+ char const *error = nullptr;
+ int32_t res{0};
+ unsigned encodingSize{0};
+ StringRef src = curHead();
+ int64_t decoded = llvm::decodeSLEB128(src.bytes_begin(), &encodingSize,
+ src.bytes_end(), &error);
+ if (error)
+ return emitError(getLocation(), error);
+ if (std::isgreater(decoded, std::numeric_limits<int32_t>::max()) ||
+ std::isgreater(std::numeric_limits<int32_t>::min(), decoded))
+ return emitError(getLocation()) << "literal does not fit on 32 bits";
+
+ res = static_cast<int32_t>(decoded);
+ offset += encodingSize;
+ return res;
+}
+
+template <>
+FailureOr<int64_t> ParserHead::parseLiteral<int64_t>() {
+ char const *error = nullptr;
+ unsigned encodingSize{0};
+ StringRef src = curHead();
+ int64_t res = llvm::decodeSLEB128(src.bytes_begin(), &encodingSize,
+ src.bytes_end(), &error);
+ if (error)
+ return emitError(getLocation(), error);
+
+ offset += encodingSize;
+ return res;
+}
+
+FailureOr<uint32_t> ParserHead::parseVectorSize() {
+ return parseLiteral<uint32_t>();
+}
+
+inline FailureOr<uint32_t> ParserHead::parseUI32() {
+ return parseLiteral<uint32_t>();
+}
+
+inline FailureOr<int64_t> ParserHead::parseI64() {
+ return parseLiteral<int64_t>();
+}
+
+template <std::byte opCode>
+inline parsed_inst_t ExpressionParser::parseSpecificInstruction(OpBuilder &) {
+ return emitError(*currentOpLoc, "unknown instruction opcode: ")
+ << static_cast<int>(opCode);
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+void ValueStack::dump() const {
+ llvm::dbgs() << "================= Wasm ValueStack =======================\n";
+ llvm::dbgs() << "size: " << size() << "\n";
+ llvm::dbgs() << "<Top>"
+ << "\n";
+ // Stack is pushed to via push_back. Therefore the top of the stack is the
+ // end of the vector. Iterate in reverse so that the first thing we print
+ // is the top of the stack.
+ size_t stackSize = size();
+ for (size_t idx = 0; idx < stackSize;) {
+ size_t actualIdx = stackSize - 1 - idx;
+ llvm::dbgs() << " ";
+ values[actualIdx].dump();
+ }
+ llvm::dbgs() << "<Bottom>"
+ << "\n";
+ llvm::dbgs() << "=========================================================\n";
+}
+#endif
+
+parsed_inst_t ValueStack::popOperands(TypeRange operandTypes, Location *opLoc) {
+ LLVM_DEBUG(llvm::dbgs() << "Popping from ValueStack\n");
----------------
joker-eph wrote:
I would recommend including `"llvm/Support/DebugLog.h` and replacing this line with:
```suggestion
LDBG() << "Popping from ValueStack";
```
https://github.com/llvm/llvm-project/pull/152131
More information about the Mlir-commits
mailing list