[Mlir-commits] [mlir] [MLIR][WasmSSA] Add global_set and select ops (PR #196613)

Byeongjee Kang llvmlistbot at llvm.org
Thu Jul 23 07:45:00 PDT 2026


https://github.com/byeongjee updated https://github.com/llvm/llvm-project/pull/196613

>From 4eb1075004eafe159cdb9b436cb5a4fb9a2a33b4 Mon Sep 17 00:00:00 2001
From: Byeongjee Kang <byeongjee.kang at gmail.com>
Date: Fri, 8 May 2026 00:20:59 -0400
Subject: [PATCH 1/2] [MLIR][WasmSSA] Add global_set op

Adds the `wasmssa.global_set` operation, which writes a value to a
mutable global variable. The op verifies that the referenced symbol is
a `wasmssa.global` or `wasmssa.import_global` marked mutable, and that
the operand type matches the global's declared type.
---
 .../mlir/Dialect/WasmSSA/IR/WasmSSAOps.td     | 19 +++++++
 mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp    | 38 +++++++++++++
 .../Dialect/WasmSSA/custom_parser/global.mlir | 12 ++++
 mlir/test/Dialect/WasmSSA/global-invalid.mlir | 55 +++++++++++++++++++
 4 files changed, 124 insertions(+)

diff --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
index bfa0953f50ac8..e2fb642433266 100644
--- a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
@@ -381,6 +381,25 @@ def WasmSSA_GlobalGetOp : WasmSSA_Op<"global_get", [DeclareOpInterfaceMethods<Sy
   let assemblyFormat = "$global attr-dict `:` type($global_val)";
 }
 
+def WasmSSA_GlobalSetOp : WasmSSA_Op<"global_set", [
+    DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
+  let summary = "Sets the value of the mutable global passed as argument.";
+  let description = [{
+     Sets the value of the mutable global referenced by the symbol to the given
+     value.
+
+     Example:
+
+     ```mlir
+     // Sets the value of `@global_0` to `%v`.
+     wasmssa.global_set @global_0 to %v : i32
+     ```
+    }];
+  let arguments = (ins FlatSymbolRefAttr: $global,
+                       WasmSSA_ValType: $value);
+  let assemblyFormat = "$global `to` $value `:` type($value) attr-dict";
+}
+
 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.";
diff --git a/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
index 08de887a56a97..eab9c7a9d53a7 100644
--- a/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
+++ b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
@@ -331,6 +331,44 @@ GlobalGetOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// GlobalSetOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+GlobalSetOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+  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";
+
+  Type globalType;
+  bool isMutable = false;
+  if (auto global = dyn_cast<GlobalOp>(definitionOp)) {
+    globalType = global.getType();
+    isMutable = global.getIsMutable();
+  } else if (auto globalImport = dyn_cast<GlobalImportOp>(definitionOp)) {
+    globalType = globalImport.getType();
+    isMutable = globalImport.getIsMutable();
+  } else {
+    return emitError() << "symbol @" << referencedSymbol
+                       << " is not a global symbol";
+  }
+
+  if (!isMutable)
+    return emitError("global.set target must be mutable");
+
+  Type valueType = getValue().getType();
+  if (globalType != valueType)
+    return emitError("global.set value type does not match target global "
+                     "type: expected ")
+           << globalType << " but got " << valueType;
+
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // GlobalImportOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/WasmSSA/custom_parser/global.mlir b/mlir/test/Dialect/WasmSSA/custom_parser/global.mlir
index a25abbd2a8662..6441731a0a335 100644
--- a/mlir/test/Dialect/WasmSSA/custom_parser/global.mlir
+++ b/mlir/test/Dialect/WasmSSA/custom_parser/global.mlir
@@ -19,6 +19,12 @@ module {
     %0 = wasmssa.global_get @global_0 : i32
     wasmssa.return %0 : i32
   }
+
+  wasmssa.func @set_global() {
+    %0 = wasmssa.const 7 : i32
+    wasmssa.global_set @global_2 to %0 : i32
+    wasmssa.return
+  }
 }
 
 // CHECK-LABEL:   wasmssa.import_global "from_js" from "env" as @global_0 : i32
@@ -42,3 +48,9 @@ module {
 // CHECK:           %[[VAL_0:.*]] = wasmssa.global_get @global_0 : i32
 // CHECK:           wasmssa.return %[[VAL_0]] : i32
 // CHECK:         }
+
+// CHECK-LABEL:   wasmssa.func @set_global() {
+// CHECK:           %[[VAL_0:.*]] = wasmssa.const 7 : i32
+// CHECK:           wasmssa.global_set @global_2 to %[[VAL_0]] : i32
+// CHECK:           wasmssa.return
+// CHECK:         }
diff --git a/mlir/test/Dialect/WasmSSA/global-invalid.mlir b/mlir/test/Dialect/WasmSSA/global-invalid.mlir
index c5bc606fd13f3..600b2f0715fdc 100644
--- a/mlir/test/Dialect/WasmSSA/global-invalid.mlir
+++ b/mlir/test/Dialect/WasmSSA/global-invalid.mlir
@@ -40,3 +40,58 @@ module {
     wasmssa.return %0 : i32
   }
 }
+
+// -----
+
+module {
+  wasmssa.func @f() {
+    %0 = wasmssa.const 1 : i32
+    // expected-error at +1 {{symbol @missing is undefined}}
+    wasmssa.global_set @missing to %0 : i32
+    wasmssa.return
+  }
+}
+
+// -----
+
+module {
+  wasmssa.global @global_0 i32 : {
+    %0 = wasmssa.const 0 : i32
+    wasmssa.return %0 : i32
+  }
+  wasmssa.func @f() {
+    %0 = wasmssa.const 1 : i32
+    // expected-error at +1 {{global.set target must be mutable}}
+    wasmssa.global_set @global_0 to %0 : i32
+    wasmssa.return
+  }
+}
+
+// -----
+
+module {
+  wasmssa.global @global_0 i64 mutable : {
+    %0 = wasmssa.const 0 : i64
+    wasmssa.return %0 : i64
+  }
+  wasmssa.func @f() {
+    %0 = wasmssa.const 1 : i32
+    // expected-error at +1 {{global.set value type does not match target global type: expected 'i64' but got 'i32'}}
+    wasmssa.global_set @global_0 to %0 : i32
+    wasmssa.return
+  }
+}
+
+// -----
+
+module {
+  wasmssa.func @not_global() {
+    wasmssa.return
+  }
+  wasmssa.func @f() {
+    %0 = wasmssa.const 1 : i32
+    // expected-error at +1 {{symbol @not_global is not a global symbol}}
+    wasmssa.global_set @not_global to %0 : i32
+    wasmssa.return
+  }
+}

>From c9c103386c84a4756f84bbf3a60cbfb07076b541 Mon Sep 17 00:00:00 2001
From: Byeongjee Kang <byeongjee.kang at gmail.com>
Date: Fri, 8 May 2026 15:05:35 -0400
Subject: [PATCH 2/2] [MLIR][WasmSSA] Add select op

Adds the `wasmssa.select` operation, a Wasm value-typed ternary that
returns one of two operands based on an i32 condition. The op uses the
`AllTypesMatch` trait to require both branches and the result share the
same Wasm value type, and is marked `Pure` since it has no side effects.

Also adds the `SideEffectInterfaces` td/header includes needed by the
`Pure` trait.
---
 .../include/mlir/Dialect/WasmSSA/IR/WasmSSA.h |  1 +
 .../mlir/Dialect/WasmSSA/IR/WasmSSAOps.td     | 24 ++++++++++++
 .../Dialect/WasmSSA/custom_parser/select.mlir | 37 +++++++++++++++++++
 3 files changed, 62 insertions(+)
 create mode 100644 mlir/test/Dialect/WasmSSA/custom_parser/select.mlir

diff --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSA.h b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSA.h
index 64391d807c633..64d480c065b79 100644
--- a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSA.h
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSA.h
@@ -38,6 +38,7 @@
 #include "mlir/Interfaces/CallInterfaces.h"
 #include "mlir/Interfaces/FunctionInterfaces.h"
 #include "mlir/Interfaces/InferTypeOpInterface.h"
+#include "mlir/Interfaces/SideEffectInterfaces.h"
 
 //===----------------------------------------------------------------------===//
 // WebAssembly Constraints
diff --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
index e2fb642433266..3b3c3e8825fef 100644
--- a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
@@ -16,6 +16,7 @@ include "mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.td"
 
 include "mlir/Interfaces/FunctionInterfaces.td"
 include "mlir/Interfaces/InferTypeOpInterface.td"
+include "mlir/Interfaces/SideEffectInterfaces.td"
 include "mlir/IR/BuiltinAttributeInterfaces.td"
 include "mlir/IR/SymbolInterfaces.td"
 
@@ -628,6 +629,29 @@ def WasmSSA_ReturnOp : WasmSSA_Op<"return", [Terminator]> {
   ];
 }
 
+def WasmSSA_SelectOp : WasmSSA_Op<"select", [
+    AllTypesMatch<["true_value", "false_value", "result"]>,
+    Pure]> {
+  let summary = "Select between two values based on a condition.";
+  let description = [{
+     Returns `true_value` if `condition` is non-zero, `false_value` otherwise.
+     Both operands and the result must share the same Wasm value type.
+
+     Example:
+
+     ```mlir
+     // Returns %a if %cond is non-zero, otherwise %b.
+     %r = wasmssa.select %cond, %a, %b : i32
+     ```
+    }];
+  let arguments = (ins WasmSSA_ValType: $true_value,
+                       WasmSSA_ValType: $false_value,
+                       I32: $condition);
+  let results = (outs WasmSSA_ValType: $result);
+  let assemblyFormat =
+      "$condition `,` $true_value `,` $false_value attr-dict `:` type($result)";
+}
+
 // ---- Numeric ops
 
 class WasmSSA_BinaryNumericalOp<string mnemonic, string summaryStr, string descStr,
diff --git a/mlir/test/Dialect/WasmSSA/custom_parser/select.mlir b/mlir/test/Dialect/WasmSSA/custom_parser/select.mlir
new file mode 100644
index 0000000000000..80ced11c01bc3
--- /dev/null
+++ b/mlir/test/Dialect/WasmSSA/custom_parser/select.mlir
@@ -0,0 +1,37 @@
+// RUN: mlir-opt %s | FileCheck %s
+
+module {
+  wasmssa.func @select_i32(%cond: !wasmssa<local ref to i32>,
+                           %a: !wasmssa<local ref to i32>,
+                           %b: !wasmssa<local ref to i32>) -> i32 {
+    %0 = wasmssa.local_get %cond : ref to i32
+    %1 = wasmssa.local_get %a : ref to i32
+    %2 = wasmssa.local_get %b : ref to i32
+    %r = wasmssa.select %0, %1, %2 : i32
+    wasmssa.return %r : i32
+  }
+
+  wasmssa.func @select_f64(%cond: !wasmssa<local ref to i32>,
+                           %a: !wasmssa<local ref to f64>,
+                           %b: !wasmssa<local ref to f64>) -> f64 {
+    %0 = wasmssa.local_get %cond : ref to i32
+    %1 = wasmssa.local_get %a : ref to f64
+    %2 = wasmssa.local_get %b : ref to f64
+    %r = wasmssa.select %0, %1, %2 : f64
+    wasmssa.return %r : f64
+  }
+}
+
+// CHECK-LABEL:   wasmssa.func @select_i32(
+// CHECK:           %[[COND:.*]] = wasmssa.local_get
+// CHECK:           %[[A:.*]] = wasmssa.local_get
+// CHECK:           %[[B:.*]] = wasmssa.local_get
+// CHECK:           %[[R:.*]] = wasmssa.select %[[COND]], %[[A]], %[[B]] : i32
+// CHECK:           wasmssa.return %[[R]] : i32
+
+// CHECK-LABEL:   wasmssa.func @select_f64(
+// CHECK:           %[[COND:.*]] = wasmssa.local_get
+// CHECK:           %[[A:.*]] = wasmssa.local_get
+// CHECK:           %[[B:.*]] = wasmssa.local_get
+// CHECK:           %[[R:.*]] = wasmssa.select %[[COND]], %[[A]], %[[B]] : f64
+// CHECK:           wasmssa.return %[[R]] : f64



More information about the Mlir-commits mailing list