Skip to content

[MLIR][WASM] Extending the Wasm binary to WasmSSA dialect importer #154053

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 19, 2025

Conversation

lforg37
Copy link
Contributor

@lforg37 lforg37 commented Aug 18, 2025

This is the continuation of #152131

This PR adds support for parsing the global initializer and function body, and support for decoding scalar numerical instructions and variable related instructions.

lforg37 and others added 2 commits August 18, 2025 11:26
---------

Co-authored-by: Ferdinand Lemaire <ferdinand.lemaire@woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette@woven-planet.global>
---------

Co-authored-by: Ferdinand Lemaire <ferdinand.lemaire@woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette@woven-planet.global>
Copy link

github-actions bot commented Aug 18, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

lforg37 and others added 3 commits August 18, 2025 12:43
---------

Co-authored-by: Ferdinand Lemaire <ferdinand.lemaire@woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette@woven-planet.global>
---------

Co-authored-by: Ferdinand Lemaire <ferdinand.lemaire@woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette@woven-planet.global>
@lforg37 lforg37 force-pushed the lforget.upstream_wasm_mlir_translate branch from ac0d84d to 5421c85 Compare August 18, 2025 03:43
@lforg37 lforg37 marked this pull request as ready for review August 18, 2025 05:23
@llvmbot llvmbot added the mlir label Aug 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 18, 2025

@llvm/pr-subscribers-mlir

Author: Luc Forget (lforg37)

Changes

This is the continuation of #152131

This PR adds support for parsing the global initializer and function body, and support for decoding scalar numerical instructions, variable related instruction,


Patch is 81.42 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/154053.diff

48 Files Affected:

  • (modified) mlir/include/mlir/Target/Wasm/WasmBinaryEncoding.h (+69)
  • (modified) mlir/lib/Target/Wasm/TranslateFromWasm.cpp (+296-15)
  • (added) mlir/test/Target/Wasm/abs.mlir (+23)
  • (added) mlir/test/Target/Wasm/and.mlir (+27)
  • (added) mlir/test/Target/Wasm/clz.mlir (+25)
  • (added) mlir/test/Target/Wasm/const.mlir (+37)
  • (added) mlir/test/Target/Wasm/copysign.mlir (+31)
  • (added) mlir/test/Target/Wasm/ctz.mlir (+25)
  • (added) mlir/test/Target/Wasm/div.mlir (+127)
  • (added) mlir/test/Target/Wasm/global.mlir (+66)
  • (added) mlir/test/Target/Wasm/inputs/abs.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/and.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/clz.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/const.yaml.wasm (+39)
  • (added) mlir/test/Target/Wasm/inputs/copysign.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/ctz.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/div.yaml.wasm (+89)
  • (added) mlir/test/Target/Wasm/inputs/global.yaml.wasm (+63)
  • (added) mlir/test/Target/Wasm/inputs/local.yaml.wasm (+37)
  • (added) mlir/test/Target/Wasm/inputs/max.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/min.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/neg.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/or.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/popcnt.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/rem.yaml.wasm (+45)
  • (added) mlir/test/Target/Wasm/inputs/rotl.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/rotr.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/shl.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/shr_s.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/shr_u.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/sqrt.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/inputs/sub.yaml.wasm (+39)
  • (added) mlir/test/Target/Wasm/inputs/xor.yaml.wasm (+33)
  • (added) mlir/test/Target/Wasm/local.mlir (+59)
  • (added) mlir/test/Target/Wasm/max.mlir (+30)
  • (added) mlir/test/Target/Wasm/min.mlir (+29)
  • (added) mlir/test/Target/Wasm/neg.mlir (+23)
  • (added) mlir/test/Target/Wasm/or.mlir (+27)
  • (added) mlir/test/Target/Wasm/popcnt.mlir (+25)
  • (added) mlir/test/Target/Wasm/rem.mlir (+53)
  • (added) mlir/test/Target/Wasm/rotl.mlir (+27)
  • (added) mlir/test/Target/Wasm/rotr.mlir (+27)
  • (added) mlir/test/Target/Wasm/shl.mlir (+27)
  • (added) mlir/test/Target/Wasm/shr_s.mlir (+27)
  • (added) mlir/test/Target/Wasm/shr_u.mlir (+27)
  • (added) mlir/test/Target/Wasm/sqrt.mlir (+23)
  • (added) mlir/test/Target/Wasm/sub.mlir (+52)
  • (added) mlir/test/Target/Wasm/xor.mlir (+27)
diff --git a/mlir/include/mlir/Target/Wasm/WasmBinaryEncoding.h b/mlir/include/mlir/Target/Wasm/WasmBinaryEncoding.h
index 3280432b5f038..21adde878994e 100644
--- a/mlir/include/mlir/Target/Wasm/WasmBinaryEncoding.h
+++ b/mlir/include/mlir/Target/Wasm/WasmBinaryEncoding.h
@@ -20,10 +20,79 @@ struct WasmBinaryEncoding {
   /// Byte encodings for Wasm instructions.
   struct OpCode {
     // Locals, globals, constants.
+    static constexpr std::byte localGet{0x20};
+    static constexpr std::byte localSet{0x21};
+    static constexpr std::byte localTee{0x22};
+    static constexpr std::byte globalGet{0x23};
     static constexpr std::byte constI32{0x41};
     static constexpr std::byte constI64{0x42};
     static constexpr std::byte constFP32{0x43};
     static constexpr std::byte constFP64{0x44};
+
+    // Numeric operations.
+    static constexpr std::byte clzI32{0x67};
+    static constexpr std::byte ctzI32{0x68};
+    static constexpr std::byte popcntI32{0x69};
+    static constexpr std::byte addI32{0x6A};
+    static constexpr std::byte subI32{0x6B};
+    static constexpr std::byte mulI32{0x6C};
+    static constexpr std::byte divSI32{0x6d};
+    static constexpr std::byte divUI32{0x6e};
+    static constexpr std::byte remSI32{0x6f};
+    static constexpr std::byte remUI32{0x70};
+    static constexpr std::byte andI32{0x71};
+    static constexpr std::byte orI32{0x72};
+    static constexpr std::byte xorI32{0x73};
+    static constexpr std::byte shlI32{0x74};
+    static constexpr std::byte shrSI32{0x75};
+    static constexpr std::byte shrUI32{0x76};
+    static constexpr std::byte rotlI32{0x77};
+    static constexpr std::byte rotrI32{0x78};
+    static constexpr std::byte clzI64{0x79};
+    static constexpr std::byte ctzI64{0x7A};
+    static constexpr std::byte popcntI64{0x7B};
+    static constexpr std::byte addI64{0x7C};
+    static constexpr std::byte subI64{0x7D};
+    static constexpr std::byte mulI64{0x7E};
+    static constexpr std::byte divSI64{0x7F};
+    static constexpr std::byte divUI64{0x80};
+    static constexpr std::byte remSI64{0x81};
+    static constexpr std::byte remUI64{0x82};
+    static constexpr std::byte andI64{0x83};
+    static constexpr std::byte orI64{0x84};
+    static constexpr std::byte xorI64{0x85};
+    static constexpr std::byte shlI64{0x86};
+    static constexpr std::byte shrSI64{0x87};
+    static constexpr std::byte shrUI64{0x88};
+    static constexpr std::byte rotlI64{0x89};
+    static constexpr std::byte rotrI64{0x8A};
+    static constexpr std::byte absF32{0x8B};
+    static constexpr std::byte negF32{0x8C};
+    static constexpr std::byte ceilF32{0x8D};
+    static constexpr std::byte floorF32{0x8E};
+    static constexpr std::byte truncF32{0x8F};
+    static constexpr std::byte sqrtF32{0x91};
+    static constexpr std::byte addF32{0x92};
+    static constexpr std::byte subF32{0x93};
+    static constexpr std::byte mulF32{0x94};
+    static constexpr std::byte divF32{0x95};
+    static constexpr std::byte minF32{0x96};
+    static constexpr std::byte maxF32{0x97};
+    static constexpr std::byte copysignF32{0x98};
+    static constexpr std::byte absF64{0x99};
+    static constexpr std::byte negF64{0x9A};
+    static constexpr std::byte ceilF64{0x9B};
+    static constexpr std::byte floorF64{0x9C};
+    static constexpr std::byte truncF64{0x9D};
+    static constexpr std::byte sqrtF64{0x9F};
+    static constexpr std::byte addF64{0xA0};
+    static constexpr std::byte subF64{0xA1};
+    static constexpr std::byte mulF64{0xA2};
+    static constexpr std::byte divF64{0xA3};
+    static constexpr std::byte minF64{0xA4};
+    static constexpr std::byte maxF64{0xA5};
+    static constexpr std::byte copysignF64{0xA6};
+    static constexpr std::byte wrap{0xA7};
   };
 
   /// Byte encodings of types in Wasm binaries
diff --git a/mlir/lib/Target/Wasm/TranslateFromWasm.cpp b/mlir/lib/Target/Wasm/TranslateFromWasm.cpp
index da811ba0954c2..e2fddf34e5256 100644
--- a/mlir/lib/Target/Wasm/TranslateFromWasm.cpp
+++ b/mlir/lib/Target/Wasm/TranslateFromWasm.cpp
@@ -16,15 +16,16 @@
 #include "mlir/IR/BuiltinAttributeInterfaces.h"
 #include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/Location.h"
+#include "mlir/Support/LLVM.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/DebugLog.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/LEB128.h"
+#include "llvm/Support/LogicalResult.h"
 
-#include <climits>
+#include <cstddef>
 #include <cstdint>
 #include <variant>
 
@@ -148,22 +149,22 @@ struct WasmModuleSymbolTables {
   }
 
   std::string getNewFuncSymbolName() const {
-    auto id = funcSymbols.size();
+    size_t id = funcSymbols.size();
     return getNewSymbolName("func_", id);
   }
 
   std::string getNewGlobalSymbolName() const {
-    auto id = globalSymbols.size();
+    size_t id = globalSymbols.size();
     return getNewSymbolName("global_", id);
   }
 
   std::string getNewMemorySymbolName() const {
-    auto id = memSymbols.size();
+    size_t id = memSymbols.size();
     return getNewSymbolName("mem_", id);
   }
 
   std::string getNewTableSymbolName() const {
-    auto id = tableSymbols.size();
+    size_t id = tableSymbols.size();
     return getNewSymbolName("table_", id);
   }
 };
@@ -232,6 +233,20 @@ class ExpressionParser {
   parseConstInst(OpBuilder &builder,
                  std::enable_if_t<std::is_arithmetic_v<valueT>> * = nullptr);
 
+  /// Construct an operation with \p numOperands operands and a single result.
+  /// Each operand must have the same type. Suitable for e.g. binops, unary
+  /// ops, etc.
+  ///
+  /// \p opcode - The WASM opcode to build.
+  /// \p valueType - The operand and result type for the built instruction.
+  /// \p numOperands - The number of operands for the built operation.
+  ///
+  /// \returns The parsed instruction result, or failure.
+  template <typename opcode, typename valueType, unsigned int numOperands>
+  inline parsed_inst_t
+  buildNumericOp(OpBuilder &builder,
+                 std::enable_if_t<std::is_arithmetic_v<valueType>> * = 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.
@@ -286,10 +301,16 @@ class ExpressionParser {
     return valueStack.pushResults(results, &currentOpLoc.value());
   }
 
+  /// The local.set and local.tee operations behave similarly and only differ
+  /// on their return value. This function factorizes the behavior of the two
+  /// operations in one place.
+  template <typename OpToCreate>
+  parsed_inst_t parseSetOrTee(OpBuilder &);
+
 private:
   std::optional<Location> currentOpLoc;
   ParserHead &parser;
-  [[maybe_unused]] WasmModuleSymbolTables const &symbols;
+  WasmModuleSymbolTables const &symbols;
   locals_t locals;
   ValueStack valueStack;
 };
@@ -322,7 +343,7 @@ class ParserHead {
   }
 
   FailureOr<std::byte> consumeByte() {
-    auto res = consumeNBytes(1);
+    FailureOr<StringRef> res = consumeNBytes(1);
     if (failed(res))
       return failure();
     return std::byte{*res->bytes_begin()};
@@ -482,7 +503,7 @@ class ParserHead {
     FileLineColLoc importLoc = getLocation();
     FailureOr<std::byte> importType = consumeByte();
     auto packager = [](auto parseResult) -> FailureOr<ImportDesc> {
-      if (llvm::failed(parseResult))
+      if (failed(parseResult))
         return failure();
       return {*parseResult};
     };
@@ -510,6 +531,60 @@ class ParserHead {
     return eParser.parse(builder);
   }
 
+  LogicalResult parseCodeFor(FuncOp func,
+                             WasmModuleSymbolTables const &symbols) {
+    SmallVector<local_val_t> locals{};
+    // Populating locals with function argument
+    Block &block = func.getBody().front();
+    // Delete temporary return argument which was only created for IR validity
+    assert(func.getBody().getBlocks().size() == 1 &&
+           "Function should only have its default created block at this point");
+    assert(block.getOperations().size() == 1 &&
+           "Only the placeholder return op should be present at this point");
+    auto returnOp = cast<ReturnOp>(&block.back());
+    assert(returnOp);
+
+    FailureOr<uint32_t> codeSizeInBytes = parseUI32();
+    if (failed(codeSizeInBytes))
+      return failure();
+    FailureOr<StringRef> codeContent = consumeNBytes(*codeSizeInBytes);
+    if (failed(codeContent))
+      return failure();
+    auto name = StringAttr::get(func->getContext(),
+                                locName.str() + "::" + func.getSymName());
+    auto cParser = ParserHead{*codeContent, name};
+    FailureOr<uint32_t> localVecSize = cParser.parseVectorSize();
+    if (failed(localVecSize))
+      return failure();
+    OpBuilder builder{&func.getBody().front().back()};
+    for (auto arg : block.getArguments())
+      locals.push_back(cast<TypedValue<LocalRefType>>(arg));
+    // Declare the local ops
+    uint32_t nVarVec = *localVecSize;
+    for (size_t i = 0; i < nVarVec; ++i) {
+      FileLineColLoc varLoc = cParser.getLocation();
+      FailureOr<uint32_t> nSubVar = cParser.parseUI32();
+      if (failed(nSubVar))
+        return failure();
+      FailureOr<Type> varT = cParser.parseValueType(func->getContext());
+      if (failed(varT))
+        return failure();
+      for (size_t j = 0; j < *nSubVar; ++j) {
+        auto local = builder.create<LocalOp>(varLoc, *varT);
+        locals.push_back(local.getResult());
+      }
+    }
+    parsed_inst_t res = cParser.parseExpression(builder, symbols, locals);
+    if (failed(res))
+      return failure();
+    if (!cParser.end())
+      return emitError(cParser.getLocation(),
+                       "unparsed garbage remaining at end of code block");
+    builder.create<ReturnOp>(func->getLoc(), *res);
+    returnOp->erase();
+    return success();
+  }
+
   bool end() const { return curHead().empty(); }
 
   ParserHead copy() const { return *this; }
@@ -535,7 +610,7 @@ class ParserHead {
 
 template <>
 FailureOr<float> ParserHead::parseLiteral<float>() {
-  auto bytes = consumeNBytes(4);
+  FailureOr<StringRef> bytes = consumeNBytes(4);
   if (failed(bytes))
     return failure();
   float result;
@@ -545,7 +620,7 @@ FailureOr<float> ParserHead::parseLiteral<float>() {
 
 template <>
 FailureOr<double> ParserHead::parseLiteral<double>() {
-  auto bytes = consumeNBytes(8);
+  FailureOr<StringRef> bytes = consumeNBytes(8);
   if (failed(bytes))
     return failure();
   double result;
@@ -650,7 +725,7 @@ parsed_inst_t ValueStack::popOperands(TypeRange operandTypes, Location *opLoc) {
          << "  Current stack size: " << values.size();
   if (operandTypes.size() > values.size())
     return emitError(*opLoc,
-                     "stack doesn't contain enough values. Trying to get ")
+                     "stack doesn't contain enough values. trying to get ")
            << operandTypes.size() << " operands on a stack containing only "
            << values.size() << " values.";
   size_t stackIdxOffset = values.size() - operandTypes.size();
@@ -660,7 +735,7 @@ parsed_inst_t ValueStack::popOperands(TypeRange operandTypes, Location *opLoc) {
     Value operand = values[i + stackIdxOffset];
     Type stackType = operand.getType();
     if (stackType != operandTypes[i])
-      return emitError(*opLoc, "invalid operand type on stack. Expecting ")
+      return emitError(*opLoc, "invalid operand type on stack. expecting ")
              << operandTypes[i] << ", value on stack is of type " << stackType
              << ".";
     LDBG() << "    POP: " << operand;
@@ -718,6 +793,70 @@ ExpressionParser::parse(OpBuilder &builder,
   }
 }
 
+template <>
+inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
+    WasmBinaryEncoding::OpCode::localGet>(OpBuilder &builder) {
+  FailureOr<uint32_t> id = parser.parseLiteral<uint32_t>();
+  Location instLoc = *currentOpLoc;
+  if (failed(id))
+    return failure();
+  if (*id >= locals.size())
+    return emitError(instLoc, "invalid local index. function has ")
+           << locals.size() << " accessible locals, received index " << *id;
+  return {{builder.create<LocalGetOp>(instLoc, locals[*id]).getResult()}};
+}
+
+template <>
+inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
+    WasmBinaryEncoding::OpCode::globalGet>(OpBuilder &builder) {
+  FailureOr<uint32_t> id = parser.parseLiteral<uint32_t>();
+  Location instLoc = *currentOpLoc;
+  if (failed(id))
+    return failure();
+  if (*id >= symbols.globalSymbols.size())
+    return emitError(instLoc, "invalid global index. function has ")
+           << symbols.globalSymbols.size()
+           << " accessible globals, received index " << *id;
+  GlobalSymbolRefContainer globalVar = symbols.globalSymbols[*id];
+  auto globalOp = builder.create<GlobalGetOp>(instLoc, globalVar.globalType,
+                                              globalVar.symbol);
+
+  return {{globalOp.getResult()}};
+}
+
+template <typename OpToCreate>
+parsed_inst_t ExpressionParser::parseSetOrTee(OpBuilder &builder) {
+  FailureOr<uint32_t> id = parser.parseLiteral<uint32_t>();
+  if (failed(id))
+    return failure();
+  if (*id >= locals.size())
+    return emitError(*currentOpLoc, "invalid local index. function has ")
+           << locals.size() << " accessible locals, received index " << *id;
+  if (valueStack.empty())
+    return emitError(
+        *currentOpLoc,
+        "invalid stack access, trying to access a value on an empty stack.");
+
+  parsed_inst_t poppedOp = popOperands(locals[*id].getType().getElementType());
+  if (failed(poppedOp))
+    return failure();
+  return {
+      builder.create<OpToCreate>(*currentOpLoc, locals[*id], poppedOp->front())
+          ->getResults()};
+}
+
+template <>
+inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
+    WasmBinaryEncoding::OpCode::localSet>(OpBuilder &builder) {
+  return parseSetOrTee<LocalSetOp>(builder);
+}
+
+template <>
+inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
+    WasmBinaryEncoding::OpCode::localTee>(OpBuilder &builder) {
+  return parseSetOrTee<LocalTeeOp>(builder);
+}
+
 template <typename T>
 inline Type buildLiteralType(OpBuilder &);
 
@@ -809,6 +948,95 @@ inline parsed_inst_t ExpressionParser::parseSpecificInstruction<
   return parseConstInst<double>(builder);
 }
 
+template <typename opcode, typename valueType, unsigned int numOperands>
+inline parsed_inst_t ExpressionParser::buildNumericOp(
+    OpBuilder &builder, std::enable_if_t<std::is_arithmetic_v<valueType>> *) {
+  auto ty = buildLiteralType<valueType>(builder);
+  LLVM_DEBUG(llvm::dbgs() << "*** buildNumericOp: numOperands = " << numOperands
+                          << ", type = " << ty << " ***\n");
+  auto tysToPop = SmallVector<Type, numOperands>();
+  tysToPop.resize(numOperands);
+  std::fill(tysToPop.begin(), tysToPop.end(), ty);
+  auto operands = popOperands(tysToPop);
+  if (failed(operands))
+    return failure();
+  auto op = builder.create<opcode>(*currentOpLoc, *operands).getResult();
+  LLVM_DEBUG(llvm::dbgs() << "Built: ");
+  LLVM_DEBUG(op.dump());
+  return {{op}};
+}
+
+// Convenience macro for generating numerical operations.
+#define BUILD_NUMERIC_OP(OP_NAME, N_ARGS, PREFIX, SUFFIX, TYPE)                \
+  template <>                                                                  \
+  inline parsed_inst_t ExpressionParser::parseSpecificInstruction<             \
+      WasmBinaryEncoding::OpCode::PREFIX##SUFFIX>(OpBuilder & builder) {       \
+    return buildNumericOp<OP_NAME, TYPE, N_ARGS>(builder);                     \
+  }
+
+// Macro to define binops that only support integer types.
+#define BUILD_NUMERIC_BINOP_INT(OP_NAME, PREFIX)                               \
+  BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, I32, int32_t)                           \
+  BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, I64, int64_t)
+
+// Macro to define binops that only support floating point types.
+#define BUILD_NUMERIC_BINOP_FP(OP_NAME, PREFIX)                                \
+  BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, F32, float)                             \
+  BUILD_NUMERIC_OP(OP_NAME, 2, PREFIX, F64, double)
+
+// Macro to define binops that support both floating point and integer types.
+#define BUILD_NUMERIC_BINOP_INTFP(OP_NAME, PREFIX)                             \
+  BUILD_NUMERIC_BINOP_INT(OP_NAME, PREFIX)                                     \
+  BUILD_NUMERIC_BINOP_FP(OP_NAME, PREFIX)
+
+// Macro to implement unary ops that only support integers.
+#define BUILD_NUMERIC_UNARY_OP_INT(OP_NAME, PREFIX)                            \
+  BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, I32, int32_t)                           \
+  BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, I64, int64_t)
+
+// Macro to implement unary ops that support integer and floating point types.
+#define BUILD_NUMERIC_UNARY_OP_FP(OP_NAME, PREFIX)                             \
+  BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, F32, float)                             \
+  BUILD_NUMERIC_OP(OP_NAME, 1, PREFIX, F64, double)
+
+BUILD_NUMERIC_BINOP_FP(CopySignOp, copysign)
+BUILD_NUMERIC_BINOP_FP(DivOp, div)
+BUILD_NUMERIC_BINOP_FP(MaxOp, max)
+BUILD_NUMERIC_BINOP_FP(MinOp, min)
+BUILD_NUMERIC_BINOP_INT(AndOp, and)
+BUILD_NUMERIC_BINOP_INT(DivSIOp, divS)
+BUILD_NUMERIC_BINOP_INT(DivUIOp, divU)
+BUILD_NUMERIC_BINOP_INT(OrOp, or)
+BUILD_NUMERIC_BINOP_INT(RemSIOp, remS)
+BUILD_NUMERIC_BINOP_INT(RemUIOp, remU)
+BUILD_NUMERIC_BINOP_INT(RotlOp, rotl)
+BUILD_NUMERIC_BINOP_INT(RotrOp, rotr)
+BUILD_NUMERIC_BINOP_INT(ShLOp, shl)
+BUILD_NUMERIC_BINOP_INT(ShRSOp, shrS)
+BUILD_NUMERIC_BINOP_INT(ShRUOp, shrU)
+BUILD_NUMERIC_BINOP_INT(XOrOp, xor)
+BUILD_NUMERIC_BINOP_INTFP(AddOp, add)
+BUILD_NUMERIC_BINOP_INTFP(MulOp, mul)
+BUILD_NUMERIC_BINOP_INTFP(SubOp, sub)
+BUILD_NUMERIC_UNARY_OP_FP(AbsOp, abs)
+BUILD_NUMERIC_UNARY_OP_FP(CeilOp, ceil)
+BUILD_NUMERIC_UNARY_OP_FP(FloorOp, floor)
+BUILD_NUMERIC_UNARY_OP_FP(NegOp, neg)
+BUILD_NUMERIC_UNARY_OP_FP(SqrtOp, sqrt)
+BUILD_NUMERIC_UNARY_OP_FP(TruncOp, trunc)
+BUILD_NUMERIC_UNARY_OP_INT(ClzOp, clz)
+BUILD_NUMERIC_UNARY_OP_INT(CtzOp, ctz)
+BUILD_NUMERIC_UNARY_OP_INT(PopCntOp, popcnt)
+
+// Don't need these anymore so let's undef them.
+#undef BUILD_NUMERIC_BINOP_FP
+#undef BUILD_NUMERIC_BINOP_INT
+#undef BUILD_NUMERIC_BINOP_INTFP
+#undef BUILD_NUMERIC_UNARY_OP_FP
+#undef BUILD_NUMERIC_UNARY_OP_INT
+#undef BUILD_NUMERIC_OP
+#undef BUILD_NUMERIC_CAST_OP
+
 class WasmBinaryParser {
 private:
   struct SectionRegistry {
@@ -906,7 +1134,7 @@ class WasmBinaryParser {
     if (failed(nElemsParsed))
       return failure();
     uint32_t nElems = *nElemsParsed;
-    LDBG() << "Starting to parse " << nElems << " items for section "
+    LDBG() << "starting to parse " << nElems << " items for section "
            << secName;
     for (size_t i = 0; i < nElems; ++i) {
       if (failed(parseSectionItem<section>(ph, i)))
@@ -1005,7 +1233,7 @@ class WasmBinaryParser {
       return;
     if (version->compare(expectedVersionString)) {
       emitError(versionLoc,
-                "unsupported Wasm version. Only version 1 is supported.");
+                "unsupported Wasm version. only version 1 is supported");
       return;
     }
     LogicalResult fillRegistry = registry.populateFromBody(parser.copy());
@@ -1036,6 +1264,14 @@ class WasmBinaryParser {
     if (failed(parsingMems))
       return;
 
+    LogicalResult parsingGlobals = parseSection<WasmSectionType::GLOBAL>();
+    if (failed(parsingGlobals))
+      return;
+
+    LogicalResult parsingCode = parseSection<WasmSectionType::CODE>();
+    if (failed(parsingCode))
+      return;
+
     LogicalResult parsingExports = parseSection<WasmSectionType::EXPORT>();
     if (failed(parsingExports))
       return;
@@ -1229,6 +1465,51 @@ WasmBinaryParser::parseSectionItem<WasmSectionType::MEMORY>(ParserHead &ph,
   symbols.memSymbols.push_back({SymbolRefAttr::get(memOp)});
   return success();
 }
+
+template <>
+LogicalResult
+WasmBinaryParser::parseSectionItem<WasmSectionType::GLOBAL>(ParserHead &ph,
+                                                            size_t) {
+  FileLineColLoc globalLocation = ph.getLocation();
+  auto globalTypeParsed = ph.parseGlobalType(ctx);
+  if (failed(globalTypeParsed))
+    retur...
[truncated]

@lforg37
Copy link
Contributor Author

lforg37 commented Aug 18, 2025

@joker-eph could you review this?

@MattPD
Copy link
Member

MattPD commented Aug 18, 2025

Is it possible that the commit message has been inadvertently truncated? (It seems to abruptly terminate after variable related instruction,)

@joker-eph joker-eph merged commit df57bb8 into llvm:main Aug 19, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Aug 19, 2025

LLVM Buildbot has detected a new failure on builder mlir-s390x-linux running on systemz-1 while building mlir at step 6 "test-build-unified-tree-check-mlir".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/117/builds/12664

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-mlir) failure: test (failure)
******************** TEST 'MLIR :: Target/Wasm/abs.mlir' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
yaml2obj /home/uweigand/sandbox/buildbot/mlir-s390x-linux/llvm-project/mlir/test/Target/Wasm/inputs/abs.yaml.wasm -o - | /home/uweigand/sandbox/buildbot/mlir-s390x-linux/build/bin/mlir-translate --import-wasm | /home/uweigand/sandbox/buildbot/mlir-s390x-linux/build/bin/FileCheck /home/uweigand/sandbox/buildbot/mlir-s390x-linux/llvm-project/mlir/test/Target/Wasm/abs.mlir
# executed command: yaml2obj /home/uweigand/sandbox/buildbot/mlir-s390x-linux/llvm-project/mlir/test/Target/Wasm/inputs/abs.yaml.wasm -o -
# executed command: /home/uweigand/sandbox/buildbot/mlir-s390x-linux/build/bin/mlir-translate --import-wasm
# .---command stderr------------
# | <stdin>:0:8: remark: Adding section with section ID 1
# | <stdin>:0:23: remark: Adding section with section ID 3
# | <stdin>:0:32: remark: Adding section with section ID 7
# | <stdin>:0:59: remark: Adding section with section ID 10
# `-----------------------------
# executed command: /home/uweigand/sandbox/buildbot/mlir-s390x-linux/build/bin/FileCheck /home/uweigand/sandbox/buildbot/mlir-s390x-linux/llvm-project/mlir/test/Target/Wasm/abs.mlir
# .---command stderr------------
# | /home/uweigand/sandbox/buildbot/mlir-s390x-linux/llvm-project/mlir/test/Target/Wasm/abs.mlir:16:11: error: CHECK: expected string not found in input
# | // CHECK: %[[VAL_0:.*]] = wasmssa.const 1.000000e+01 : f32
# |           ^
# | <stdin>:2:34: note: scanning from here
# |  wasmssa.func @abs_f32() -> f32 {
# |                                  ^
# | <stdin>:3:2: note: possible intended match here
# |  %0 = wasmssa.const 1.157050e-41 : f32
# |  ^
# | /home/uweigand/sandbox/buildbot/mlir-s390x-linux/llvm-project/mlir/test/Target/Wasm/abs.mlir:21:11: error: CHECK: expected string not found in input
# | // CHECK: %[[VAL_0:.*]] = wasmssa.const 1.000000e+01 : f64
# |           ^
# | <stdin>:7:34: note: scanning from here
# |  wasmssa.func @abs_f64() -> f64 {
# |                                  ^
# | <stdin>:8:2: note: possible intended match here
# |  %0 = wasmssa.const 4.584930e-320 : f64
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/uweigand/sandbox/buildbot/mlir-s390x-linux/llvm-project/mlir/test/Target/Wasm/abs.mlir
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             1: module { 
# |             2:  wasmssa.func @abs_f32() -> f32 { 
# | check:16'0                                      X error: no match found
# |             3:  %0 = wasmssa.const 1.157050e-41 : f32 
# | check:16'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# | check:16'1      ?                                      possible intended match
# |             4:  %1 = wasmssa.abs %0 : f32 
...

joker-eph added a commit that referenced this pull request Aug 19, 2025
…orter" (#154314)

Reverts #154053

Seems like an endianness sensitivity failing a big-endian bot.
@joker-eph
Copy link
Collaborator

@lforg37 : I suspect an endianness issue here, the failing bot is big-endian.

llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Aug 19, 2025
…dialect importer" (#154314)

Reverts llvm/llvm-project#154053

Seems like an endianness sensitivity failing a big-endian bot.
lforg37 added a commit to lforg37/llvm-project that referenced this pull request Aug 20, 2025
…lvm#154053)

This is the continuation of  llvm#152131 

This PR adds support for parsing the global initializer and function
body, and support for decoding scalar numerical instructions and
variable related instructions.

---------

Co-authored-by: Ferdinand Lemaire <ferdinand.lemaire@woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette@woven-planet.global>
Co-authored-by: Luc Forget <luc.forget@woven.toyota>
@lforg37
Copy link
Contributor Author

lforg37 commented Aug 20, 2025

@joker-eph : I think I fixed the bug source on #154452, but have no big-endian machine to check it.

@lforg37 lforg37 deleted the lforget.upstream_wasm_mlir_translate branch August 20, 2025 02:14
joker-eph pushed a commit that referenced this pull request Aug 20, 2025
This is a cherry pick of #154053 with a fix for bad handling of
endianess when loading float and double litteral from the binary.

---------

Co-authored-by: Ferdinand Lemaire <ferdinand.lemaire@woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette@woven-planet.global>
Co-authored-by: Luc Forget <luc.forget@woven.toyota>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants