[Mlir-commits] [mlir] a534896 - [MLIR][Wasm] Introduce the WasmSSA MLIR dialect (#149233)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Tue Aug 5 01:23:13 PDT 2025


Author: Ferdinand Lemaire
Date: 2025-08-05T10:23:09+02:00
New Revision: a53489606a4e704e5a7ac148e950eabfe21397cd

URL: https://github.com/llvm/llvm-project/commit/a53489606a4e704e5a7ac148e950eabfe21397cd
DIFF: https://github.com/llvm/llvm-project/commit/a53489606a4e704e5a7ac148e950eabfe21397cd.diff

LOG: [MLIR][Wasm] Introduce the WasmSSA MLIR  dialect (#149233)

Introduce the WasmSSA dialect as discussed in
https://discourse.llvm.org/t/rfc-mlir-dialect-for-webassembly/86758 and
during the ODM
https://discourse.llvm.org/t/mlir-open-meeting-webassembly-dialect/86928

This PR only introduces the dialect definition and interfaces, the list
of operators and some operators-related helper functions (related to
parsing or verification) and some tests for those.
Follow-up PRs will bring the binary Webassembly importer and the
lowerings to other dialects along with testing and a driver for
conversion of Webassembly binaries to LLVM IR.

Co-authored-by: Luc Forget <dev at alias.lforget.fr>
Co-authored-by: Ferdinand Lemaire <ferdinand.lemaire at woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette at woven-planet.global>
Co-authored-by: Luc Forget <luc.forget at woven.toyota>

Added: 
    mlir/include/mlir/Dialect/WasmSSA/CMakeLists.txt
    mlir/include/mlir/Dialect/WasmSSA/IR/CMakeLists.txt
    mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSA.h
    mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSABase.td
    mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.h
    mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.td
    mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
    mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSATypes.td
    mlir/lib/Dialect/WasmSSA/CMakeLists.txt
    mlir/lib/Dialect/WasmSSA/IR/CMakeLists.txt
    mlir/lib/Dialect/WasmSSA/IR/WasmSSADialect.cpp
    mlir/lib/Dialect/WasmSSA/IR/WasmSSAInterfaces.cpp
    mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
    mlir/lib/Dialect/WasmSSA/IR/WasmSSATypes.cpp
    mlir/test/Dialect/WasmSSA/custom_parser/global.mlir
    mlir/test/Dialect/WasmSSA/custom_parser/import.mlir
    mlir/test/Dialect/WasmSSA/custom_parser/local.mlir
    mlir/test/Dialect/WasmSSA/extend-invalid.mlir
    mlir/test/Dialect/WasmSSA/global-invalid.mlir
    mlir/test/Dialect/WasmSSA/locals-invalid.mlir
    mlir/test/Dialect/WasmSSA/reinterpret-invalid.mlir

Modified: 
    mlir/include/mlir/Dialect/CMakeLists.txt
    mlir/lib/Dialect/CMakeLists.txt
    mlir/lib/RegisterAllDialects.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/CMakeLists.txt b/mlir/include/mlir/Dialect/CMakeLists.txt
index e27b1679c2a52..e870d5ad23376 100644
--- a/mlir/include/mlir/Dialect/CMakeLists.txt
+++ b/mlir/include/mlir/Dialect/CMakeLists.txt
@@ -41,5 +41,6 @@ add_subdirectory(Transform)
 add_subdirectory(UB)
 add_subdirectory(Utils)
 add_subdirectory(Vector)
+add_subdirectory(WasmSSA)
 add_subdirectory(X86Vector)
 add_subdirectory(XeGPU)

diff  --git a/mlir/include/mlir/Dialect/WasmSSA/CMakeLists.txt b/mlir/include/mlir/Dialect/WasmSSA/CMakeLists.txt
new file mode 100644
index 0000000000000..f33061b2d87cf
--- /dev/null
+++ b/mlir/include/mlir/Dialect/WasmSSA/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(IR)

diff  --git a/mlir/include/mlir/Dialect/WasmSSA/IR/CMakeLists.txt b/mlir/include/mlir/Dialect/WasmSSA/IR/CMakeLists.txt
new file mode 100644
index 0000000000000..28823947230f6
--- /dev/null
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_TARGET_DEFINITIONS WasmSSATypes.td)
+mlir_tablegen(WasmSSATypeConstraints.h.inc -gen-type-constraint-decls)
+mlir_tablegen(WasmSSATypeConstraints.cpp.inc -gen-type-constraint-defs)
+
+set (LLVM_TARGET_DEFINITIONS WasmSSAInterfaces.td)
+mlir_tablegen(WasmSSAInterfaces.h.inc -gen-op-interface-decls)
+mlir_tablegen(WasmSSAInterfaces.cpp.inc -gen-op-interface-defs)
+add_public_tablegen_target(MLIRWasmSSAInterfacesIncGen)
+
+set(LLVM_TARGET_DEFINITIONS WasmSSAOps.td)
+
+add_mlir_dialect(WasmSSAOps wasmssa)
+add_mlir_doc(WasmSSAOps WasmSSAOps Dialects/ -gen-dialect-doc)

diff  --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSA.h b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSA.h
new file mode 100644
index 0000000000000..64391d807c633
--- /dev/null
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSA.h
@@ -0,0 +1,55 @@
+//===- WasmSSA.h - WasmSSA dialect ------------------*- C++-*-==//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_WasmSSA_IR_WasmSSA_H_
+#define MLIR_DIALECT_WasmSSA_IR_WasmSSA_H_
+
+#include "mlir/Bytecode/BytecodeOpInterface.h"
+#include "mlir/IR/Dialect.h"
+
+//===----------------------------------------------------------------------===//
+// WebAssemblyDialect
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAOpsDialect.h.inc"
+
+//===----------------------------------------------------------------------===//
+// WebAssembly Dialect Types
+//===----------------------------------------------------------------------===//
+
+#define GET_TYPEDEF_CLASSES
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAOpsTypes.h.inc"
+
+//===----------------------------------------------------------------------===//
+// WebAssembly Interfaces
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.h"
+
+//===----------------------------------------------------------------------===//
+// WebAssembly Dialect Operations
+//===----------------------------------------------------------------------===//
+#include "mlir/IR/SymbolTable.h"
+#include "mlir/Interfaces/CallInterfaces.h"
+#include "mlir/Interfaces/FunctionInterfaces.h"
+#include "mlir/Interfaces/InferTypeOpInterface.h"
+
+//===----------------------------------------------------------------------===//
+// WebAssembly Constraints
+//===----------------------------------------------------------------------===//
+
+namespace mlir {
+namespace wasmssa {
+#include "mlir/Dialect/WasmSSA/IR/WasmSSATypeConstraints.h.inc"
+}
+} // namespace mlir
+
+#define GET_OP_CLASSES
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAOps.h.inc"
+
+#endif // MLIR_DIALECT_WasmSSA_IR_WasmSSA_H_

diff  --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSABase.td b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSABase.td
new file mode 100644
index 0000000000000..f2777a7b155ed
--- /dev/null
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSABase.td
@@ -0,0 +1,25 @@
+//===- WasmSSABase.td - Base defs for wasmssa dialect -*- tablegen -*-==//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef WasmSSA_BASE
+#define WasmSSA_BASE
+
+include "mlir/IR/EnumAttr.td"
+include "mlir/IR/OpBase.td"
+
+def WasmSSA_Dialect : Dialect {
+  let name = "wasmssa";
+  let cppNamespace = "::mlir::wasmssa";
+  let description = [{
+    The `wasmssa` dialect is intended to represent WebAssembly
+    modules in SSA form for easier manipulation.
+  }];
+  let useDefaultTypePrinterParser = true;
+}
+
+#endif //WasmSSA_BASE

diff  --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.h b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.h
new file mode 100644
index 0000000000000..d8948317477d0
--- /dev/null
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.h
@@ -0,0 +1,62 @@
+//===- WasmSSAInterfaces.h - WasmSSA Interfaces ---*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines op interfaces for the WasmSSA dialect in MLIR.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_WasmSSA_IR_WasmSSAINTERFACES_H_
+#define MLIR_DIALECT_WasmSSA_IR_WasmSSAINTERFACES_H_
+
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/OpDefinition.h"
+
+namespace mlir::wasmssa {
+namespace detail {
+/// Verify that `op` conforms to the ConstantExpressionInterface.
+/// `op` must be initialized with valid constant expressions.
+LogicalResult verifyConstantExpressionInterface(Operation *op);
+
+/// Verify that `op` conforms to the LabelBranchingOpInterface
+/// Checks that the branching is targetting something within its scope.
+LogicalResult verifyLabelBranchingOpInterface(Operation *op);
+
+/// Verify that `op` conforms to LabelLevelInterfaceIsTerminator
+template <typename OpType>
+LogicalResult verifyLabelLevelInterfaceIsTerminator() {
+  static_assert(OpType::template hasTrait<::mlir::OpTrait::IsTerminator>(),
+                "LabelLevelOp should be terminator ops");
+  return success();
+}
+
+/// Verify that `op` conforms to the LabelLevelInterface
+/// `op`'s target should defined at the same scope level.
+LogicalResult verifyLabelLevelInterface(Operation *op);
+} // namespace detail
+
+/// Operations implementing this trait are considered as valid
+/// constant expressions in any context (In contrast of
+/// ConstantExprCheckOpInterface which are sometimes considered valid constant
+/// expressions.
+template <class OperationType>
+struct ConstantExprOpTrait
+    : public OpTrait::TraitBase<OperationType, ConstantExprOpTrait> {};
+
+/// Trait used to verify operations that need a constant expression initializer.
+template <typename OpType>
+struct ConstantExpressionInitializerOpTrait
+    : public OpTrait::TraitBase<OpType, ConstantExpressionInitializerOpTrait> {
+  static LogicalResult verifyTrait(Operation *op) {
+    return detail::verifyConstantExpressionInterface(op);
+  }
+};
+
+} // namespace mlir::wasmssa
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.h.inc"
+
+#endif // MLIR_DIALECT_WasmSSA_IR_WasmSSAINTERFACES_H_

diff  --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.td b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.td
new file mode 100644
index 0000000000000..db4cf2d6f72b6
--- /dev/null
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.td
@@ -0,0 +1,138 @@
+//===-- WasmSSAInterfaces.td - WasmSSA Interfaces -*- tablegen -*--===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines interfaces for the WasmSSA dialect in MLIR.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef WasmSSA_INTERFACES
+#define WasmSSA_INTERFACES
+
+include "mlir/IR/OpBase.td"
+include "mlir/IR/BuiltinAttributes.td"
+
+def LabelLevelOpInterface : OpInterface<"LabelLevelOpInterface"> {
+  let cppNamespace = "::mlir::wasmssa";
+  let description = [{
+    Operation that defines one level of nesting for Wasm branching.
+
+    These ops defines Wasm control flow nesting levels (Wasm Labels) that Wasm
+    branching operations can target.
+    The branching operations specify a number of nesting level they want to exit,
+    and are redirected to the target of the corresponding nesting LabelLevelOp.
+
+    As multiple level can be escaped at once, the level defining ops need themselves
+    to be `Terminator` ops.
+  }];
+  let methods = [
+    InterfaceMethod<
+      /*desc=*/        "Returns the target block address",
+      /*returnType=*/  "::mlir::Block*",
+      /*methodName=*/  "getLabelTarget",
+      /*args=*/        (ins)
+    >
+  ];
+
+  let verify = [{
+    return success(
+      succeeded(verifyLabelLevelInterfaceIsTerminator<ConcreteOp>()) &&
+      succeeded(verifyLabelLevelInterface($_op)));
+  }];
+}
+
+def LabelBranchingOpInterface : OpInterface<"LabelBranchingOpInterface"> {
+  let cppNamespace = "::mlir::wasmssa";
+  let description = [{
+    Wasm operation that targets a label for a jump.
+  }];
+  let methods = [
+    InterfaceMethod<
+      /*desc=*/        "Returns the number of context to break from",
+      /*returnType=*/  "size_t",
+      /*methodName=*/  "getExitLevel",
+      /*args=*/        (ins)
+    >,
+    InterfaceMethod<
+      /*desc=*/        "Returns the destination of this operation",
+      /*returnType=*/  "LabelLevelOpInterface",
+      /*methodName=*/  "getTargetOp",
+      /*args=*/        (ins),
+      /*methodBody=*/ [{
+        return *LabelBranchingOpInterface::getTargetOpFromBlock($_op.getOperation()->getBlock(), $_op.getExitLevel());
+      }]
+    >,
+    InterfaceMethod<
+      /*desc=*/        "Return the target control flow ops that defined the label of this operation",
+      /*returnType=*/  "::mlir::Block*",
+      /*methodName=*/  "getTarget",
+      /*args=*/        (ins),
+      /*methodBody=*/  [{}],
+      /*defaultImpl=*/ [{
+        auto op = mlir::cast<LabelBranchingOpInterface>(this->getOperation());
+        return op.getTargetOp().getLabelTarget();
+      }]
+    >
+  ];
+
+  let extraClassDeclaration = [{
+    static ::llvm::FailureOr<LabelLevelOpInterface> getTargetOpFromBlock(::mlir::Block *block, uint32_t level);
+  }];
+  let verify = [{return verifyLabelBranchingOpInterface($_op);}];
+}
+
+def ImportOpInterface : OpInterface<"ImportOpInterface"> {
+  let cppNamespace = "::mlir::wasmssa";
+  let description = [{
+    Operation that imports a symbol from an external Wasm module;
+  }];
+
+  let methods = [
+    InterfaceMethod<
+      /*desc=*/        "Returns the module name for the import",
+      /*returnType=*/  "::llvm::StringRef",
+      /*methodName=*/  "getModuleName",
+      /*args=*/        (ins)
+      >,
+    InterfaceMethod<
+      /*desc=*/        "Returns the import name for the import",
+      /*returnType=*/  "::llvm::StringRef",
+      /*methodName=*/  "getImportName",
+      /*args=*/        (ins)
+      >,
+    InterfaceMethod<
+      /*desc=*/        "Returns the Wasm index based symbol of the op",
+      /*returnType=*/  "::mlir::StringAttr",
+      /*methodName=*/  "getSymbolName",
+      /*args=*/        (ins),
+      /*methodBody=*/  [{}],
+      /*defaultImpl=*/ [{
+        auto op = mlir::cast<ConcreteOp>(this->getOperation());
+        return op.getSymNameAttr();
+      }]
+      >,
+    InterfaceMethod<
+      /*desc=*/        "Returns the qualified name of the import",
+      /*returnType=*/  "std::string",
+      /*methodName=*/  "getQualifiedImportName",
+      /*args=*/        (ins),
+      /*methodBody=*/  [{
+        return ($_op.getModuleName() + ::llvm::Twine{"::"} + $_op.getImportName()).str();
+      }]
+      >,
+  ];
+}
+
+def ConstantExpressionInitializerOpTrait : NativeOpTrait<"ConstantExpressionInitializerOpTrait"> {
+  let cppNamespace = "::mlir::wasmssa";
+}
+
+def ConstantExprOpTrait : NativeOpTrait<"ConstantExprOpTrait"> {
+  let cppNamespace = "::mlir::wasmssa";
+}
+
+#endif // WEBASSEMBLY_INTERFACES

diff  --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
new file mode 100644
index 0000000000000..676621b176f5c
--- /dev/null
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
@@ -0,0 +1,1174 @@
+//===- WasmSSAOps.td - WasmSSA op definitions -*- tablegen -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef WasmSSA_OPS
+#define WasmSSA_OPS
+
+
+include "mlir/Dialect/WasmSSA/IR/WasmSSABase.td"
+include "mlir/Dialect/WasmSSA/IR/WasmSSATypes.td"
+include "mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.td"
+
+include "mlir/Interfaces/FunctionInterfaces.td"
+include "mlir/Interfaces/InferTypeOpInterface.td"
+include "mlir/IR/BuiltinAttributeInterfaces.td"
+include "mlir/IR/SymbolInterfaces.td"
+
+// Base class for WasmSSA operations.
+// Most operations are made to match 1:1, only ignoring the stack-based approach of Wasm
+// for an SSA based approach. In cases where operations match 1:1 the Wasm spec,
+// no description is provided.
+class WasmSSA_Op<string mnemonic, list<Trait> traits = []> :
+    Op<WasmSSA_Dialect, mnemonic, traits>;
+
+class WasmSSA_BlockLikeOp<string mnemonic, string summaryStr> :
+  WasmSSA_Op<mnemonic, [Terminator, DeclareOpInterfaceMethods<LabelLevelOpInterface>]> {
+  let summary = summaryStr;
+  let arguments = (ins Variadic<WasmSSA_ValType>: $inputs);
+  let regions = (region AnyRegion: $body);
+  let successors = (successor AnySuccessor: $target);
+  let extraClassDeclaration = [{
+    ::mlir::Block* createBlock() {
+      auto &block = getBody().emplaceBlock();
+      for (auto input : getInputs())
+        block.addArgument(input.getType(), input.getLoc());
+      return █
+    }
+  }];
+  let assemblyFormat = "(`(`$inputs^`)` `:` type($inputs))? attr-dict  `:` $body `>` $target";
+}
+
+def WasmSSA_BlockOp : WasmSSA_BlockLikeOp<"block", "Create a nesting level"> {}
+
+def WasmSSA_LoopOp : WasmSSA_BlockLikeOp<"loop", "Create a nesting level similar to Block Op, except that it has itself as a successor."> {}
+
+def WasmSSA_BlockReturnOp : WasmSSA_Op<"block_return", [Terminator,
+    DeclareOpInterfaceMethods<LabelBranchingOpInterface>]> {
+  let summary = "Return from the current block";
+  let arguments = (ins Variadic<WasmSSA_ValType>: $inputs);
+  let extraClassDeclaration = [{
+    ::mlir::Block* getTarget();
+  }];
+  let description = [{
+     Marks a return from the current block.
+
+     Example:
+
+    ```mlir
+      wasmssa.block_return
+    ```
+  }];
+  let assemblyFormat = "($inputs^ `:` type($inputs))? attr-dict";
+}
+
+def WasmSSA_BranchIfOp : WasmSSA_Op<"branch_if", [
+    Terminator,
+    DeclareOpInterfaceMethods<LabelBranchingOpInterface>]> {
+  let summary = "Jump to target level if condition has non-zero value";
+  let arguments = (ins I32: $condition,
+                       UI32Attr: $exitLevel,
+                       Variadic<WasmSSA_ValType>: $inputs);
+  let description = [{
+     Jump to target level if the condition is has a non-zero value.
+
+     Example:
+
+     ```mlir
+     wasmssa.branch_if %a to level 0 with args(%b : i32) else ^bb1
+     ```
+    }];
+  let successors = (successor AnySuccessor: $elseSuccessor);
+  let assemblyFormat = "$condition `to` `level` $exitLevel (`with` `args`  `(`$inputs^ `:` type($inputs)`)`)?  `else` $elseSuccessor  attr-dict";
+}
+
+def WasmSSA_ConstOp : WasmSSA_Op<"const", [
+    AllTypesMatch<["value", "result"]>,
+    ConstantExprOpTrait]> {
+  let summary = "Operator that represents a constant value";
+  let description = [{
+     Defines a constant value.
+
+     Example:
+
+     ```mlir
+     // Example of integer constant
+     %a = wasmssa.const 1 : i32
+
+     // Example of floating point constant
+     %b = wasmssa.const 9.000000e+00 : f64
+     ```
+    }];
+  let arguments = (ins TypedAttrInterface: $value);
+  let results = (outs WasmSSA_NumericType: $result);
+  let assemblyFormat = "$value attr-dict";
+}
+
+def WasmSSA_FuncOp : WasmSSA_Op<"func", [
+    AffineScope, AutomaticAllocationScope,
+    DeclareOpInterfaceMethods<FunctionOpInterface, ["verifyBody"]>,
+    IsolatedFromAbove,
+    Symbol]> {
+  let description = [{
+    Represents a Wasm function definition.
+
+    In Wasm function, locals and function arguments are interchangeable.
+    They are for instance both accessed using `local.get` instruction.
+
+    On the other hand, a function type is defined as a pair of tuples of Wasm value types.
+    To model this, the wasm.func operation has:
+
+    - A function type that represents the corresponding Wasm type (tuples of value types)
+
+    - Arguments of the entry block of type `!wasm<local T>`, with T the corresponding type
+     in the function type.
+
+     Example:
+
+     ```mlir
+     // A simple function with no arguments that returns a float32
+     wasmssa.func @my_f32_func() -> f32
+
+     // A function that takes a local ref argument
+     wasmssa.func @i64_wrap(%a: !wasmssa<local ref to i64>) -> i32
+     ```
+  }];
+  let arguments = (ins SymbolNameAttr: $sym_name,
+                     WasmSSA_FuncTypeAttr: $functionType,
+                     OptionalAttr<DictArrayAttr>:$arg_attrs,
+                     OptionalAttr<DictArrayAttr>:$res_attrs,
+                     DefaultValuedAttr<StrAttr, "\"nested\"">:$sym_visibility);
+  let regions = (region AnyRegion: $body);
+  let extraClassDeclaration = [{
+
+    /// Create the entry block for the function with parameters wrapped in local ref.
+    ::mlir::Block* addEntryBlock();
+
+    //===------------------------------------------------------------------===//
+    // FunctionOpInterface Methods
+    //===------------------------------------------------------------------===//
+
+    /// Returns the region on the current operation that is callable. This may
+    /// return null in the case of an external callable object, e.g. an external
+    /// function.
+    ::mlir::Region *getCallableRegion() { return isExternal() ? nullptr : &getBody(); }
+
+    /// Returns the argument types of this function.
+    ArrayRef<Type> getArgumentTypes() { return getFunctionType().getInputs(); }
+
+    /// Returns the result types of this function.
+    ArrayRef<Type> getResultTypes() { return getFunctionType().getResults(); }
+  }];
+
+  let builders = [
+    OpBuilder<(ins "::llvm::StringRef":$symbol, "FunctionType":$funcType )>
+  ];
+  let hasCustomAssemblyFormat = 1;
+}
+
+def WasmSSA_FuncCallOp : WasmSSA_Op<"call"> {
+  let summary = "Calling a Wasm function";
+  let description = [{
+     Emits a call to a defined function
+
+     Example:
+
+     ```mlir
+     %a = wasmssa.call @func_0 : () -> i32
+     ```
+    }];
+  let arguments = (ins FlatSymbolRefAttr: $callee,
+                       Variadic<WasmSSA_ValType>:  $operands);
+  let results = (outs Variadic<WasmSSA_ValType>: $results);
+  let assemblyFormat = "$callee (`(`$operands^`)`)? attr-dict `:` functional-type($operands, $results)";
+}
+
+def WasmSSA_FuncImportOp : WasmSSA_Op<"import_func", [
+    Symbol,
+    CallableOpInterface,
+    ImportOpInterface]> {
+  let summary = "Importing a function variable";
+  let description = [{
+     Imports a function from another module
+
+     Example:
+
+     ```mlir
+     // Imports foo(i32) -> () from the module my_module
+     wasmssa.import_func "foo" from "my_module" as @func_0 {sym_visibility = "nested", type = (i32) -> ()}
+     ```
+    }];
+  let arguments = (ins SymbolNameAttr: $sym_name,
+                     StrAttr: $moduleName,
+                     StrAttr: $importName,
+                     WasmSSA_FuncTypeAttr: $type,
+                     OptionalAttr<DictArrayAttr>:$arg_attrs,
+                     OptionalAttr<DictArrayAttr>:$res_attrs,
+                     OptionalAttr<StrAttr>:$sym_visibility);
+  let extraClassDeclaration = [{
+    bool isDeclaration() const { return true; }
+
+    Region *getCallableRegion() { return nullptr; }
+
+    ::llvm::ArrayRef<Type> getArgumentTypes() {
+      return getType().getInputs();
+    }
+
+    ::llvm::ArrayRef<Type> getResultTypes() {
+      return getType().getResults();
+    }
+  }];
+  let builders = [
+    OpBuilder<(ins "StringRef":$symbol,
+                   "StringRef":$moduleName,
+                   "StringRef":$importName,
+                   "FunctionType": $type)>
+  ];
+  let assemblyFormat = "$importName `from` $moduleName `as` $sym_name attr-dict";
+}
+
+def WasmSSA_GlobalOp : WasmSSA_Op<"global", [
+  AffineScope, AutomaticAllocationScope,
+  IsolatedFromAbove, Symbol, ConstantExpressionInitializerOpTrait]> {
+  let summary= "WebAssembly global value";
+  let arguments = (ins SymbolNameAttr: $sym_name,
+                     WasmSSA_ValTypeAttr: $type,
+                     UnitAttr: $isMutable,
+                     OptionalAttr<StrAttr>:$sym_visibility);
+  let description = [{
+    WebAssembly global variable.
+    Body contains the initialization instructions for the variable value.
+    The body must contain only instructions considered `const` in a webassembly context,
+    such as `wasmssa.const` or `global.get`.
+
+    Example:
+
+    ```mlir
+    // Define a global_var, a mutable i32 global variable equal to 10.
+    wasmssa.global @global_var i32 mutable nested : {
+          %[[VAL_0:.*]] = wasmssa.const 10 : i32
+          wasmssa.return %[[VAL_0]] : i32
+    }
+    ```
+  }];
+  let regions = (region AnyRegion: $initializer);
+
+  let builders = [
+    OpBuilder<(ins "StringRef":$symbol,
+                   "Type": $type,
+                   "bool": $isMutable)>
+  ];
+  let hasCustomAssemblyFormat = 1;
+}
+
+def WasmSSA_GlobalImportOp : WasmSSA_Op<"import_global", [
+    Symbol,
+    ImportOpInterface]> {
+  let summary = "Importing a global variable";
+  let description = [{
+     Imports a global from another module
+
+     Example:
+
+     ```mlir
+     // Imports the "glob" i32 global from the module my_module as "global_0"
+     wasmssa.import_global "glob" from "my_module" as @global_0 nested : i32
+     ```
+    }];
+  let arguments = (ins SymbolNameAttr: $sym_name,
+                     StrAttr: $moduleName,
+                     StrAttr: $importName,
+                     WasmSSA_ValTypeAttr: $type,
+                     UnitAttr: $isMutable,
+                     OptionalAttr<StrAttr>:$sym_visibility);
+  let extraClassDeclaration = [{
+    bool isDeclaration() const { return true; }
+  }];
+  let builders = [
+    OpBuilder<(ins "StringRef":$symbol,
+                   "StringRef":$moduleName,
+                   "StringRef":$importName,
+                   "Type": $type,
+                   "bool": $isMutable)>
+  ];
+  let hasCustomAssemblyFormat = 1;
+}
+
+def WasmSSA_GlobalGetOp : WasmSSA_Op<"global_get", [DeclareOpInterfaceMethods<SymbolUserOpInterface>,
+  ConstantExprOpTrait]> {
+  let summary = "Returns the value of the global passed as argument.";
+  let description = [{
+     Retrieves the value of the global passed as argument and stores it in a
+     variable
+
+     Example:
+
+     ```mlir
+     // Gets the value of `@global_0` and stores its value in %a
+     %a = wasmssa.global_get @global_0 : i32
+     ```
+    }];
+  let arguments = (ins FlatSymbolRefAttr: $global);
+  let results = (outs WasmSSA_ValType: $global_val);
+  let assemblyFormat = "$global attr-dict `:` type($global_val)";
+}
+
+def WasmSSA_IfOp : WasmSSA_Op<"if", [Terminator,
+    DeclareOpInterfaceMethods<LabelLevelOpInterface>]> {
+  let summary = "Execute the if region if condition value is non-zero, the else region otherwise.";
+  let description = [{
+    Execute the if region if the condition is non-zero. Otherwise the else region is executed.
+    The else region can be empty but must return the same datatype as the if region.
+    If clauses can be nested.
+
+     Example:
+
+     ```mlir
+     // Runs the if clause is %a is non-zero
+     "wasmssa.if"(%a)[^bb1] ({
+        // Execute if %a is non-zero
+     },{
+        // else clause
+     }) : (i32) -> ()
+     ```
+    }];
+  let arguments = (ins I32:$condition, Variadic<WasmSSA_ValType>: $inputs);
+  let regions = (region AnyRegion: $if, AnyRegion: $else);
+  let successors = (successor AnySuccessor: $target);
+  let extraClassDeclaration = [{
+    private:
+    inline ::mlir::Block* createBlock(::mlir::Region& region) {
+      assert(region.empty() && "Creating entry block on non empty region");
+      assert(region.getParentOp() == this->getOperation() &&
+        "Creating block for region that isn't part of the current op");
+      auto &block = region.emplaceBlock();
+      for (auto input : getInputs())
+        block.addArgument(input.getType(), input.getLoc());
+      return █
+    }
+
+    public:
+    ::mlir::Block* createIfBlock() {
+      return createBlock(getIf());
+    }
+    ::mlir::Block* createElseBlock() {
+      return createBlock(getElse());
+    }
+  }];
+}
+
+def WasmSSA_LocalOp : WasmSSA_Op<"local", [
+    DeclareOpInterfaceMethods<InferTypeOpInterface>]> {
+  let summary = "Declaration of local variable";
+  let description = [{
+    Declares a local variable
+
+     Example:
+
+     ```mlir
+     // Declares `%a`, a float32 local
+     %a = wasmssa.local of type f32
+     ```
+    }];
+  let arguments = (ins WasmSSA_ValTypeAttr: $type);
+  let results = (outs WasmSSA_LocalRef: $result);
+  let assemblyFormat = "`of` `type` $type attr-dict";
+}
+
+def WasmSSA_LocalGetOp : WasmSSA_Op<"local_get", [
+    DeclareOpInterfaceMethods<InferTypeOpInterface>]> {
+  let summary = "Set local to value and return the operand.";
+  let description = [{
+    Gets the value of a local variable and returns a reference to it.
+
+     Example:
+
+     ```mlir
+     // Retrieves a reference to `%a`, a float32 local
+     %b = wasmssa.local_get %a : ref to f32
+     ```
+    }];
+  let arguments = (ins WasmSSA_LocalRef: $localVar);
+  let results = (outs WasmSSA_ValType: $result);
+  let assemblyFormat = "$localVar `:` type($localVar) attr-dict";
+}
+
+def WasmSSA_LocalSetOp : WasmSSA_Op<"local_set"> {
+  let summary = "Set local to given value";
+  let description = [{
+    Sets the value of a local variable.
+
+     Example:
+
+     ```mlir
+     // Sets `%d`, to the value of `%c`
+     wasmssa.local_set %d :  ref to i32 to %c : i32
+     ```
+    }];
+  let arguments = (ins WasmSSA_LocalRef: $localVar,
+                       WasmSSA_ValType: $value);
+  let hasVerifier = 1;
+  let assemblyFormat = "$localVar `:` type($localVar) `to` $value `:` type($value) attr-dict";
+}
+
+def WasmSSA_LocalTeeOp : WasmSSA_Op<"local_tee", [
+    DeclareOpInterfaceMethods<InferTypeOpInterface>]> {
+  let summary = "Set local to value and return the operand.";
+  let description = [{
+    Sets the value of a local variable and returns it.
+
+     Example:
+
+     ```mlir
+     // Sets `%b`, to the value of `%c` and returns it in %a
+      %a = wasmssa.local_tee %b :  ref to i32 to %c : i32
+     ```
+    }];
+  let arguments = (ins WasmSSA_LocalRef: $localVar,
+                       WasmSSA_ValType: $value);
+  let results = (outs WasmSSA_ValType: $result);
+  let hasVerifier = 1;
+  let assemblyFormat = "$localVar `:` type($localVar) `to` $value `:` type($value) attr-dict";
+}
+
+def WasmSSA_MemOp : WasmSSA_Op<"memory", [Symbol]> {
+  let summary= "WebAssembly memory definition";
+  let description = [{
+    Define a memory to be used by the program.
+    Multiple memories can be defined in the same module.
+
+     Example:
+
+     ```mlir
+     // Define the `mem_0` memory with defined bounds of 0 -> 65536
+     "wasmssa.memory"() <{limits = !wasmssa<limit[0:65536]>, sym_name = "mem_0"}> : () -> ()
+     ```
+    }];
+  let arguments = (ins SymbolNameAttr: $sym_name,
+                     WasmSSA_LimitTypeAttr: $limits,
+                     OptionalAttr<StrAttr>:$sym_visibility);
+  let builders = [
+    OpBuilder<(ins
+    "::llvm::StringRef":$symbol,
+    "wasmssa::LimitType":$limit)>
+  ];
+}
+
+def WasmSSA_MemImportOp : WasmSSA_Op<"import_mem", [Symbol, ImportOpInterface]> {
+  let summary = "Importing a memory";
+  let description = [{
+    Import a memory from another module.
+
+     Example:
+
+     ```mlir
+     // Import the memory `mem` from `my_module` as @mem_0
+     wasmssa.import_mem "mem" from "my_module" as @mem_0 {limits = !wasmssa<limit[2:]>}
+    ```
+    }];
+  let arguments = (ins SymbolNameAttr: $sym_name,
+                     StrAttr: $moduleName,
+                     StrAttr: $importName,
+                     WasmSSA_LimitTypeAttr: $limits,
+                     OptionalAttr<StrAttr>:$sym_visibility);
+  let extraClassDeclaration = [{
+     bool isDeclaration() const { return true; }
+   }];
+  let builders = [OpBuilder<(ins
+    "::llvm::StringRef":$symbol,
+    "::llvm::StringRef":$moduleName,
+    "::llvm::StringRef":$importName,
+    "wasmssa::LimitType":$limits)>];
+  let assemblyFormat = "$importName `from` $moduleName `as` $sym_name attr-dict";
+}
+
+def WasmSSA_TableOp : WasmSSA_Op<"table", [Symbol]> {
+  let summary= "WebAssembly table value";
+  let arguments = (ins SymbolNameAttr: $sym_name,
+                     WasmSSA_TableTypeAttr: $type,
+                     OptionalAttr<StrAttr>:$sym_visibility);
+  let builders = [OpBuilder<(ins
+      "::llvm::StringRef":$symbol,
+      "wasmssa::TableType":$type)>];
+}
+
+def WasmSSA_TableImportOp : WasmSSA_Op<"import_table", [Symbol, ImportOpInterface]> {
+  let summary = "Importing a table";
+  let description = [{
+    Import a table from another module.
+
+     Example:
+
+     ```mlir
+     // Import the table `table` from `my_module` as @table_0
+     wasmssa.import_table "table" from "my_module" as @table_0 {type = !wasmssa<tabletype !wasmssa.funcref [2:]>}
+    ```
+    }];
+  let arguments = (ins SymbolNameAttr: $sym_name,
+                     StrAttr: $moduleName,
+                     StrAttr: $importName,
+                     WasmSSA_TableTypeAttr: $type,
+                     OptionalAttr<StrAttr>:$sym_visibility);
+  let extraClassDeclaration = [{
+    bool isDeclaration() const { return true; }
+  }];
+  let assemblyFormat = "$importName `from` $moduleName `as` $sym_name attr-dict";
+  let builders = [OpBuilder<(ins
+      "::llvm::StringRef":$symbol,
+      "::llvm::StringRef":$moduleName,
+      "::llvm::StringRef":$importName,
+      "wasmssa::TableType":$type)>];
+}
+
+def WasmSSA_ReturnOp : WasmSSA_Op<"return", [Terminator]> {
+  let summary = "Return from the current function frame";
+  let arguments = (ins Variadic<WasmSSA_ValType>:  $operands);
+  let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?";
+  let builders = [
+    OpBuilder<(ins)>
+  ];
+}
+
+// ---- Numeric ops
+
+class WasmSSA_BinaryNumericalOp<string mnemonic, string summaryStr, string descStr,
+                       list<Type> validOpTypes> :
+  WasmSSA_Op<mnemonic, [AllTypesMatch<["lhs", "rhs", "result"]>]> {
+  let summary = summaryStr;
+  let description = descStr;
+  let arguments = (ins AnyTypeOf<validOpTypes>:$lhs, AnyTypeOf<validOpTypes>:$rhs);
+  let results = (outs AnyTypeOf<validOpTypes>:$result);
+  let assemblyFormat = "$lhs $rhs `:` type($lhs) attr-dict";
+}
+
+def WasmSSA_AddOp : WasmSSA_BinaryNumericalOp<"add",
+    "Sum two values",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.add %b %c : i32
+    ```
+    }],
+    [WasmSSA_NumericType]>{}
+
+def WasmSSA_AndOp : WasmSSA_BinaryNumericalOp<"and",
+    "Compute the bitwise AND between two values",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.and %b %c : i32
+    ```
+    }],
+    [WasmSSA_NumericType]>{}
+
+def WasmSSA_DivOp : WasmSSA_BinaryNumericalOp<"div",
+    "Division between floating point values",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.div %b %c : f32
+    ```
+    }],
+    [WasmSSA_FPType]>{}
+
+def WasmSSA_DivUIOp : WasmSSA_BinaryNumericalOp<"div_ui",
+    "Divide values interpreted as unsigned int",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.div_ui %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_DivSIOp : WasmSSA_BinaryNumericalOp<"div_si",
+    "Divide values interpreted as signed int",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.div_si %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_MulOp : WasmSSA_BinaryNumericalOp<"mul",
+    "Multiply two values",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.mul %b %c : i32
+    ```
+    }],
+    [WasmSSA_NumericType]>{}
+
+def WasmSSA_OrOp : WasmSSA_BinaryNumericalOp<"or",
+    "Compute the bitwise OR of two values",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.or %b %c : i32
+    ```
+    }],
+    [WasmSSA_NumericType]>{}
+
+def WasmSSA_SubOp : WasmSSA_BinaryNumericalOp<"sub",
+    "Subtract two values",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.sub %b %c : i32
+    ```
+    }],
+    [WasmSSA_NumericType]>{}
+
+def WasmSSA_RemUIOp : WasmSSA_BinaryNumericalOp<"rem_ui",
+    "Calculate the remainder of dividing two integer values as an unsigned integer",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.rem_ui %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_RemSIOp : WasmSSA_BinaryNumericalOp<"rem_si",
+    "Calculate the remainder of dividing two integer values as signed integer",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.rem_si %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_XOrOp : WasmSSA_BinaryNumericalOp<"xor",
+    "Compute the bitwise XOR of two values",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.xor %b %c : i32
+    ```
+    }],
+    [WasmSSA_NumericType]>{}
+
+def WasmSSA_MinOp : WasmSSA_BinaryNumericalOp<"min",
+    "Compute the minimum of two floating point values.",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.min %b %c : f32
+    ```
+    }],
+    [WasmSSA_FPType]>{}
+
+def WasmSSA_MaxOp : WasmSSA_BinaryNumericalOp<"max",
+    "Compute the minimum of two floating point values.",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.max %b %c : f32
+    ```
+    }],
+    [WasmSSA_FPType]>{}
+
+def WasmSSA_CopySignOp : WasmSSA_BinaryNumericalOp<"copysign",
+    "Copy sign from one floating point value to the other.",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.copysign %b %c : f32
+    ```
+    }],
+    [WasmSSA_FPType]>{}
+
+class WasmSSA_BinaryComparisonOp<string mnemonic, string summaryStr, string descStr,
+                       list<Type> validOpTypes> :
+  WasmSSA_Op<mnemonic, [AllTypesMatch<["lhs", "rhs"]>]> {
+  let summary = summaryStr;
+  let description = descStr;
+  let arguments = (ins AnyTypeOf<validOpTypes>:$lhs, AnyTypeOf<validOpTypes>:$rhs);
+  let results = (outs I32:$result);
+  let assemblyFormat = "$lhs $rhs `:` type($lhs) `->` type($result) attr-dict";
+}
+
+def WasmSSA_EqOp : WasmSSA_BinaryComparisonOp<"eq",
+    "Check if two values are equal",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.eq %b %c : i32
+    ```
+    }],
+    [WasmSSA_NumericType]>{}
+
+def WasmSSA_NeOp : WasmSSA_BinaryComparisonOp<"ne",
+    "Check if two values are 
diff erent",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.ne %b %c : i32
+    ```
+    }],
+    [WasmSSA_NumericType]>{}
+
+def WasmSSA_LtSIOp : WasmSSA_BinaryComparisonOp<"lt_si",
+    "Check if a signed integer value is less than another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.lt_si %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_LtUIOp : WasmSSA_BinaryComparisonOp<"lt_ui",
+    "Check if an unsigned integer value is less than another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.lt_ui %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_LeSIOp : WasmSSA_BinaryComparisonOp<"le_si",
+    "Check if a signed integer value is less or equal to another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.le_si %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_LeUIOp : WasmSSA_BinaryComparisonOp<"le_ui",
+    "Check if an unsigned integer value is less or equal to another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.le_ui %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_GtSIOp : WasmSSA_BinaryComparisonOp<"gt_si",
+    "Check if a signed integer value is greater than another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.gt_si %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_GtUIOp : WasmSSA_BinaryComparisonOp<"gt_ui",
+    "Check if an unsigned integer value is greater than another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.gt_ui %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_GeSIOp : WasmSSA_BinaryComparisonOp<"ge_si",
+    "Check if a signed integer value is greater or equal to another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.ge_si %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_GeUIOp : WasmSSA_BinaryComparisonOp<"ge_ui",
+    "Check if an unsigned integer value is greater or equal to another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.ge_ui %b %c : i32
+    ```
+    }],
+    [WasmSSA_IntegerType]>{}
+
+def WasmSSA_LtOp : WasmSSA_BinaryComparisonOp<"lt",
+    "Check if a float value is less than another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.lt %b %c : i32
+    ```
+    }],
+    [WasmSSA_FPType]>{}
+
+def WasmSSA_LeOp : WasmSSA_BinaryComparisonOp<"le",
+    "Check if a float value is less or equal to another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.le %b %c : i32
+    ```
+    }],
+    [WasmSSA_FPType]>{}
+
+def WasmSSA_GtOp : WasmSSA_BinaryComparisonOp<"gt",
+    "Check if a float value is greater than another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.gt %b %c : i32
+    ```
+    }],
+    [WasmSSA_FPType]>{}
+
+def WasmSSA_GeOp : WasmSSA_BinaryComparisonOp<"ge",
+    "Check if a float value is greater or equal to another",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.ge %b %c : i32
+    ```
+    }],
+    [WasmSSA_FPType]>{}
+
+// Integer shift and rotate operations.
+class WasmSSA_ShiftRotateOp<string mnemonic, string summaryStr, string descStr> :
+  WasmSSA_Op<mnemonic, [AllTypesMatch<["val", "bits", "result"]>]> {
+  let summary = summaryStr;
+  let description = descStr;
+  let arguments = (ins WasmSSA_IntegerType:$val, WasmSSA_IntegerType:$bits);
+  let results = (outs WasmSSA_IntegerType:$result);
+  let assemblyFormat = "$val `by` $bits `bits` `:` type($val) attr-dict";
+}
+
+def WasmSSA_ShLOp : WasmSSA_ShiftRotateOp<"shl",
+    [{Consume an integer and an integer shift amount. The first
+    integer shall be shifted left by N bits, where N is the value of the second
+    integer.}],
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.shl %b by %c bits : i64
+    ```
+    }]
+    >{}
+
+def WasmSSA_ShRSOp : WasmSSA_ShiftRotateOp<"shr_s",
+    [{Arithmetic right shift.
+
+    Consume an integer and an integer shift amount. The first
+    integer shall be shifted right by N bits, where N is the value of the
+    second integer.
+
+    Vacated bits on the left shall be filled with the sign bit.}],
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.shr_s %b by %c bits : i64
+    ```
+    }]
+    >{}
+
+def WasmSSA_ShRUOp : WasmSSA_ShiftRotateOp<"shr_u",
+    [{Logical right shift.
+
+    Consume an integer, and an integer shift amount. The first
+    integer shall be shifted right by N bits, where N is the value of the
+    second integer.
+
+    Vacated bits on the left shall be filled with zeroes.}],
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.shr_u %b by %c bits : i64
+    ```
+    }]
+    >{}
+
+def WasmSSA_RotlOp : WasmSSA_ShiftRotateOp<"rotl",
+    [{Rotate left.
+
+    Consume an integer and an integer rotate. The first
+    integer shall be rotated left by N bits, where N is the value of the
+    second integer.}],
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.rotl %b by %c bits : i64
+    ```
+    }]
+    >{}
+
+def WasmSSA_RotrOp : WasmSSA_ShiftRotateOp<"rotr",
+    [{Rotate right.
+
+    Consume an integer, and an integer rotate. The first
+    integer shall be rotated right by N bits, where N is the value of the
+    second integer.}],
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.rotr %b by %c bits : i64
+    ```
+    }]
+    >{}
+
+class WasmSSA_ConversionOp<string mnemonic, string summaryStr, string descStr,
+                       list<Type> ValidInputTypes,
+                       list<Type> ValidOutputTypes> :
+    WasmSSA_Op<mnemonic> {
+  let summary = summaryStr;
+  let description = descStr;
+  let arguments = (ins AnyTypeOf<ValidInputTypes>:$input);
+  let results = (outs AnyTypeOf<ValidOutputTypes>:$result);
+  let assemblyFormat = "$input `:` type($input) `to` type($result)  attr-dict";
+}
+
+def WasmSSA_ConvertUOp : WasmSSA_ConversionOp<"convert_u",
+    [{Convert integer, interpreted as binary encoded positive value, to floating-point value.
+
+    Consume an integer and produces a floating point value containing the rounded value of the original operand. Rounding is round to nearest, tie to even.}],
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.convert_u %b : i32 to f64
+    ```
+    }],
+    [WasmSSA_IntegerType],
+    [WasmSSA_FPType]>{}
+
+def WasmSSA_ConvertSOp : WasmSSA_ConversionOp<"convert_s",
+    [{Convert integer interpreted as 2's complement signed value to floating-point value.
+
+    Consume an integer and produces a floating point value containing the rounded value of the original operand. Rounding is round to nearest, tie to even.}],
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.convert_s %b : i32 to f64
+    ```
+    }],
+    [WasmSSA_IntegerType],
+    [WasmSSA_FPType]>{}
+
+def WasmSSA_DemoteOp : WasmSSA_ConversionOp<"demote",
+    "Convert a f64 value to f32",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.demote %b : f64 to f32
+    ```
+    }],
+    [F64],
+    [F32]>{}
+
+def WasmSSA_ExtendSI32Op : WasmSSA_Op<"extend_i32_s">{
+  let summary = [{Sign extend i32 to i64.}];
+  let description =  [{Example:
+
+     ```mlir
+     %a = wasmssa.extend_i32_s %b to i64
+    ```
+    }];
+  let arguments = (ins I32:$input);
+  let results = (outs I64:$result);
+  let assemblyFormat = "$input `to` type($result)  attr-dict";
+}
+
+def WasmSSA_ExtendUI32Op : WasmSSA_Op<"extend_i32_u">{
+  let summary = [{Zero extend i32 to i64.}];
+  let description = [{Example:
+
+     ```mlir
+     %a = wasmssa.extend_i32_s %b to i64
+    ```
+    }];
+  let arguments = (ins I32:$input);
+  let results = (outs I64:$result);
+  let assemblyFormat = "$input `to` type($result)  attr-dict";
+}
+
+def WasmSSA_ExtendLowBitsSOp : WasmSSA_Op<"extend", [AllTypesMatch<["input", "result"]>]> {
+  let summary = "";
+  let description = [{
+  Extend low bytes of a value to fit a given width.
+  For instance, signed extension from 8 low bits of the 32-bits integer value
+  254 (0x000000FE) would produce the value -2 (0xFFFFFFFE).
+
+  This corresponds to the `extendnn` instruction of Wasm, which shouldn't be
+  confused with the `extend_inn` Wasm instruction, for which all input bits
+  are used and widened to wider output type.
+  In this operation, input and output types are the same.
+
+  Example:
+
+  ```mlir
+  %a = wasmssa.extend 16 low bits from %[[VAL_0]]: i64
+  ```
+  }];
+  let arguments = (ins WasmSSA_IntegerType:$input, Builtin_IntegerAttr: $bitsToTake);
+  let results = (outs WasmSSA_IntegerType: $result);
+  let hasVerifier = 1;
+  let assemblyFormat = "$bitsToTake `low` `bits` `from` $input `:` type($input) attr-dict";
+}
+
+def WasmSSA_PromoteOp : WasmSSA_ConversionOp<"promote",
+    "Get f64 representation of a f32 value.",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.promote %b : f32 to f64
+     ```
+    }],
+    [Builtin_Float32],
+    [Builtin_Float64]>{}
+
+def WasmSSA_WrapOp : WasmSSA_ConversionOp<"wrap",
+    "Cast an i64 to i32 by using a wrapping mechanism: y = x mod 2^32",
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.wrap %b : i64 to i32
+     ```
+    }],
+    [I64],
+    [I32]>{}
+
+// Reinterpret ops are basically all one-offs. They all have an unique,
+// type-postfixed opcode, and support exactly one input and output type.
+def WasmSSA_ReinterpretOp : WasmSSA_ConversionOp<"reinterpret",
+    [{Reinterpret the value represented by a bit vector by
+      bit-casting it to another type of same representation width.}],
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.reinterpret %b : f32 as i32
+     ```
+    }],
+    [WasmSSA_NumericType], [WasmSSA_NumericType]>{
+    let assemblyFormat = "$input `:` type($input) `as` type($result) attr-dict";
+    let hasVerifier = 1;
+}
+
+class WasmSSA_UnaryNumericalOp<string mnemonic,
+                            string summaryStr,
+                            string descStr,
+                            list<Type> validOpTypes> :
+  WasmSSA_Op<mnemonic, [AllTypesMatch<["src", "result"]>]> {
+    let summary = summaryStr;
+    let description = descStr;
+    let arguments = (ins AnyTypeOf<validOpTypes>:$src);
+    let results = (outs AnyTypeOf<validOpTypes>:$result);
+    let assemblyFormat = "$src`:` type($src) attr-dict";
+}
+
+def WasmSSA_AbsOp : WasmSSA_UnaryNumericalOp<"abs",
+                                       "Floating point absolute value",
+                                        [{Example:
+
+                                         ```mlir
+                                         %a = wasmssa.abs %b : f32
+                                         ```
+                                        }],
+                                       [WasmSSA_FPType]>{}
+
+def WasmSSA_CeilOp : WasmSSA_UnaryNumericalOp<"ceil",
+                                       "Ceil rounding of floating point value",
+                                        [{Example:
+
+                                         ```mlir
+                                         %a = wasmssa.ceil %b : f32
+                                         ```
+                                        }],
+                                       [WasmSSA_FPType]>{}
+
+def WasmSSA_FloorOp : WasmSSA_UnaryNumericalOp<"floor",
+                                       "Floor rounding of floating point value",
+                                        [{Example:
+
+                                         ```mlir
+                                         %a = wasmssa.floor %b : f32
+                                         ```
+                                        }],
+                                       [WasmSSA_FPType]>{}
+
+def WasmSSA_NegOp : WasmSSA_UnaryNumericalOp<"neg",
+                                       "Floating point negation",
+                                        [{Example:
+
+                                         ```mlir
+                                         %a = wasmssa.neg %b : f32
+                                         ```
+                                        }],
+                                       [WasmSSA_FPType]>{}
+
+def WasmSSA_SqrtOp : WasmSSA_UnaryNumericalOp<"sqrt",
+                                       "Floating point square root",
+                                        [{Example:
+
+                                         ```mlir
+                                         %a = wasmssa.sqrt %b : f32
+                                         ```
+                                        }],
+                                       [WasmSSA_FPType]>{}
+
+def WasmSSA_TruncOp : WasmSSA_UnaryNumericalOp<"trunc",
+                                       "Trunc of floating point value",
+                                        [{Example:
+
+                                         ```mlir
+                                         %a = wasmssa.trunc %b : f32
+                                         ```
+                                        }],
+                                       [WasmSSA_FPType]>{}
+
+def WasmSSA_CtzOp : WasmSSA_UnaryNumericalOp<"ctz",
+                                       "Count trailing zeroes of an integer",
+                                        [{Example:
+
+                                         ```mlir
+                                         %a = wasmssa.ctz %b : i32
+                                         ```
+                                        }],
+                                       [WasmSSA_IntegerType]>{}
+
+def WasmSSA_ClzOp : WasmSSA_UnaryNumericalOp<"clz",
+                                       "Count leading zeroes of an integer",
+                                        [{Example:
+
+                                         ```mlir
+                                         %a = wasmssa.clz %b : i32
+                                         ```
+                                        }],
+                                       [WasmSSA_IntegerType]>{}
+
+def WasmSSA_EqzOp : WasmSSA_Op<"eqz", []> {
+  let summary = "Check if the given value is equal to zero";
+  let description =
+    [{Example:
+
+     ```mlir
+     %a = wasmssa.eqz %b : i64 -> i32
+     ```
+    }];
+  let arguments = (ins WasmSSA_IntegerType: $input);
+  let results = (outs I32: $result);
+  let assemblyFormat = "$input`:` type($input) `->` type($result) attr-dict";
+}
+
+
+def WasmSSA_PopCntOp : WasmSSA_UnaryNumericalOp<"popcnt",
+                                       "Population count of an integer.",
+                                        [{Example:
+
+                                         ```mlir
+                                         %a = wasmssa.popcnt %b : i32
+                                         ```
+                                        }],
+                                       [WasmSSA_IntegerType]>{}
+
+
+#endif // WasmSSA_OPS

diff  --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSATypes.td b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSATypes.td
new file mode 100644
index 0000000000000..4d78836cce75e
--- /dev/null
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSATypes.td
@@ -0,0 +1,85 @@
+//===- WasmSSATypes.td - WasmSSA types def ----*- tablegen -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef WasmSSA_TYPES
+#define WasmSSA_TYPES
+
+include "mlir/Dialect/WasmSSA/IR/WasmSSABase.td"
+
+include "mlir/IR/AttrTypeBase.td"
+include "mlir/IR/BuiltinTypes.td"
+include "mlir/IR/CommonAttrConstraints.td"
+include "mlir/IR/CommonTypeConstraints.td"
+
+def WasmSSA_IntegerType : AnyTypeOf<[I32, I64]>;
+def WasmSSA_FPType: AnyTypeOf<[F32, F64]>;
+def WasmSSA_NumericType : AnyTypeOf<[WasmSSA_IntegerType, WasmSSA_FPType]>{
+  let cppFunctionName = "isWasmNumericType";
+}
+def WasmSSA_VecType : AnyTypeOf<[I128]>;
+
+class WasmSSA_Type<string name, string typeMnemonic, list<Trait> traits = []>
+    : TypeDef<WasmSSA_Dialect, name, traits> {
+  let mnemonic = typeMnemonic;
+}
+
+def WasmSSA_FuncRefType : WasmSSA_Type<"FuncRef", "funcref"> {
+  let summary = "Opaque type for function reference";
+  let assemblyFormat = "";
+}
+
+def WasmSSA_ExternRefType : WasmSSA_Type<"ExternRef", "externref"> {
+  let summary = "Opaque type for external reference";
+}
+
+def WasmSSA_RefType : AnyTypeOf<[WasmSSA_FuncRefType, WasmSSA_ExternRefType]> {
+  let cppFunctionName = "isWasmRefType";
+}
+
+def WasmSSA_ValType : AnyTypeOf<[WasmSSA_NumericType, WasmSSA_VecType, WasmSSA_RefType]> {
+  let cppFunctionName = "isWasmValueType";
+}
+
+def WasmSSA_ResultType : TupleOf<[WasmSSA_ValType]>;
+
+def WasmSSA_FuncType : TypeAlias<FunctionType>;
+
+def WasmSSA_LimitType : WasmSSA_Type<"Limit", "limit"> {
+  let summary = "Wasm limit type";
+  let parameters = (ins "uint32_t":$min,
+                        OptionalParameter<"std::optional<uint32_t>">:$max);
+  let assemblyFormat = "`[` $min `` `:` ($max^)? `]`";
+}
+
+def WasmSSA_LocalRef : WasmSSA_Type<"LocalRef", "local"> {
+  let summary = "Type of a local variable";
+  let parameters = (ins WasmSSA_ValType: $elementType);
+  let assemblyFormat = "`ref` `to` $elementType";
+  let builders = [TypeBuilderWithInferredContext<(ins "Type":$typeParam), [{
+      return get(typeParam.getContext(), typeParam);
+    }]>,];
+
+}
+
+def WasmSSA_TableType : WasmSSA_Type<"Table", "tabletype"> {
+  let summary = "Wasm table type";
+  let parameters = (ins WasmSSA_RefType:$reference,
+                       WasmSSA_LimitType:$limit);
+  let assemblyFormat = "$reference $limit";
+}
+
+def WasmSSA_FuncTypeAttr : TypeAttrOf<WasmSSA_FuncType>;
+def WasmSSA_LimitTypeAttr : TypeAttrOf<WasmSSA_LimitType>;
+def WasmSSA_TableTypeAttr : TypeAttrOf<WasmSSA_TableType>;
+def WasmSSA_ValTypeAttr : TypeAttrOf<WasmSSA_ValType>;
+
+def WasmSSA_IntegerAttr : AnyAttrOf<[I32Attr, I64Attr]>;
+def WasmSSA_FPAttr : AnyAttrOf<[F32Attr, F64Attr]>;
+def WasmSSA_NumericAttr : AnyAttrOf<[WasmSSA_IntegerAttr, WasmSSA_FPAttr]>;
+
+#endif // WasmSSA_TYPES

diff  --git a/mlir/lib/Dialect/CMakeLists.txt b/mlir/lib/Dialect/CMakeLists.txt
index 053ee95e92053..0acb4b16fe002 100644
--- a/mlir/lib/Dialect/CMakeLists.txt
+++ b/mlir/lib/Dialect/CMakeLists.txt
@@ -41,6 +41,7 @@ add_subdirectory(Transform)
 add_subdirectory(UB)
 add_subdirectory(Utils)
 add_subdirectory(Vector)
+add_subdirectory(WasmSSA)
 add_subdirectory(X86Vector)
 add_subdirectory(XeGPU)
 

diff  --git a/mlir/lib/Dialect/WasmSSA/CMakeLists.txt b/mlir/lib/Dialect/WasmSSA/CMakeLists.txt
new file mode 100644
index 0000000000000..f33061b2d87cf
--- /dev/null
+++ b/mlir/lib/Dialect/WasmSSA/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(IR)

diff  --git a/mlir/lib/Dialect/WasmSSA/IR/CMakeLists.txt b/mlir/lib/Dialect/WasmSSA/IR/CMakeLists.txt
new file mode 100644
index 0000000000000..9fc2d7b87abd7
--- /dev/null
+++ b/mlir/lib/Dialect/WasmSSA/IR/CMakeLists.txt
@@ -0,0 +1,24 @@
+add_mlir_dialect_library(MLIRWasmSSADialect
+  WasmSSAOps.cpp
+  WasmSSADialect.cpp
+  WasmSSAInterfaces.cpp
+  WasmSSATypes.cpp
+
+  ADDITIONAL_HEADER_DIRS
+  ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/WasmSSA
+
+  DEPENDS
+  MLIRWasmSSAOpsIncGen
+  MLIRWasmSSAInterfacesIncGen
+
+  LINK_LIBS PUBLIC
+  MLIRCastInterfaces
+  MLIRDataLayoutInterfaces
+  MLIRDialect
+  MLIRInferTypeOpInterface
+  MLIRIR
+  MLIRSupport
+
+  PRIVATE
+  MLIRFunctionInterfaces
+  )

diff  --git a/mlir/lib/Dialect/WasmSSA/IR/WasmSSADialect.cpp b/mlir/lib/Dialect/WasmSSA/IR/WasmSSADialect.cpp
new file mode 100644
index 0000000000000..98c3555b5324e
--- /dev/null
+++ b/mlir/lib/Dialect/WasmSSA/IR/WasmSSADialect.cpp
@@ -0,0 +1,38 @@
+//===- WebAssemblyDialect.cpp - MLIR WebAssembly dialect implementation ---===//
+//
+// 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 "llvm/ADT/TypeSwitch.h"
+
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/DialectImplementation.h"
+#include "mlir/Support/LLVM.h"
+
+using namespace mlir;
+using namespace mlir::wasmssa;
+
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAOpsDialect.cpp.inc"
+
+//===----------------------------------------------------------------------===//
+// TableGen'd types definitions
+//===----------------------------------------------------------------------===//
+
+#define GET_TYPEDEF_CLASSES
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAOpsTypes.cpp.inc"
+
+void wasmssa::WasmSSADialect::initialize() {
+  addOperations<
+#define GET_OP_LIST
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAOps.cpp.inc"
+      >();
+  addTypes<
+#define GET_TYPEDEF_LIST
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAOpsTypes.cpp.inc"
+      >();
+}

diff  --git a/mlir/lib/Dialect/WasmSSA/IR/WasmSSAInterfaces.cpp b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAInterfaces.cpp
new file mode 100644
index 0000000000000..61cdf6f70e188
--- /dev/null
+++ b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAInterfaces.cpp
@@ -0,0 +1,69 @@
+//===- WasmSSAInterfaces.cpp - WasmSSA Interfaces -*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines op interfaces for the WasmSSA dialect in MLIR.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.h"
+#include "mlir/Dialect/WasmSSA/IR/WasmSSA.h"
+#include "mlir/IR/Operation.h"
+#include "mlir/IR/Visitors.h"
+#include "mlir/Support/LLVM.h"
+#include "llvm/Support/LogicalResult.h"
+
+namespace mlir::wasmssa {
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.cpp.inc"
+
+namespace detail {
+LogicalResult verifyLabelBranchingOpInterface(Operation *op) {
+  auto branchInterface = dyn_cast<LabelBranchingOpInterface>(op);
+  llvm::FailureOr<LabelLevelOpInterface> res =
+      LabelBranchingOpInterface::getTargetOpFromBlock(
+          op->getBlock(), branchInterface.getExitLevel());
+  return res;
+}
+
+LogicalResult verifyConstantExpressionInterface(Operation *op) {
+  Region &initializerRegion = op->getRegion(0);
+  WalkResult resultState =
+      initializerRegion.walk([&](Operation *currentOp) -> WalkResult {
+        if (isa<ReturnOp>(currentOp) ||
+            currentOp->hasTrait<ConstantExprOpTrait>())
+          return WalkResult::advance();
+        op->emitError("expected a constant initializer for this operator, got ")
+            << currentOp;
+        return WalkResult::interrupt();
+      });
+  return success(!resultState.wasInterrupted());
+}
+
+LogicalResult verifyLabelLevelInterface(Operation *op) {
+  Block *target = cast<LabelLevelOpInterface>(op).getLabelTarget();
+  Region *targetRegion = target->getParent();
+  if (targetRegion != op->getParentRegion() &&
+      targetRegion->getParentOp() != op)
+    return op->emitError("target should be a block defined in same level than "
+                         "operation or in its region.");
+  return success();
+}
+} // namespace detail
+
+llvm::FailureOr<LabelLevelOpInterface>
+LabelBranchingOpInterface::getTargetOpFromBlock(::mlir::Block *block,
+                                                uint32_t breakLevel) {
+  LabelLevelOpInterface res{};
+  for (size_t curLevel{0}; curLevel <= breakLevel; curLevel++) {
+    res = dyn_cast_or_null<LabelLevelOpInterface>(block->getParentOp());
+    if (!res)
+      return failure();
+    block = res->getBlock();
+  }
+  return res;
+}
+} // namespace mlir::wasmssa

diff  --git a/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
new file mode 100644
index 0000000000000..2ce32fe895205
--- /dev/null
+++ b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
@@ -0,0 +1,454 @@
+//===- WasmSSAOps.cpp - WasmSSA dialect operations ----------------===//
+//
+// 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/Dialect/WasmSSA/IR/WasmSSAInterfaces.h"
+
+#include "mlir/IR/Attributes.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/Dialect.h"
+#include "mlir/IR/Region.h"
+#include "mlir/IR/SymbolTable.h"
+#include "mlir/Interfaces/FunctionImplementation.h"
+#include "llvm/Support/Casting.h"
+
+//===----------------------------------------------------------------------===//
+// TableGen'd op method definitions
+//===----------------------------------------------------------------------===//
+
+#define GET_OP_CLASSES
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAOps.cpp.inc"
+
+#include "mlir/IR/OpImplementation.h"
+#include "mlir/IR/Types.h"
+#include "llvm/Support/LogicalResult.h"
+
+using namespace mlir;
+using namespace wasmssa;
+
+namespace {
+inline LogicalResult
+inferTeeGetResType(ValueRange operands,
+                   SmallVectorImpl<Type> &inferredReturnTypes) {
+  if (operands.empty())
+    return failure();
+  auto opType = dyn_cast<LocalRefType>(operands.front().getType());
+  if (!opType)
+    return failure();
+  inferredReturnTypes.push_back(opType.getElementType());
+  return success();
+}
+
+ParseResult parseImportOp(OpAsmParser &parser, OperationState &result) {
+  std::string importName;
+  auto *ctx = parser.getContext();
+  ParseResult res = parser.parseString(&importName);
+  result.addAttribute("importName", StringAttr::get(ctx, importName));
+
+  std::string fromStr;
+  res = parser.parseKeywordOrString(&fromStr);
+  if (failed(res) || fromStr != "from")
+    return failure();
+
+  std::string moduleName;
+  res = parser.parseString(&moduleName);
+  if (failed(res))
+    return failure();
+  result.addAttribute("moduleName", StringAttr::get(ctx, moduleName));
+
+  std::string asStr;
+  res = parser.parseKeywordOrString(&asStr);
+  if (failed(res) || asStr != "as")
+    return failure();
+
+  StringAttr symbolName;
+  res = parser.parseSymbolName(symbolName, SymbolTable::getSymbolAttrName(),
+                               result.attributes);
+  return res;
+}
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// BlockOp
+//===----------------------------------------------------------------------===//
+
+Block *BlockOp::getLabelTarget() { return getTarget(); }
+
+//===----------------------------------------------------------------------===//
+// BlockReturnOp
+//===----------------------------------------------------------------------===//
+
+std::size_t BlockReturnOp::getExitLevel() { return 0; }
+
+Block *BlockReturnOp::getTarget() {
+  return cast<LabelBranchingOpInterface>(getOperation())
+      .getTargetOp()
+      .getOperation()
+      ->getSuccessor(0);
+}
+
+//===----------------------------------------------------------------------===//
+// ExtendLowBitsSOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult ExtendLowBitsSOp::verify() {
+  auto bitsToTake = getBitsToTake().getValue().getLimitedValue();
+  if (bitsToTake != 32 && bitsToTake != 16 && bitsToTake != 8)
+    return emitError("extend op can only take 8, 16 or 32 bits. Got ")
+           << bitsToTake;
+
+  if (bitsToTake >= getInput().getType().getIntOrFloatBitWidth())
+    return emitError("trying to extend the ")
+           << bitsToTake << " low bits from a " << getInput().getType()
+           << " value is illegal";
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// FuncOp
+//===----------------------------------------------------------------------===//
+
+Block *FuncOp::addEntryBlock() {
+  if (!getBody().empty()) {
+    emitError("adding entry block to a FuncOp which already has one");
+    return &getBody().front();
+  }
+  Block &block = getBody().emplaceBlock();
+  for (auto argType : getFunctionType().getInputs())
+    block.addArgument(LocalRefType::get(argType), getLoc());
+  return █
+}
+
+void FuncOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+                   StringRef symbol, FunctionType funcType) {
+  FuncOp::build(odsBuilder, odsState, symbol, funcType, {}, {}, "nested");
+}
+
+ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
+  auto buildFuncType = [&parser](Builder &builder, ArrayRef<Type> argTypes,
+                                 ArrayRef<Type> results,
+                                 function_interface_impl::VariadicFlag,
+                                 std::string &) {
+    SmallVector<Type> argTypesWithoutLocal{};
+    argTypesWithoutLocal.reserve(argTypes.size());
+    llvm::for_each(argTypes, [&parser, &argTypesWithoutLocal](Type argType) {
+      auto refType = dyn_cast<LocalRefType>(argType);
+      auto loc = parser.getEncodedSourceLoc(parser.getCurrentLocation());
+      if (!refType) {
+        mlir::emitError(loc, "invalid type for wasm.func argument. Expecting "
+                             "!wasm<local T>, got ")
+            << argType;
+        return;
+      }
+      argTypesWithoutLocal.push_back(refType.getElementType());
+    });
+
+    return builder.getFunctionType(argTypesWithoutLocal, results);
+  };
+
+  return function_interface_impl::parseFunctionOp(
+      parser, result, /*allowVariadic=*/false,
+      getFunctionTypeAttrName(result.name), buildFuncType,
+      getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
+}
+
+LogicalResult FuncOp::verifyBody() {
+  if (getBody().empty())
+    return success();
+  Block &entry = getBody().front();
+  if (entry.getNumArguments() != getFunctionType().getNumInputs())
+    return emitError("entry block should have same number of arguments as "
+                     "function type. Function type has ")
+           << getFunctionType().getNumInputs() << ", entry block has "
+           << entry.getNumArguments();
+
+  for (auto [argNo, funcSignatureType, blockType] : llvm::enumerate(
+           getFunctionType().getInputs(), entry.getArgumentTypes())) {
+    auto blockLocalRefType = dyn_cast<LocalRefType>(blockType);
+    if (!blockLocalRefType)
+      return emitError("entry block argument type should be LocalRefType, got ")
+             << blockType << " for block argument " << argNo;
+    if (blockLocalRefType.getElementType() != funcSignatureType)
+      return emitError("func argument type #")
+             << argNo << "(" << funcSignatureType
+             << ") doesn't match entry block referenced type ("
+             << blockLocalRefType.getElementType() << ")";
+  }
+  return success();
+}
+
+void FuncOp::print(OpAsmPrinter &p) {
+  function_interface_impl::printFunctionOp(
+      p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
+      getArgAttrsAttrName(), getResAttrsAttrName());
+}
+
+//===----------------------------------------------------------------------===//
+// FuncImportOp
+//===----------------------------------------------------------------------===//
+
+void FuncImportOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+                         StringRef symbol, StringRef moduleName,
+                         StringRef importName, FunctionType type) {
+  FuncImportOp::build(odsBuilder, odsState, symbol, moduleName, importName,
+                      type, {}, {}, odsBuilder.getStringAttr("nested"));
+}
+
+//===----------------------------------------------------------------------===//
+// GlobalOp
+//===----------------------------------------------------------------------===//
+
+void GlobalOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+                     StringRef symbol, Type type, bool isMutable) {
+  GlobalOp::build(odsBuilder, odsState, symbol, type, isMutable,
+                  odsBuilder.getStringAttr("nested"));
+}
+
+// Custom formats
+ParseResult GlobalOp::parse(OpAsmParser &parser, OperationState &result) {
+  StringAttr symbolName;
+  Type globalType;
+  auto *ctx = parser.getContext();
+  ParseResult res = parser.parseSymbolName(
+      symbolName, SymbolTable::getSymbolAttrName(), result.attributes);
+
+  res = parser.parseType(globalType);
+  result.addAttribute(getTypeAttrName(result.name), TypeAttr::get(globalType));
+  std::string mutableString;
+  res = parser.parseOptionalKeywordOrString(&mutableString);
+  if (res.succeeded() && mutableString == "mutable")
+    result.addAttribute("isMutable", UnitAttr::get(ctx));
+  std::string visibilityString;
+  res = parser.parseOptionalKeywordOrString(&visibilityString);
+  if (res.succeeded())
+    result.addAttribute("sym_visibility",
+                        StringAttr::get(ctx, visibilityString));
+  res = parser.parseColon();
+  Region *globalInitRegion = result.addRegion();
+  res = parser.parseRegion(*globalInitRegion);
+  return res;
+}
+
+void GlobalOp::print(OpAsmPrinter &printer) {
+  printer << " @" << getSymName().str() << " " << getType();
+  if (getIsMutable())
+    printer << " mutable";
+  if (auto vis = getSymVisibility())
+    printer << " " << *vis;
+  printer << " :";
+  Region &body = getRegion();
+  if (!body.empty()) {
+    printer << ' ';
+    printer.printRegion(body, /*printEntryBlockArgs=*/false,
+                        /*printBlockTerminators=*/true);
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// GlobalGetOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+GlobalGetOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+  // If the parent requires a constant context, verify that global.get is a
+  // constant as defined per the wasm standard.
+  if (!this->getOperation()
+           ->getParentWithTrait<ConstantExpressionInitializerOpTrait>())
+    return success();
+  Operation *symTabOp = SymbolTable::getNearestSymbolTable(*this);
+  StringRef referencedSymbol = getGlobal();
+  Operation *definitionOp = symbolTable.lookupSymbolIn(
+      symTabOp, StringAttr::get(this->getContext(), referencedSymbol));
+  if (!definitionOp)
+    return emitError() << "symbol @" << referencedSymbol << " is undefined";
+  auto definitionImport = dyn_cast<GlobalImportOp>(definitionOp);
+  if (!definitionImport || definitionImport.getIsMutable()) {
+    return emitError("global.get op is considered constant if it's referring "
+                     "to a import.global symbol marked non-mutable");
+  }
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// GlobalImportOp
+//===----------------------------------------------------------------------===//
+
+void GlobalImportOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+                           StringRef symbol, StringRef moduleName,
+                           StringRef importName, Type type, bool isMutable) {
+  GlobalImportOp::build(odsBuilder, odsState, symbol, moduleName, importName,
+                        type, isMutable, odsBuilder.getStringAttr("nested"));
+}
+
+ParseResult GlobalImportOp::parse(OpAsmParser &parser, OperationState &result) {
+  auto *ctx = parser.getContext();
+  ParseResult res = parseImportOp(parser, result);
+  if (res.failed())
+    return failure();
+  std::string mutableOrSymVisString;
+  res = parser.parseOptionalKeywordOrString(&mutableOrSymVisString);
+  if (res.succeeded() && mutableOrSymVisString == "mutable") {
+    result.addAttribute("isMutable", UnitAttr::get(ctx));
+    res = parser.parseOptionalKeywordOrString(&mutableOrSymVisString);
+  }
+
+  if (res.succeeded())
+    result.addAttribute("sym_visibility",
+                        StringAttr::get(ctx, mutableOrSymVisString));
+  res = parser.parseColon();
+
+  Type importedType;
+  res = parser.parseType(importedType);
+  if (res.succeeded())
+    result.addAttribute(getTypeAttrName(result.name),
+                        TypeAttr::get(importedType));
+  return res;
+}
+
+void GlobalImportOp::print(OpAsmPrinter &printer) {
+  printer << " \"" << getImportName() << "\" from \"" << getModuleName()
+          << "\" as @" << getSymName();
+  if (getIsMutable())
+    printer << " mutable";
+  if (auto vis = getSymVisibility())
+    printer << " " << *vis;
+  printer << " : " << getType();
+}
+
+//===----------------------------------------------------------------------===//
+// IfOp
+//===----------------------------------------------------------------------===//
+
+Block *IfOp::getLabelTarget() { return getTarget(); }
+
+//===----------------------------------------------------------------------===//
+// LocalOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult LocalOp::inferReturnTypes(
+    MLIRContext *context, ::std::optional<Location> location,
+    ValueRange operands, DictionaryAttr attributes, OpaqueProperties properties,
+    RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
+  LocalOp::GenericAdaptor<ValueRange> adaptor{operands, attributes, properties,
+                                              regions};
+  auto type = adaptor.getTypeAttr();
+  if (!type)
+    return failure();
+  auto resType = LocalRefType::get(type.getContext(), type.getValue());
+  inferredReturnTypes.push_back(resType);
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// LocalGetOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult LocalGetOp::inferReturnTypes(
+    MLIRContext *context, ::std::optional<Location> location,
+    ValueRange operands, DictionaryAttr attributes, OpaqueProperties properties,
+    RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
+  return inferTeeGetResType(operands, inferredReturnTypes);
+}
+
+//===----------------------------------------------------------------------===//
+// LocalSetOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult LocalSetOp::verify() {
+  if (getLocalVar().getType().getElementType() != getValue().getType())
+    return emitError("input type and result type of local.set do not match");
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// LocalTeeOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult LocalTeeOp::inferReturnTypes(
+    MLIRContext *context, ::std::optional<Location> location,
+    ValueRange operands, DictionaryAttr attributes, OpaqueProperties properties,
+    RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
+  return inferTeeGetResType(operands, inferredReturnTypes);
+}
+
+LogicalResult LocalTeeOp::verify() {
+  if (getLocalVar().getType().getElementType() != getValue().getType() ||
+      getValue().getType() != getResult().getType())
+    return emitError("input type and output type of local.tee do not match");
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// LoopOp
+//===----------------------------------------------------------------------===//
+
+Block *LoopOp::getLabelTarget() { return &getBody().front(); }
+
+//===----------------------------------------------------------------------===//
+// MemOp
+//===----------------------------------------------------------------------===//
+
+void MemOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+                  StringRef symbol, LimitType limit) {
+  MemOp::build(odsBuilder, odsState, symbol, limit,
+               odsBuilder.getStringAttr("nested"));
+}
+
+//===----------------------------------------------------------------------===//
+// MemImportOp
+//===----------------------------------------------------------------------===//
+
+void MemImportOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+                        StringRef symbol, StringRef moduleName,
+                        StringRef importName, LimitType limits) {
+  MemImportOp::build(odsBuilder, odsState, symbol, moduleName, importName,
+                     limits, odsBuilder.getStringAttr("nested"));
+}
+
+//===----------------------------------------------------------------------===//
+// ReinterpretOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult ReinterpretOp::verify() {
+  auto inT = getInput().getType();
+  auto resT = getResult().getType();
+  if (inT == resT)
+    return emitError("reinterpret input and output type should be distinct");
+  if (inT.getIntOrFloatBitWidth() != resT.getIntOrFloatBitWidth())
+    return emitError() << "input type (" << inT << ") and output type (" << resT
+                       << ") have incompatible bit widths";
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
+// ReturnOp
+//===----------------------------------------------------------------------===//
+
+void ReturnOp::build(OpBuilder &odsBuilder, OperationState &odsState) {}
+
+//===----------------------------------------------------------------------===//
+// TableOp
+//===----------------------------------------------------------------------===//
+
+void TableOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+                    StringRef symbol, TableType type) {
+  TableOp::build(odsBuilder, odsState, symbol, type,
+                 odsBuilder.getStringAttr("nested"));
+}
+
+//===----------------------------------------------------------------------===//
+// TableImportOp
+//===----------------------------------------------------------------------===//
+
+void TableImportOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+                          StringRef symbol, StringRef moduleName,
+                          StringRef importName, TableType type) {
+  TableImportOp::build(odsBuilder, odsState, symbol, moduleName, importName,
+                       type, odsBuilder.getStringAttr("nested"));
+}

diff  --git a/mlir/lib/Dialect/WasmSSA/IR/WasmSSATypes.cpp b/mlir/lib/Dialect/WasmSSA/IR/WasmSSATypes.cpp
new file mode 100644
index 0000000000000..bee8c8167248d
--- /dev/null
+++ b/mlir/lib/Dialect/WasmSSA/IR/WasmSSATypes.cpp
@@ -0,0 +1,18 @@
+//===- WasmSSAOps.cpp - WasmSSA dialect operations ----------------===//
+//
+// 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/OpImplementation.h"
+#include "mlir/IR/Types.h"
+#include "llvm/Support/LogicalResult.h"
+
+#include <optional>
+
+namespace mlir::wasmssa {
+#include "mlir/Dialect/WasmSSA/IR/WasmSSATypeConstraints.cpp.inc"
+} // namespace mlir::wasmssa

diff  --git a/mlir/lib/RegisterAllDialects.cpp b/mlir/lib/RegisterAllDialects.cpp
index 7a345ed1349a7..950b85e2543bc 100644
--- a/mlir/lib/RegisterAllDialects.cpp
+++ b/mlir/lib/RegisterAllDialects.cpp
@@ -95,6 +95,7 @@
 #include "mlir/Dialect/Vector/IR/VectorOps.h"
 #include "mlir/Dialect/Vector/Transforms/BufferizableOpInterfaceImpl.h"
 #include "mlir/Dialect/Vector/Transforms/SubsetOpInterfaceImpl.h"
+#include "mlir/Dialect/WasmSSA/IR/WasmSSA.h"
 #include "mlir/Dialect/X86Vector/X86VectorDialect.h"
 #include "mlir/Dialect/XeGPU/IR/XeGPU.h"
 #include "mlir/IR/Dialect.h"
@@ -149,6 +150,7 @@ void mlir::registerAllDialects(DialectRegistry &registry) {
                   transform::TransformDialect,
                   ub::UBDialect,
                   vector::VectorDialect,
+                  wasmssa::WasmSSADialect,
                   x86vector::X86VectorDialect,
                   xegpu::XeGPUDialect,
                   xevm::XeVMDialect>();

diff  --git a/mlir/test/Dialect/WasmSSA/custom_parser/global.mlir b/mlir/test/Dialect/WasmSSA/custom_parser/global.mlir
new file mode 100644
index 0000000000000..b9b342052ff1b
--- /dev/null
+++ b/mlir/test/Dialect/WasmSSA/custom_parser/global.mlir
@@ -0,0 +1,44 @@
+// RUN: mlir-opt %s | FileCheck %s
+
+module {
+  wasmssa.import_global "from_js" from "env" as @global_0 nested : i32
+
+  wasmssa.global @global_1 i32 : {
+    %0 = wasmssa.const 10 : i32
+    wasmssa.return %0 : i32
+  }
+  wasmssa.global @global_2 i32 mutable : {
+    %0 = wasmssa.const 17 : i32
+    wasmssa.return %0 : i32
+  }
+  wasmssa.global @global_3 i32 mutable : {
+    %0 = wasmssa.const 10 : i32
+    wasmssa.return %0 : i32
+  }
+  wasmssa.global @global_4 i32 : {
+    %0 = wasmssa.global_get @global_0 : i32
+    wasmssa.return %0 : i32
+  }
+}
+
+// CHECK-LABEL:   wasmssa.import_global "from_js" from "env" as @global_0 nested : i32
+
+// CHECK-LABEL:   wasmssa.global @global_1 i32 : {
+// CHECK:           %[[VAL_0:.*]] = wasmssa.const 10 : i32
+// CHECK:           wasmssa.return %[[VAL_0]] : i32
+// CHECK:         }
+
+// CHECK-LABEL:   wasmssa.global @global_2 i32 mutable : {
+// CHECK:           %[[VAL_0:.*]] = wasmssa.const 17 : i32
+// CHECK:           wasmssa.return %[[VAL_0]] : i32
+// CHECK:         }
+
+// CHECK-LABEL:   wasmssa.global @global_3 i32 mutable : {
+// CHECK:           %[[VAL_0:.*]] = wasmssa.const 10 : i32
+// CHECK:           wasmssa.return %[[VAL_0]] : i32
+// CHECK:         }
+
+// CHECK-LABEL:   wasmssa.global @global_4 i32 : {
+// CHECK:           %[[VAL_0:.*]] = wasmssa.global_get @global_0 : i32
+// CHECK:           wasmssa.return %[[VAL_0]] : i32
+// CHECK:         }

diff  --git a/mlir/test/Dialect/WasmSSA/custom_parser/import.mlir b/mlir/test/Dialect/WasmSSA/custom_parser/import.mlir
new file mode 100644
index 0000000000000..3cc05486c4a4d
--- /dev/null
+++ b/mlir/test/Dialect/WasmSSA/custom_parser/import.mlir
@@ -0,0 +1,17 @@
+// RUN: mlir-opt %s | FileCheck %s
+
+module {
+  wasmssa.import_func "foo" from "my_module" as @func_0 {sym_visibility = "nested", type = (i32) -> ()}
+  wasmssa.import_func "bar" from "my_module" as @func_1 {sym_visibility = "nested", type = (i32) -> ()}
+  wasmssa.import_table "table" from "my_module" as @table_0 {sym_visibility = "nested", type = !wasmssa<tabletype !wasmssa.funcref [2:]>}
+  wasmssa.import_mem "mem" from "my_module" as @mem_0 {limits = !wasmssa<limit[2:]>, sym_visibility = "nested"}
+  wasmssa.import_global "glob" from "my_module" as @global_0 nested : i32
+  wasmssa.import_global "glob_mut" from "my_other_module" as @global_1 mutable nested : i32
+}
+
+// CHECK-LABEL:   wasmssa.import_func "foo" from "my_module" as @func_0 {sym_visibility = "nested", type = (i32) -> ()}
+// CHECK:         wasmssa.import_func "bar" from "my_module" as @func_1 {sym_visibility = "nested", type = (i32) -> ()}
+// CHECK:         wasmssa.import_table "table" from "my_module" as @table_0 {sym_visibility = "nested", type = !wasmssa<tabletype !wasmssa.funcref [2:]>}
+// CHECK:         wasmssa.import_mem "mem" from "my_module" as @mem_0 {limits = !wasmssa<limit[2:]>, sym_visibility = "nested"}
+// CHECK:         wasmssa.import_global "glob" from "my_module" as @global_0 nested : i32
+// CHECK:         wasmssa.import_global "glob_mut" from "my_other_module" as @global_1 mutable nested : i32

diff  --git a/mlir/test/Dialect/WasmSSA/custom_parser/local.mlir b/mlir/test/Dialect/WasmSSA/custom_parser/local.mlir
new file mode 100644
index 0000000000000..3f6423fa2a1f5
--- /dev/null
+++ b/mlir/test/Dialect/WasmSSA/custom_parser/local.mlir
@@ -0,0 +1,45 @@
+// RUN: mlir-opt %s | FileCheck %s
+
+module {
+  wasmssa.func nested @func_0() -> f32 {
+    %0 = wasmssa.local of type f32
+    %1 = wasmssa.local of type f32
+    %2 = wasmssa.const 8.000000e+00 : f32
+    %3 = wasmssa.const 1.200000e+01 : f32
+    %4 = wasmssa.add %2 %3 : f32
+    wasmssa.return %4 : f32
+  }
+  wasmssa.func nested @func_1() -> i32 {
+    %0 = wasmssa.local of type i32
+    %1 = wasmssa.local of type i32
+    %2 = wasmssa.const 8 : i32
+    %3 = wasmssa.const 12 : i32
+    %4 = wasmssa.add %2 %3 : i32
+    wasmssa.return %4 : i32
+  }
+  wasmssa.func nested @func_2(%arg0: !wasmssa<local ref to i32>) -> i32 {
+    %0 = wasmssa.const 3 : i32
+    wasmssa.return %0 : i32
+  }
+}
+
+// CHECK-LABEL:   wasmssa.func nested @func_0() -> f32 {
+// CHECK:           %[[VAL_0:.*]] = wasmssa.local of type f32
+// CHECK:           %[[VAL_1:.*]] = wasmssa.local of type f32
+// CHECK:           %[[VAL_2:.*]] = wasmssa.const 8.000000e+00 : f32
+// CHECK:           %[[VAL_3:.*]] = wasmssa.const 1.200000e+01 : f32
+// CHECK:           %[[VAL_4:.*]] = wasmssa.add %[[VAL_2]] %[[VAL_3]] : f32
+// CHECK:           wasmssa.return %[[VAL_4]] : f32
+
+// CHECK-LABEL:   wasmssa.func nested @func_1() -> i32 {
+// CHECK:           %[[VAL_0:.*]] = wasmssa.local of type i32
+// CHECK:           %[[VAL_1:.*]] = wasmssa.local of type i32
+// CHECK:           %[[VAL_2:.*]] = wasmssa.const 8 : i32
+// CHECK:           %[[VAL_3:.*]] = wasmssa.const 12 : i32
+// CHECK:           %[[VAL_4:.*]] = wasmssa.add %[[VAL_2]] %[[VAL_3]] : i32
+// CHECK:           wasmssa.return %[[VAL_4]] : i32
+
+// CHECK-LABEL:   wasmssa.func nested @func_2(
+// CHECK-SAME:      %[[ARG0:.*]]: !wasmssa<local ref to i32>) -> i32 {
+// CHECK:           %[[VAL_0:.*]] = wasmssa.const 3 : i32
+// CHECK:           wasmssa.return %[[VAL_0]] : i32

diff  --git a/mlir/test/Dialect/WasmSSA/extend-invalid.mlir b/mlir/test/Dialect/WasmSSA/extend-invalid.mlir
new file mode 100644
index 0000000000000..8d782801c33f9
--- /dev/null
+++ b/mlir/test/Dialect/WasmSSA/extend-invalid.mlir
@@ -0,0 +1,18 @@
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics
+
+
+wasmssa.func nested @extend_low_64() -> i32 {
+  %0 = wasmssa.const 10 : i32
+  // expected-error at +1 {{extend op can only take 8, 16 or 32 bits. Got 64}}
+  %1 = wasmssa.extend 64 low bits from %0: i32
+  wasmssa.return %1 : i32
+}
+
+// -----
+
+wasmssa.func nested @extend_too_much() -> i32 {
+  %0 = wasmssa.const 10 : i32
+  // expected-error at +1 {{trying to extend the 32 low bits from a 'i32' value is illegal}}
+  %1 = wasmssa.extend 32 low bits from %0: i32
+  wasmssa.return %1 : i32
+}

diff  --git a/mlir/test/Dialect/WasmSSA/global-invalid.mlir b/mlir/test/Dialect/WasmSSA/global-invalid.mlir
new file mode 100644
index 0000000000000..b9cafd8b900bf
--- /dev/null
+++ b/mlir/test/Dialect/WasmSSA/global-invalid.mlir
@@ -0,0 +1,32 @@
+// RUN: mlir-opt %s -verify-diagnostics --split-input-file
+
+module {
+  // expected-error at +1 {{expected a constant initializer for this operator}}
+  wasmssa.global @illegal i32 mutable : {
+    %0 = wasmssa.const 17: i32
+    %1 = wasmssa.const 35: i32
+    %2 = wasmssa.add %0 %1 : i32
+    wasmssa.return %2 : i32
+  }
+}
+
+// -----
+
+module {
+  wasmssa.import_global "glob" from "my_module" as @global_0 mutable nested : i32
+  wasmssa.global @global_1 i32 : {
+  // expected-error at +1 {{global.get op is considered constant if it's referring to a import.global symbol marked non-mutable}}
+    %0 = wasmssa.global_get @global_0 : i32
+    wasmssa.return %0 : i32
+  }
+}
+
+// -----
+
+module {
+  wasmssa.global @global_1 i32 : {
+  // expected-error at +1 {{symbol @glarble is undefined}}
+    %0 = wasmssa.global_get @glarble : i32
+    wasmssa.return %0 : i32
+  }
+}

diff  --git a/mlir/test/Dialect/WasmSSA/locals-invalid.mlir b/mlir/test/Dialect/WasmSSA/locals-invalid.mlir
new file mode 100644
index 0000000000000..35c590b36b289
--- /dev/null
+++ b/mlir/test/Dialect/WasmSSA/locals-invalid.mlir
@@ -0,0 +1,17 @@
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics
+
+wasmssa.func nested @local_set_err(%arg0: !wasmssa<local ref to i32>) -> i64 {
+  %0 = wasmssa.const 3 : i64
+  // expected-error at +1 {{input type and result type of local.set do not match}}
+  wasmssa.local_set %arg0 : ref to i32 to %0 : i64
+  wasmssa.return %0 : i64
+}
+
+// -----
+
+wasmssa.func nested @local_tee_err(%arg0: !wasmssa<local ref to i32>) -> i32 {
+  %0 = wasmssa.const 3 : i64
+  // expected-error at +1 {{input type and output type of local.tee do not match}}
+  %1 = wasmssa.local_tee %arg0 :  ref to i32 to %0 : i64
+  wasmssa.return %1 : i32
+}

diff  --git a/mlir/test/Dialect/WasmSSA/reinterpret-invalid.mlir b/mlir/test/Dialect/WasmSSA/reinterpret-invalid.mlir
new file mode 100644
index 0000000000000..1c6371b4875ae
--- /dev/null
+++ b/mlir/test/Dialect/WasmSSA/reinterpret-invalid.mlir
@@ -0,0 +1,17 @@
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics
+
+wasmssa.func @f32_reinterpret_f32() -> f32 {
+  %0 = wasmssa.const -1.000000e+00 : f32
+  // expected-error at +1 {{reinterpret input and output type should be distinct}}
+  %1 = wasmssa.reinterpret %0 : f32 as f32
+  wasmssa.return %1 : f32
+}
+
+// -----
+
+wasmssa.func @f64_reinterpret_f32() -> f32 {
+  %0 = wasmssa.const -1.000000e+00 : f64
+  // expected-error at +1 {{input type ('f64') and output type ('f32') have incompatible bit widths}}
+  %1 = wasmssa.reinterpret %0 : f64 as f32
+  wasmssa.return %1 : f32
+}


        


More information about the Mlir-commits mailing list