[Mlir-commits] [mlir] c93bad6 - [mlir][wasmssa] Fix if/else round-trip and verify return types (#227828)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sat Oct 3 20:23:29 PDT 2026
Author: Saqib Khan
Date: 2026-10-04T03:23:20Z
New Revision: c93bad65aa654f507ea768610d003c42ba31ab7b
URL: https://github.com/llvm/llvm-project/commit/c93bad65aa654f507ea768610d003c42ba31ab7b
DIFF: https://github.com/llvm/llvm-project/commit/c93bad65aa654f507ea768610d003c42ba31ab7b.diff
LOG: [mlir][wasmssa] Fix if/else round-trip and verify return types (#227828)
Fixes two WasmSSA dialect bugs:
**#226072: `wasmssa.if` does not round-trip.** The else region was
printed with `printKeywordOrString("else ")`. Because of the trailing
space, `"else "` is not a valid bare keyword, so it was printed as a
quoted string (`} "else "{`), which `parseElseRegion` does not accept.
The else keyword is now printed directly, giving `} else {`.
**#226073: `wasmssa.return` is not checked against the function's
results.** `wasmssa.return` had no verifier, so a function could return
a different number or type of values than its signature declares. This
adds a `ReturnOp` verifier that checks the operands against the
enclosing `wasmssa.func`. Checking in the return op rather than in
`FuncOp::verifyBody` also covers returns nested inside
`block`/`loop`/`if` regions.
Tests:
- `custom_parser/if.mlir` expected the broken `"else "` output; it now
expects `} else {` and gains a second RUN line piping through `mlir-opt`
again to check the round trip. Its `@func_0` returned `f32` from an
`i32` function, which the new verifier rejects, so the signature is now
`-> f32`.
- `Target/Wasm/if.mlir` also expected the quoted `"else "` and is
updated. The Wasm importer's output passes the new verifier.
- New `return-invalid.mlir` covers type mismatch, too few / too many
operands, and a mismatched return inside a nested `if` region.
`llvm-lit mlir/test/Dialect/WasmSSA mlir/test/Target/Wasm
mlir/test/Conversion/RaiseWasm`: 112/112 pass. The new and updated
dialect tests fail without the fix.
Assisted-by: Claude Code (Claude Opus 5.5)
Co-authored-by: Ferdinand Lemaire <flscminecraft at gmail.com>
Added:
mlir/test/Dialect/WasmSSA/return-invalid.mlir
Modified:
mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
mlir/test/Dialect/WasmSSA/custom_parser/if.mlir
mlir/test/Target/Wasm/if.mlir
Removed:
################################################################################
diff --git a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
index 2df567c393e59..050877fb78507 100644
--- a/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
+++ b/mlir/include/mlir/Dialect/WasmSSA/IR/WasmSSAOps.td
@@ -685,6 +685,7 @@ def WasmSSA_ReturnOp : WasmSSA_Op<"return", [Terminator]> {
let builders = [
OpBuilder<(ins)>
];
+ let hasVerifier = 1;
}
def WasmSSA_SelectOp : WasmSSA_Op<"select", [
diff --git a/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
index e0e4b085bc21e..e04201863b107 100644
--- a/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
+++ b/mlir/lib/Dialect/WasmSSA/IR/WasmSSAOps.cpp
@@ -37,7 +37,7 @@ void printElseRegion(OpAsmPrinter &opPrinter, Operation *op,
Region &elseRegion) {
if (elseRegion.empty())
return;
- opPrinter.printKeywordOrString("else ");
+ opPrinter << "else ";
opPrinter.printRegion(elseRegion);
}
} // namespace
@@ -493,3 +493,21 @@ LogicalResult ReinterpretOp::verify() {
//===----------------------------------------------------------------------===//
void ReturnOp::build(OpBuilder &odsBuilder, OperationState &odsState) {}
+
+LogicalResult ReturnOp::verify() {
+ auto funcOp = (*this)->getParentOfType<FuncOp>();
+ if (!funcOp)
+ return success();
+ ArrayRef<Type> resultTypes = funcOp.getFunctionType().getResults();
+ if (getOperands().size() != resultTypes.size())
+ return emitOpError("has ") << getOperands().size()
+ << " operands, but enclosing function returns "
+ << resultTypes.size();
+ for (auto [idx, resultType, operandType] :
+ llvm::enumerate(resultTypes, getOperands().getTypes()))
+ if (resultType != operandType)
+ return emitOpError("type of return operand #")
+ << idx << " (" << operandType
+ << ") doesn't match function result type (" << resultType << ")";
+ return success();
+}
diff --git a/mlir/test/Dialect/WasmSSA/custom_parser/if.mlir b/mlir/test/Dialect/WasmSSA/custom_parser/if.mlir
index cee3c695af2f0..e0f39651a5728 100644
--- a/mlir/test/Dialect/WasmSSA/custom_parser/if.mlir
+++ b/mlir/test/Dialect/WasmSSA/custom_parser/if.mlir
@@ -1,18 +1,19 @@
// RUN: mlir-opt %s | FileCheck %s
+// RUN: mlir-opt %s | mlir-opt | FileCheck %s
// CHECK-LABEL: wasmssa.func @func_0(
-// CHECK-SAME: %[[ARG0:.*]]: !wasmssa<local ref to i32>) -> i32 {
+// CHECK-SAME: %[[ARG0:.*]]: !wasmssa<local ref to i32>) -> f32 {
// CHECK: %[[VAL_0:.*]] = wasmssa.local_get %[[ARG0]] : ref to i32
// CHECK: wasmssa.if %[[VAL_0]] : {
// CHECK: %[[VAL_1:.*]] = wasmssa.const 5.000000e-01 : f32
// CHECK: wasmssa.block_return %[[VAL_1]] : f32
-// CHECK: } "else "{
+// CHECK: } else {
// CHECK: %[[VAL_2:.*]] = wasmssa.const 2.500000e-01 : f32
// CHECK: wasmssa.block_return %[[VAL_2]] : f32
// CHECK: }> ^bb1
// CHECK: ^bb1(%[[VAL_3:.*]]: f32):
// CHECK: wasmssa.return %[[VAL_3]] : f32
-wasmssa.func @func_0(%arg0 : !wasmssa<local ref to i32>) -> i32 {
+wasmssa.func @func_0(%arg0 : !wasmssa<local ref to i32>) -> f32 {
%cond = wasmssa.local_get %arg0 : ref to i32
wasmssa.if %cond : {
%c0 = wasmssa.const 0.5 : f32
diff --git a/mlir/test/Dialect/WasmSSA/return-invalid.mlir b/mlir/test/Dialect/WasmSSA/return-invalid.mlir
new file mode 100644
index 0000000000000..6d0c673ce35a8
--- /dev/null
+++ b/mlir/test/Dialect/WasmSSA/return-invalid.mlir
@@ -0,0 +1,36 @@
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics
+
+wasmssa.func @return_type_mismatch() -> i32 {
+ %0 = wasmssa.const 0.5 : f32
+ // expected-error at +1 {{type of return operand #0 ('f32') doesn't match function result type ('i32')}}
+ wasmssa.return %0 : f32
+}
+
+// -----
+
+wasmssa.func @return_too_few_operands() -> i32 {
+ // expected-error at +1 {{has 0 operands, but enclosing function returns 1}}
+ wasmssa.return
+}
+
+// -----
+
+wasmssa.func @return_too_many_operands() {
+ %0 = wasmssa.const 1 : i32
+ // expected-error at +1 {{has 1 operands, but enclosing function returns 0}}
+ wasmssa.return %0 : i32
+}
+
+// -----
+
+wasmssa.func @return_in_nested_region(%arg0 : !wasmssa<local ref to i32>) -> i64 {
+ %cond = wasmssa.local_get %arg0 : ref to i32
+ wasmssa.if %cond : {
+ %c0 = wasmssa.const 1 : i32
+ // expected-error at +1 {{type of return operand #0 ('i32') doesn't match function result type ('i64')}}
+ wasmssa.return %c0 : i32
+ } >^bb1
+ ^bb1:
+ %c1 = wasmssa.const 2 : i64
+ wasmssa.return %c1 : i64
+}
diff --git a/mlir/test/Target/Wasm/if.mlir b/mlir/test/Target/Wasm/if.mlir
index 2d7bfbed6f754..a7acc3d836e64 100644
--- a/mlir/test/Target/Wasm/if.mlir
+++ b/mlir/test/Target/Wasm/if.mlir
@@ -62,7 +62,7 @@
// CHECK: %[[VAL_6:.*]] = wasmssa.const 1 : i32
// CHECK: %[[VAL_7:.*]] = wasmssa.add %[[VAL_5]] %[[VAL_6]] : i32
// CHECK: wasmssa.block_return %[[VAL_7]] : i32
-// CHECK: } "else "{
+// CHECK: } else {
// CHECK: %[[VAL_8:.*]] = wasmssa.local_get %[[ARG0]] : ref to i32
// CHECK: %[[VAL_9:.*]] = wasmssa.const 1 : i32
// CHECK: %[[VAL_10:.*]] = wasmssa.shr_u %[[VAL_8]] by %[[VAL_9]] bits : i32
@@ -104,7 +104,7 @@
// CHECK: } > ^bb1
// CHECK: ^bb1(%[[VAL_10:.*]]: i32):
// CHECK: wasmssa.block_return %[[VAL_10]] : i32
-// CHECK: } "else "{
+// CHECK: } else {
// CHECK: %[[VAL_11:.*]] = wasmssa.const 1 : i32
// CHECK: wasmssa.block_return %[[VAL_11]] : i32
// CHECK: }> ^bb1
More information about the Mlir-commits
mailing list