[Mlir-commits] [mlir] [MLIR][WASM] Introduce full support for raising WASM MLIR to other dialects (PR #205990)
Ferdinand Lemaire
llvmlistbot at llvm.org
Wed Jul 22 03:58:48 PDT 2026
https://github.com/flemairen6 updated https://github.com/llvm/llvm-project/pull/205990
>From f294988d80afcf50d52102adf91aa52dd2e9199e Mon Sep 17 00:00:00 2001
From: Ferdinand Lemaire <ferdinand.lemaire at woven-planet.global>
Date: Thu, 3 Jul 2025 12:48:24 +0900
Subject: [PATCH 1/7] [mlir][wasm] RaiseWasmMLIRPass: Add suport for comparison
operators
--
Co-authored-by: Luc Forget <luc.forget at woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette at woven-planet.global>
---
.../Conversion/RaiseWasm/RaiseWasmMLIR.cpp | 139 ++++++
.../wasm-comparisons-to-arith-cmp.mlir | 421 ++++++++++++++++++
.../RaiseWasm/wasm-eqz-to-arith-cmp.mlir | 28 ++
3 files changed, 588 insertions(+)
create mode 100644 mlir/test/Conversion/RaiseWasm/wasm-comparisons-to-arith-cmp.mlir
create mode 100644 mlir/test/Conversion/RaiseWasm/wasm-eqz-to-arith-cmp.mlir
diff --git a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
index 83bfde7032ef8..1fb4564032421 100644
--- a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
+++ b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
@@ -124,6 +124,104 @@ using WasmTruncOpConversion = OpMappingConversion<TruncOp, math::TruncOp>;
using WasmSqrtOpConversion = OpMappingConversion<SqrtOp, math::SqrtOp>;
using WasmWrapOpConversion = OpMappingConversion<WrapOp, arith::TruncIOp>;
+template <typename SourceOp, typename TargetOp, typename AttrType,
+ typename ValType, ValType flag>
+struct ComparisonOpConversion : OpConversionPattern<SourceOp> {
+ using OpConversionPattern<SourceOp>::OpConversionPattern;
+
+ LogicalResult
+ matchAndRewrite(SourceOp srcOp, typename SourceOp::Adaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ auto cmpRes =
+ rewriter
+ .create<TargetOp>(srcOp.getLoc(), rewriter.getI1Type(),
+ AttrType::get(rewriter.getContext(), flag),
+ adaptor.getLhs(), adaptor.getRhs())
+ .getResult();
+ rewriter.replaceOpWithNewOp<arith::ExtUIOp>(srcOp, rewriter.getI32Type(),
+ cmpRes);
+
+ return success();
+ }
+};
+
+template <typename SourceOp, arith::CmpFPredicate compFlag>
+using FPComparisonConversion =
+ ComparisonOpConversion<SourceOp, arith::CmpFOp, arith::CmpFPredicateAttr,
+ arith::CmpFPredicate, compFlag>;
+
+template <typename SourceOp, arith::CmpIPredicate compFlag>
+using IntComparisonConversion =
+ ComparisonOpConversion<SourceOp, arith::CmpIOp, arith::CmpIPredicateAttr,
+ arith::CmpIPredicate, compFlag>;
+
+using WasmLtSIOpConversion =
+ IntComparisonConversion<LtSIOp, arith::CmpIPredicate::slt>;
+using WasmLeSIOpConversion =
+ IntComparisonConversion<LeSIOp, arith::CmpIPredicate::sle>;
+using WasmGtSIOpConversion =
+ IntComparisonConversion<GtSIOp, arith::CmpIPredicate::sgt>;
+using WasmGeSIOpConversion =
+ IntComparisonConversion<GeSIOp, arith::CmpIPredicate::sge>;
+using WasmLtUIOpConversion =
+ IntComparisonConversion<LtUIOp, arith::CmpIPredicate::ult>;
+using WasmLeUIOpConversion =
+ IntComparisonConversion<LeUIOp, arith::CmpIPredicate::ule>;
+using WasmGtUIOpConversion =
+ IntComparisonConversion<GtUIOp, arith::CmpIPredicate::ugt>;
+using WasmGeUIOpConversion =
+ IntComparisonConversion<GeUIOp, arith::CmpIPredicate::uge>;
+using WasmLtOpConversion =
+ FPComparisonConversion<LtOp, arith::CmpFPredicate::OLT>;
+using WasmLeOpConversion =
+ FPComparisonConversion<LeOp, arith::CmpFPredicate::OLE>;
+using WasmGtOpConversion =
+ FPComparisonConversion<GtOp, arith::CmpFPredicate::OGT>;
+using WasmGeOpConversion =
+ FPComparisonConversion<GeOp, arith::CmpFPredicate::OGE>;
+
+template <typename SourceOp, arith::CmpIPredicate IntFlag,
+ arith::CmpFPredicate FloatFlag>
+struct IntFpComparisonOpConversion : OpConversionPattern<SourceOp> {
+ using OpConversionPattern<SourceOp>::OpConversionPattern;
+
+ LogicalResult
+ matchAndRewrite(SourceOp srcOp, typename SourceOp::Adaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ Value comparisonResult;
+ if (srcOp.getLhs().getType().isInteger())
+ comparisonResult =
+ rewriter
+ .create<arith::CmpIOp>(
+ srcOp.getLoc(), rewriter.getI1Type(),
+ arith::CmpIPredicateAttr::get(rewriter.getContext(), IntFlag),
+ adaptor.getLhs(), adaptor.getRhs())
+ .getResult();
+ else if (srcOp.getLhs().getType().isFloat())
+ comparisonResult =
+ rewriter
+ .create<arith::CmpFOp>(srcOp.getLoc(), rewriter.getI1Type(),
+ arith::CmpFPredicateAttr::get(
+ rewriter.getContext(), FloatFlag),
+ adaptor.getLhs(), adaptor.getRhs())
+ .getResult();
+ else
+ return rewriter.notifyMatchFailure(
+ srcOp.getLoc(), "Unsupported datatype for comparison OP.");
+
+ rewriter.replaceOpWithNewOp<arith::ExtUIOp>(srcOp, rewriter.getI32Type(),
+ comparisonResult);
+ return success();
+ }
+};
+
+using WasmEqOpConversion =
+ IntFpComparisonOpConversion<EqOp, arith::CmpIPredicate::eq,
+ arith::CmpFPredicate::OEQ>;
+using WasmNeOpConversion =
+ IntFpComparisonOpConversion<NeOp, arith::CmpIPredicate::ne,
+ arith::CmpFPredicate::ONE>;
+
struct WasmCallOpConversion : OpConversionPattern<FuncCallOp> {
using OpConversionPattern::OpConversionPattern;
@@ -148,6 +246,32 @@ struct WasmConstOpConversion : OpConversionPattern<ConstOp> {
}
};
+struct WasmEqzOpConversion : OpConversionPattern<EqzOp> {
+ using OpConversionPattern::OpConversionPattern;
+
+ LogicalResult
+ matchAndRewrite(EqzOp eqzOp, EqzOp::Adaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ auto loc = eqzOp->getLoc();
+ auto zero =
+ rewriter
+ .create<arith::ConstantOp>(
+ loc, rewriter.getIntegerAttr(adaptor.getInput().getType(), 0))
+ .getResult();
+ auto cmpRes = rewriter
+ .create<arith::CmpIOp>(
+ loc, rewriter.getI1Type(),
+ arith::CmpIPredicateAttr::get(
+ rewriter.getContext(), arith::CmpIPredicate::eq),
+ adaptor.getInput(), zero)
+ .getResult();
+ rewriter.replaceOpWithNewOp<arith::ExtUIOp>(eqzOp, rewriter.getI32Type(),
+ cmpRes);
+
+ return success();
+ }
+};
+
struct WasmFuncImportOpConversion : OpConversionPattern<FuncImportOp> {
using OpConversionPattern::OpConversionPattern;
@@ -429,21 +553,36 @@ void mlir::populateRaiseWasmMLIRConversionPatterns(
WasmDivFPOpConversion,
WasmDivSIOpConversion,
WasmDivUIOpConversion,
+ WasmEqOpConversion,
+ WasmEqzOpConversion,
WasmExtendSOpConversion,
WasmExtendUOpConversion,
WasmFloorOpConversion,
WasmFuncImportOpConversion,
WasmFuncOpConversion,
+ WasmGeOpConversion,
+ WasmGeSIOpConversion,
+ WasmGeUIOpConversion,
WasmGlobalImportOpConverter,
WasmGlobalWithConstInitConversion,
WasmGlobalWithGetGlobalInitConversion,
+ WasmGtOpConversion,
+ WasmGtSIOpConversion,
+ WasmGtUIOpConversion,
+ WasmLeOpConversion,
+ WasmLeSIOpConversion,
+ WasmLeUIOpConversion,
WasmLocalConversion,
WasmLocalGetConversion,
WasmLocalSetConversion,
WasmLocalTeeConversion,
+ WasmLtOpConversion,
+ WasmLtSIOpConversion,
+ WasmLtUIOpConversion,
WasmMaxOpConversion,
WasmMinOpConversion,
WasmMulOpConversion,
+ WasmNeOpConversion,
WasmNegOpConversion,
WasmOrOpConversion,
WasmPopCntOpConversion,
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-comparisons-to-arith-cmp.mlir b/mlir/test/Conversion/RaiseWasm/wasm-comparisons-to-arith-cmp.mlir
new file mode 100644
index 0000000000000..bc952b47520ca
--- /dev/null
+++ b/mlir/test/Conversion/RaiseWasm/wasm-comparisons-to-arith-cmp.mlir
@@ -0,0 +1,421 @@
+// RUN: mlir-opt %s --raise-wasm-mlir | FileCheck %s
+
+
+module {
+ wasmssa.func nested @func_lt_si32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.lt_si %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+
+ wasmssa.func nested @func_le_si32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.le_si %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_lt_ui32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.lt_ui %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_le_ui32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.le_ui %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_gt_si32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.gt_si %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_gt_ui32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.gt_ui %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ge_si32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.ge_si %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ge_ui32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.ge_ui %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_lt_si64() -> i32 {
+ %0 = wasmssa.const 12 : i64
+ %1 = wasmssa.const 50 : i64
+ %2 = wasmssa.lt_si %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_le_si64() -> i32 {
+ %0 = wasmssa.const 12 : i64
+ %1 = wasmssa.const 50 : i64
+ %2 = wasmssa.le_si %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_lt_ui_i64() -> i32 {
+ %0 = wasmssa.const 12 : i64
+ %1 = wasmssa.const 50 : i64
+ %2 = wasmssa.lt_ui %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_le_ui_i64() -> i32 {
+ %0 = wasmssa.const 12 : i64
+ %1 = wasmssa.const 50 : i64
+ %2 = wasmssa.le_ui %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_gt_si_i64() -> i32 {
+ %0 = wasmssa.const 12 : i64
+ %1 = wasmssa.const 50 : i64
+ %2 = wasmssa.gt_si %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_gt_ui_i64() -> i32 {
+ %0 = wasmssa.const 12 : i64
+ %1 = wasmssa.const 50 : i64
+ %2 = wasmssa.gt_ui %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ge_si_i64() -> i32 {
+ %0 = wasmssa.const 12 : i64
+ %1 = wasmssa.const 50 : i64
+ %2 = wasmssa.ge_si %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ge_ui_i64() -> i32 {
+ %0 = wasmssa.const 12 : i64
+ %1 = wasmssa.const 50 : i64
+ %2 = wasmssa.ge_ui %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_lt_f32() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f32
+ %1 = wasmssa.const 1.400000e+01 : f32
+ %2 = wasmssa.lt %0 %1 : f32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_le_f32() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f32
+ %1 = wasmssa.const 1.400000e+01 : f32
+ %2 = wasmssa.le %0 %1 : f32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_gt_f32() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f32
+ %1 = wasmssa.const 1.400000e+01 : f32
+ %2 = wasmssa.gt %0 %1 : f32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ge_f32() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f32
+ %1 = wasmssa.const 1.400000e+01 : f32
+ %2 = wasmssa.ge %0 %1 : f32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_lt_f64() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f64
+ %1 = wasmssa.const 1.400000e+01 : f64
+ %2 = wasmssa.lt %0 %1 : f64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_le_f64() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f64
+ %1 = wasmssa.const 1.400000e+01 : f64
+ %2 = wasmssa.le %0 %1 : f64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_gt_f64() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f64
+ %1 = wasmssa.const 1.400000e+01 : f64
+ %2 = wasmssa.gt %0 %1 : f64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ge_f64() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f64
+ %1 = wasmssa.const 1.400000e+01 : f64
+ %2 = wasmssa.ge %0 %1 : f64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_eq_i32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.eq %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_eq_i64() -> i32 {
+ %0 = wasmssa.const 20 : i64
+ %1 = wasmssa.const 5 : i64
+ %2 = wasmssa.eq %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_eq_f32() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f32
+ %1 = wasmssa.const 1.400000e+01 : f32
+ %2 = wasmssa.eq %0 %1 : f32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_eq_f64() -> i32 {
+ %0 = wasmssa.const 1.700000e+01 : f64
+ %1 = wasmssa.const 0.000000e+00 : f64
+ %2 = wasmssa.eq %0 %1 : f64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ne_i32() -> i32 {
+ %0 = wasmssa.const 12 : i32
+ %1 = wasmssa.const 50 : i32
+ %2 = wasmssa.ne %0 %1 : i32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ne_i64() -> i32 {
+ %0 = wasmssa.const 20 : i64
+ %1 = wasmssa.const 5 : i64
+ %2 = wasmssa.ne %0 %1 : i64 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ne_f32() -> i32 {
+ %0 = wasmssa.const 5.000000e+00 : f32
+ %1 = wasmssa.const 1.400000e+01 : f32
+ %2 = wasmssa.ne %0 %1 : f32 -> i32
+ wasmssa.return %2 : i32
+ }
+ wasmssa.func nested @func_ne_f64() -> i32 {
+ %0 = wasmssa.const 1.700000e+01 : f64
+ %1 = wasmssa.const 0.000000e+00 : f64
+ %2 = wasmssa.ne %0 %1 : f64 -> i32
+ wasmssa.return %2 : i32
+ }
+}
+// CHECK-LABEL: func.func @func_lt_si32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi slt, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_le_si32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi sle, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_lt_ui32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi ult, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_le_ui32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi ule, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_gt_si32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi sgt, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_gt_ui32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi ugt, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ge_si32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi sge, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ge_ui32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi uge, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_lt_si64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi slt, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_le_si64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi sle, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_lt_ui_i64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi ult, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_le_ui_i64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi ule, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_gt_si_i64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi sgt, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_gt_ui_i64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi ugt, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ge_si_i64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi sge, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ge_ui_i64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi uge, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_lt_f32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f32
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f32
+// CHECK: %[[VAL_2:.*]] = arith.cmpf olt, %[[VAL_0]], %[[VAL_1]] : f32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_le_f32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f32
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f32
+// CHECK: %[[VAL_2:.*]] = arith.cmpf ole, %[[VAL_0]], %[[VAL_1]] : f32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_gt_f32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f32
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f32
+// CHECK: %[[VAL_2:.*]] = arith.cmpf ogt, %[[VAL_0]], %[[VAL_1]] : f32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ge_f32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f32
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f32
+// CHECK: %[[VAL_2:.*]] = arith.cmpf oge, %[[VAL_0]], %[[VAL_1]] : f32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_lt_f64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f64
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f64
+// CHECK: %[[VAL_2:.*]] = arith.cmpf olt, %[[VAL_0]], %[[VAL_1]] : f64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_le_f64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f64
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f64
+// CHECK: %[[VAL_2:.*]] = arith.cmpf ole, %[[VAL_0]], %[[VAL_1]] : f64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_gt_f64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f64
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f64
+// CHECK: %[[VAL_2:.*]] = arith.cmpf ogt, %[[VAL_0]], %[[VAL_1]] : f64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ge_f64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f64
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f64
+// CHECK: %[[VAL_2:.*]] = arith.cmpf oge, %[[VAL_0]], %[[VAL_1]] : f64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_eq_i32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi eq, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_eq_i64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 20 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 5 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi eq, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_eq_f32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f32
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f32
+// CHECK: %[[VAL_2:.*]] = arith.cmpf oeq, %[[VAL_0]], %[[VAL_1]] : f32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_eq_f64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 1.700000e+01 : f64
+// CHECK: %[[VAL_1:.*]] = arith.constant 0.000000e+00 : f64
+// CHECK: %[[VAL_2:.*]] = arith.cmpf oeq, %[[VAL_0]], %[[VAL_1]] : f64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ne_i32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 50 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi ne, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ne_i64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 20 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 5 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi ne, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ne_f32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 5.000000e+00 : f32
+// CHECK: %[[VAL_1:.*]] = arith.constant 1.400000e+01 : f32
+// CHECK: %[[VAL_2:.*]] = arith.cmpf one, %[[VAL_0]], %[[VAL_1]] : f32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @func_ne_f64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 1.700000e+01 : f64
+// CHECK: %[[VAL_1:.*]] = arith.constant 0.000000e+00 : f64
+// CHECK: %[[VAL_2:.*]] = arith.cmpf one, %[[VAL_0]], %[[VAL_1]] : f64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-eqz-to-arith-cmp.mlir b/mlir/test/Conversion/RaiseWasm/wasm-eqz-to-arith-cmp.mlir
new file mode 100644
index 0000000000000..083a24c5f36a8
--- /dev/null
+++ b/mlir/test/Conversion/RaiseWasm/wasm-eqz-to-arith-cmp.mlir
@@ -0,0 +1,28 @@
+// RUN: mlir-opt %s --raise-wasm-mlir | FileCheck %s
+
+module {
+ wasmssa.func @eqz_i32() -> i32 {
+ %0 = wasmssa.const 13 : i32
+ %1 = wasmssa.eqz %0 : i32 -> i32
+ wasmssa.return %1 : i32
+ }
+ wasmssa.func @eqz_i64() -> i32 {
+ %0 = wasmssa.const 13 : i64
+ %1 = wasmssa.eqz %0 : i64 -> i32
+ wasmssa.return %1 : i32
+ }
+}
+
+// CHECK-LABEL: func.func @eqz_i32() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 13 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_2:.*]] = arith.cmpi eq, %[[VAL_0]], %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK-LABEL: func.func @eqz_i64() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 13 : i64
+// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i64
+// CHECK: %[[VAL_2:.*]] = arith.cmpi eq, %[[VAL_0]], %[[VAL_1]] : i64
+// CHECK: %[[VAL_3:.*]] = arith.extui %[[VAL_2]] : i1 to i32
+// CHECK: return %[[VAL_3]] : i32
>From 49944e5c6ec9e356338dfaabbd248fb969ce49b8 Mon Sep 17 00:00:00 2001
From: Ferdinand Lemaire <ferdinand.lemaire at woven-planet.global>
Date: Thu, 3 Jul 2025 13:06:50 +0900
Subject: [PATCH 2/7] [mlir][wasm] RaiseWasmMLIRPass: Add rotl and extend
support
--
Co-authored-by: Luc Forget <luc.forget at woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette at woven-planet.global>
---
.../Conversion/RaiseWasm/RaiseWasmMLIR.cpp | 87 +++++++++++++++++++
.../RaiseWasm/wasm-extend-to-arith-ext.mlir | 76 ++++++++++++++++
.../RaiseWasm/wasm-rotl-to-arith.mlir | 75 ++++++++++++++++
.../RaiseWasm/wasm-rotr-to-arith.mlir | 77 ++++++++++++++++
4 files changed, 315 insertions(+)
create mode 100644 mlir/test/Conversion/RaiseWasm/wasm-extend-to-arith-ext.mlir
create mode 100644 mlir/test/Conversion/RaiseWasm/wasm-rotl-to-arith.mlir
create mode 100644 mlir/test/Conversion/RaiseWasm/wasm-rotr-to-arith.mlir
diff --git a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
index 1fb4564032421..bf958aad5012b 100644
--- a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
+++ b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
@@ -124,6 +124,72 @@ using WasmTruncOpConversion = OpMappingConversion<TruncOp, math::TruncOp>;
using WasmSqrtOpConversion = OpMappingConversion<SqrtOp, math::SqrtOp>;
using WasmWrapOpConversion = OpMappingConversion<WrapOp, arith::TruncIOp>;
+/// Lower a rotate to a series of bitwise operations. Intended for us
+/// in dialects that do not natively support rotate operations.
+///
+/// Result stays in the wasm dialect. It will then subsequently be lowered to
+/// the target dialect.
+///
+/// The rotate will be lowered to a pattern like so:
+///
+/// (val LHSShiftOp (bits & (width-1))) | (val RHSShiftOp (-bits & (width-1)))
+///
+/// Where LHSShiftOp and RHSShiftOp are shift operations. Concretely,
+///
+/// rotr = (val >> (bits & (width - 1))) | (val << (-bits & (width - 1)))
+/// rotl = (val << (bits & (width - 1))) | (val >> (-bits & (width - 1)))
+///
+/// Using this variant ensures that our rotate is defined in the target dialect.
+///
+/// \p SourceOp - Rotate operation to replace.
+/// \p LHSShiftOp - Shift operation to use on the left-hand side of the OR.
+/// \p RHSShiftOp - Shift operation to use on the right-hand side of the OR.
+template <typename SourceOp, typename LHSShiftOp, typename RHSShiftOp>
+struct RotateOpConversion : OpConversionPattern<SourceOp> {
+ using OpConversionPattern<SourceOp>::OpConversionPattern;
+
+ LogicalResult
+ matchAndRewrite(SourceOp srcOp, typename SourceOp::Adaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ const Type ty = srcOp->getResultTypes()[0];
+ const Location loc = srcOp->getLoc();
+ const Value val = adaptor.getVal();
+ const Value bits = adaptor.getBits();
+ const unsigned width = ty.getIntOrFloatBitWidth();
+
+ // Materialize (width - 1) for use in both sides of the expression.
+ auto cstWidthMinusOne =
+ rewriter.create<ConstOp>(loc, IntegerAttr::get(ty, width - 1));
+
+ // Form the left-hand side of the OR:
+ // (val (lhs shift op) (bits & (width - 1)))
+ auto orLHS = rewriter.create<LHSShiftOp>(
+ loc, val, rewriter.create<AndOp>(loc, bits, cstWidthMinusOne));
+
+ // Form the right-hand side of the OR:
+ // (val (rhs shift op) (-bits & (width - 1)))
+ auto orRHS = rewriter.create<RHSShiftOp>(
+ loc, val,
+ // (-bits & (width - 1))
+ rewriter.create<AndOp>(
+ loc,
+ // 0 - bits == -bits
+ rewriter.create<SubOp>(
+ loc,
+ rewriter.create<ConstOp>(loc, IntegerAttr::get(ty, 0)),
+ bits),
+ cstWidthMinusOne));
+
+ // OR together the two shifts and replace the rotate with the new
+ // expression.
+ rewriter.replaceOpWithNewOp<OrOp>(srcOp, orLHS, orRHS);
+ return success();
+ }
+};
+
+using WasmRotrOpConversion = RotateOpConversion<RotrOp, ShRUOp, ShLOp>;
+using WasmRotlOpConversion = RotateOpConversion<RotlOp, ShLOp, ShRUOp>;
+
template <typename SourceOp, typename TargetOp, typename AttrType,
typename ValType, ValType flag>
struct ComparisonOpConversion : OpConversionPattern<SourceOp> {
@@ -272,6 +338,24 @@ struct WasmEqzOpConversion : OpConversionPattern<EqzOp> {
}
};
+struct WasmExtendLowBitsOpConversion : OpConversionPattern<ExtendLowBitsSOp> {
+ using OpConversionPattern::OpConversionPattern;
+
+ LogicalResult
+ matchAndRewrite(ExtendLowBitsSOp extendLowBytesSOp,
+ ExtendLowBitsSOp::Adaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ auto truncWidth = extendLowBytesSOp.getBitsToTake().getInt();
+ auto truncation = rewriter.create<arith::TruncIOp>(
+ extendLowBytesSOp->getLoc(), rewriter.getIntegerType(truncWidth),
+ adaptor.getInput());
+ rewriter.replaceOpWithNewOp<arith::ExtSIOp>(
+ extendLowBytesSOp, extendLowBytesSOp.getResult().getType(),
+ truncation.getResult());
+ return success();
+ }
+};
+
struct WasmFuncImportOpConversion : OpConversionPattern<FuncImportOp> {
using OpConversionPattern::OpConversionPattern;
@@ -555,6 +639,7 @@ void mlir::populateRaiseWasmMLIRConversionPatterns(
WasmDivUIOpConversion,
WasmEqOpConversion,
WasmEqzOpConversion,
+ WasmExtendLowBitsOpConversion,
WasmExtendSOpConversion,
WasmExtendUOpConversion,
WasmFloorOpConversion,
@@ -591,6 +676,8 @@ void mlir::populateRaiseWasmMLIRConversionPatterns(
WasmRemSIOpConversion,
WasmRemUIOpConversion,
WasmReturnOpConversion,
+ WasmRotlOpConversion,
+ WasmRotrOpConversion,
WasmShLOpConversion,
WasmShRSOpConversion,
WasmShRUOpConversion,
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-extend-to-arith-ext.mlir b/mlir/test/Conversion/RaiseWasm/wasm-extend-to-arith-ext.mlir
new file mode 100644
index 0000000000000..90c92001a78d6
--- /dev/null
+++ b/mlir/test/Conversion/RaiseWasm/wasm-extend-to-arith-ext.mlir
@@ -0,0 +1,76 @@
+// RUN: mlir-opt %s --raise-wasm-mlir | FileCheck %s
+
+// CHECK-LABEL: func.func @func_0() -> i64 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 10 : i32
+// CHECK: %[[VAL_1:.*]] = arith.extsi %[[VAL_0]] : i32 to i64
+// CHECK: return %[[VAL_1]] : i64
+wasmssa.func nested @func_0() -> i64 {
+ %0 = wasmssa.const 10 : i32
+ %1 = wasmssa.extend_i32_s %0 to i64
+ wasmssa.return %1 : i64
+}
+
+// CHECK-LABEL: func.func @func_1() -> i64 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 10 : i32
+// CHECK: %[[VAL_1:.*]] = arith.extui %[[VAL_0]] : i32 to i64
+// CHECK: return %[[VAL_1]] : i64
+wasmssa.func nested @func_1() -> i64 {
+ %0 = wasmssa.const 10 : i32
+ %1 = wasmssa.extend_i32_u %0 to i64
+ wasmssa.return %1 : i64
+}
+
+// CHECK-LABEL: func.func @func_2() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 10 : i32
+// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i32 to i8
+// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i8 to i32
+// CHECK: return %[[VAL_2]] : i32
+wasmssa.func nested @func_2() -> i32 {
+ %0 = wasmssa.const 10 : i32
+ %1 = wasmssa.extend 8 low bits from %0: i32
+ wasmssa.return %1 : i32
+}
+
+// CHECK-LABEL: func.func @func_3() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 10 : i32
+// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i32 to i16
+// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i16 to i32
+// CHECK: return %[[VAL_2]] : i32
+wasmssa.func nested @func_3() -> i32 {
+ %0 = wasmssa.const 10 : i32
+ %1 = wasmssa.extend 16 low bits from %0: i32
+ wasmssa.return %1 : i32
+}
+
+// CHECK-LABEL: func.func @func_4() -> i64 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 10 : i64
+// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i64 to i8
+// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i8 to i64
+// CHECK: return %[[VAL_2]] : i64
+wasmssa.func nested @func_4() -> i64 {
+ %0 = wasmssa.const 10 : i64
+ %1 = wasmssa.extend 8 low bits from %0: i64
+ wasmssa.return %1 : i64
+}
+
+// CHECK-LABEL: func.func @func_5() -> i64 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 10 : i64
+// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i64 to i16
+// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i16 to i64
+// CHECK: return %[[VAL_2]] : i64
+wasmssa.func nested @func_5() -> i64 {
+ %0 = wasmssa.const 10 : i64
+ %1 = wasmssa.extend 16 low bits from %0: i64
+ wasmssa.return %1 : i64
+}
+
+// CHECK-LABEL: func.func @func_6() -> i64 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 10 : i64
+// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i64 to i32
+// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i32 to i64
+// CHECK: return %[[VAL_2]] : i64
+wasmssa.func nested @func_6() -> i64 {
+ %0 = wasmssa.const 10 : i64
+ %1 = wasmssa.extend 32 low bits from %0: i64
+ wasmssa.return %1 : i64
+}
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-rotl-to-arith.mlir b/mlir/test/Conversion/RaiseWasm/wasm-rotl-to-arith.mlir
new file mode 100644
index 0000000000000..91e929bd5ff35
--- /dev/null
+++ b/mlir/test/Conversion/RaiseWasm/wasm-rotl-to-arith.mlir
@@ -0,0 +1,75 @@
+// RUN: mlir-opt --split-input-file %s --raise-wasm-mlir -o - | FileCheck %s
+
+// Given:
+// %res = wasmssa.rotl %val by %bits bits : i32
+//
+// Produce:
+// res = (val >> (bits & 31)) | (val << (-bits & 31))
+
+// CHECK-LABEL: func.func @rotl_i32(
+// CHECK-SAME: %[[VALREF:.*]]: i32,
+// CHECK-SAME: %[[BITSREF:.*]]: i32) -> i32 {
+
+// Storage etc
+// CHECK-DAG: %[[VAL_1:.*]] = memref.alloca() : memref<i32>
+// CHECK-DAG: %[[VAL_0:.*]] = memref.alloca() : memref<i32>
+// CHECK-DAG: memref.store %[[VALREF]], %[[VAL_0]][] : memref<i32>
+// CHECK-DAG: memref.store %[[BITSREF]], %[[VAL_1]][] : memref<i32>
+// CHECK-DAG: %[[VAL:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK-DAG: %[[BITS:.*]] = memref.load %[[VAL_1]][] : memref<i32>
+
+// (val << (bits & 31))
+// CHECK: %[[THIRTY_ONE:.*]] = arith.constant 31 : i32
+// CHECK: %[[LHS_AND:.*]] = arith.andi %[[BITS]], %[[THIRTY_ONE]] : i32
+// CHECK: %[[SHRU:.*]] = arith.shli %[[VAL]], %[[LHS_AND]] : i32
+
+// (val >> (-bits & 31))
+// CHECK: %[[ZERO:.*]] = arith.constant 0 : i32
+// CHECK: %[[NEG_BITS:.*]] = arith.subi %[[ZERO]], %[[BITS]] : i32
+// CHECK: %[[RHS_AND:.*]] = arith.andi %[[NEG_BITS]], %[[THIRTY_ONE]] : i32
+// CHECK: %[[SHL:.*]] = arith.shrui %[[VAL]], %[[RHS_AND]] : i32
+
+// CHECK: %[[RES:.*]] = arith.ori %[[SHRU]], %[[SHL]] : i32
+// CHECK: return %[[RES]] : i32
+wasmssa.func nested @rotl_i32(%arg0: !wasmssa<local ref to i32>, %arg1: !wasmssa<local ref to i32>) -> i32 {
+ %v0 = wasmssa.local_get %arg0 : ref to i32
+ %v1 = wasmssa.local_get %arg1 : ref to i32
+
+ %op = wasmssa.rotl %v0 by %v1 bits : i32
+ wasmssa.return %op : i32
+}
+
+// Same as above, but with 64 bits.
+// CHECK-LABEL: func.func @rotl_i64(
+// CHECK-SAME: %[[VALREF:.*]]: i64,
+// CHECK-SAME: %[[BITSREF:.*]]: i64) -> i64 {
+
+// Storage etc
+// CHECK-DAG: %[[VAL_1:.*]] = memref.alloca() : memref<i64>
+// CHECK-DAG: %[[VAL_0:.*]] = memref.alloca() : memref<i64>
+// CHECK-DAG: memref.store %[[VALREF]], %[[VAL_0]][] : memref<i64>
+// CHECK-DAG: memref.store %[[BITSREF]], %[[VAL_1]][] : memref<i64>
+// CHECK-DAG: %[[VAL:.*]] = memref.load %[[VAL_0]][] : memref<i64>
+// CHECK-DAG: %[[BITS:.*]] = memref.load %[[VAL_1]][] : memref<i64>
+
+// (val << (bits & 63))
+// CHECK: %[[SIXTY_THREE:.*]] = arith.constant 63 : i64
+// CHECK: %[[LHS_AND:.*]] = arith.andi %[[BITS]], %[[SIXTY_THREE]] : i64
+// CHECK: %[[SHRU:.*]] = arith.shli %[[VAL]], %[[LHS_AND]] : i64
+
+// (val >> (-bits & 63))
+// CHECK: %[[ZERO:.*]] = arith.constant 0 : i64
+// CHECK: %[[NEG_BITS:.*]] = arith.subi %[[ZERO]], %[[BITS]] : i64
+// CHECK: %[[RHS_AND:.*]] = arith.andi %[[NEG_BITS]], %[[SIXTY_THREE]] : i64
+// CHECK: %[[SHL:.*]] = arith.shrui %[[VAL]], %[[RHS_AND]] : i64
+
+// Form final result.
+// CHECK: %[[RES:.*]] = arith.ori %[[SHRU]], %[[SHL]] : i64
+// CHECK: return %[[RES]] : i64
+wasmssa.func nested @rotl_i64(%arg0: !wasmssa<local ref to i64>, %arg1: !wasmssa<local ref to i64>) -> i64 {
+ %v0 = wasmssa.local_get %arg0 : ref to i64
+ %v1 = wasmssa.local_get %arg1 : ref to i64
+
+ %op = wasmssa.rotl %v0 by %v1 bits : i64
+ wasmssa.return %op : i64
+}
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-rotr-to-arith.mlir b/mlir/test/Conversion/RaiseWasm/wasm-rotr-to-arith.mlir
new file mode 100644
index 0000000000000..77e301c3221e3
--- /dev/null
+++ b/mlir/test/Conversion/RaiseWasm/wasm-rotr-to-arith.mlir
@@ -0,0 +1,77 @@
+// RUN: mlir-opt --split-input-file %s --raise-wasm-mlir -o - | FileCheck %s
+
+// Given:
+// %res = wasmssa.rotr %val by %bits bits : i32
+//
+// Produce:
+// res = (val >> (bits & 31)) | (val << (-bits & 31))
+
+// CHECK-LABEL: func.func @rotr_i32(
+// CHECK-SAME: %[[VALREF:.*]]: i32,
+// CHECK-SAME: %[[BITSREF:.*]]: i32) -> i32 {
+
+// Storage etc
+// CHECK-DAG: %[[VAL_1:.*]] = memref.alloca() : memref<i32>
+// CHECK-DAG: %[[VAL_0:.*]] = memref.alloca() : memref<i32>
+// CHECK-DAG: memref.store %[[VALREF]], %[[VAL_0]][] : memref<i32>
+// CHECK-DAG: memref.store %[[BITSREF]], %[[VAL_1]][] : memref<i32>
+// CHECK-DAG: %[[VAL:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK-DAG: %[[BITS:.*]] = memref.load %[[VAL_1]][] : memref<i32>
+
+// (val >> (bits & 31))
+// CHECK: %[[THIRTY_ONE:.*]] = arith.constant 31 : i32
+// CHECK: %[[LHS_AND:.*]] = arith.andi %[[BITS]], %[[THIRTY_ONE]] : i32
+// CHECK: %[[SHRU:.*]] = arith.shrui %[[VAL]], %[[LHS_AND]] : i32
+
+// (val << (-bits & 31))
+// CHECK: %[[ZERO:.*]] = arith.constant 0 : i32
+// CHECK: %[[NEG_BITS:.*]] = arith.subi %[[ZERO]], %[[BITS]] : i32
+// CHECK: %[[RHS_AND:.*]] = arith.andi %[[NEG_BITS]], %[[THIRTY_ONE]] : i32
+// CHECK: %[[SHL:.*]] = arith.shli %[[VAL]], %[[RHS_AND]] : i32
+
+// CHECK: %[[RES:.*]] = arith.ori %[[SHRU]], %[[SHL]] : i32
+// CHECK: return %[[RES]] : i32
+// CHECK: }
+wasmssa.func nested @rotr_i32(%arg0: !wasmssa<local ref to i32>, %arg1: !wasmssa<local ref to i32>) -> i32 {
+ %v0 = wasmssa.local_get %arg0 : ref to i32
+ %v1 = wasmssa.local_get %arg1 : ref to i32
+
+ %op = wasmssa.rotr %v0 by %v1 bits : i32
+ wasmssa.return %op : i32
+}
+
+// Same as above, but with 64 bits.
+// CHECK-LABEL: func.func @rotr_i64(
+// CHECK-SAME: %[[VALREF:.*]]: i64,
+// CHECK-SAME: %[[BITSREF:.*]]: i64) -> i64 {
+
+// Storage etc
+// CHECK-DAG: %[[VAL_1:.*]] = memref.alloca() : memref<i64>
+// CHECK-DAG: %[[VAL_0:.*]] = memref.alloca() : memref<i64>
+// CHECK-DAG: memref.store %[[VALREF]], %[[VAL_0]][] : memref<i64>
+// CHECK-DAG: memref.store %[[BITSREF]], %[[VAL_1]][] : memref<i64>
+// CHECK-DAG: %[[VAL:.*]] = memref.load %[[VAL_0]][] : memref<i64>
+// CHECK-DAG: %[[BITS:.*]] = memref.load %[[VAL_1]][] : memref<i64>
+
+// (val >> (bits & 63))
+// CHECK: %[[SIXTY_THREE:.*]] = arith.constant 63 : i64
+// CHECK: %[[LHS_AND:.*]] = arith.andi %[[BITS]], %[[SIXTY_THREE]] : i64
+// CHECK: %[[SHRU:.*]] = arith.shrui %[[VAL]], %[[LHS_AND]] : i64
+
+// (val << (-bits & 63))
+// CHECK: %[[ZERO:.*]] = arith.constant 0 : i64
+// CHECK: %[[NEG_BITS:.*]] = arith.subi %[[ZERO]], %[[BITS]] : i64
+// CHECK: %[[RHS_AND:.*]] = arith.andi %[[NEG_BITS]], %[[SIXTY_THREE]] : i64
+// CHECK: %[[SHL:.*]] = arith.shli %[[VAL]], %[[RHS_AND]] : i64
+
+// Form final result.
+// CHECK: %[[RES:.*]] = arith.ori %[[SHRU]], %[[SHL]] : i64
+// CHECK: return %[[RES]] : i64
+// CHECK: }
+wasmssa.func nested @rotr_i64(%arg0: !wasmssa<local ref to i64>, %arg1: !wasmssa<local ref to i64>) -> i64 {
+ %v0 = wasmssa.local_get %arg0 : ref to i64
+ %v1 = wasmssa.local_get %arg1 : ref to i64
+
+ %op = wasmssa.rotr %v0 by %v1 bits : i64
+ wasmssa.return %op : i64
+}
>From 6431a7494a5edc89de997962d92b1803d6aed43f Mon Sep 17 00:00:00 2001
From: Ferdinand Lemaire <ferdinand.lemaire at woven-planet.global>
Date: Thu, 3 Jul 2025 13:24:21 +0900
Subject: [PATCH 3/7] [mlir][wasm] RaiseWasmMLIRPass: Add support for control
flow operations in functions
--
Co-authored-by: Luc Forget <luc.forget at woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette at woven-planet.global>
---
.../Conversion/RaiseWasm/RaiseWasmMLIR.cpp | 184 ++++++++-
.../RaiseWasm/wasm-blocks-to-cf.mlir | 366 ++++++++++++++++++
.../Conversion/RaiseWasm/wasm-loop-to-cf.mlir | 206 ++++++++++
3 files changed, 755 insertions(+), 1 deletion(-)
create mode 100644 mlir/test/Conversion/RaiseWasm/wasm-blocks-to-cf.mlir
create mode 100644 mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir
diff --git a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
index bf958aad5012b..d9dcb756790f8 100644
--- a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
+++ b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
@@ -371,6 +371,187 @@ struct WasmFuncImportOpConversion : OpConversionPattern<FuncImportOp> {
struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
using OpConversionPattern::OpConversionPattern;
+ ///
+ /// Control flow conversion needs shared state for tracking which block
+ /// corresponds to which operation at which level.
+ ///
+ /// This class handles such tracking and performs the conversion of control
+ /// flow related ops contained in a function.
+ class CFRewriterVisitor {
+ private:
+ using branch_to_dest_t =
+ llvm::DenseMap<WasmSSALabelBranchingInterface, Block *>;
+ Value getCompResultAsI1(Value compResult,
+ ConversionPatternRewriter &rewriter) {
+ auto testValue = rewriter.create<arith::ConstantOp>(
+ compResult.getLoc(), rewriter.getI32IntegerAttr(0));
+ auto flag = rewriter
+ .create<arith::CmpIOp>(
+ compResult.getLoc(), rewriter.getIntegerType(1),
+ arith::CmpIPredicate::ne, compResult, testValue)
+ .getResult();
+ return flag;
+ }
+
+ void replaceNestLevelWithBranch(BlockOp blockOp,
+ llvm::ArrayRef<Block *> regionsToEntry,
+ ConversionPatternRewriter &rewriter) {
+ rewriter.replaceOpWithNewOp<cf::BranchOp>(blockOp, regionsToEntry[0],
+ blockOp->getOperands());
+ }
+
+ void replaceNestLevelWithBranch(LoopOp loopOp,
+ llvm::ArrayRef<Block *> regionsToEntry,
+ ConversionPatternRewriter &rewriter) {
+ rewriter.replaceOpWithNewOp<cf::BranchOp>(loopOp, regionsToEntry[0],
+ loopOp->getOperands());
+ }
+
+ void replaceNestLevelWithBranch(IfOp ifOp,
+ llvm::ArrayRef<Block *> regionsToEntry,
+ ConversionPatternRewriter &rewriter) {
+ Block *falseDest =
+ regionsToEntry.size() == 2 ? regionsToEntry[1] : ifOp.getTarget();
+ auto flag = getCompResultAsI1(ifOp.getCondition(), rewriter);
+ rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
+ ifOp, flag, regionsToEntry[0], ifOp.getInputs(), falseDest,
+ ifOp.getInputs());
+ }
+
+ template <typename LevelType>
+ LogicalResult
+ replaceNestLevelWithBranchWrapper(WasmSSALabelLevelInterface nestingOp,
+ llvm::ArrayRef<Block *> regionsToEntry,
+ ConversionPatternRewriter &rewriter) {
+ auto cast = dyn_cast<LevelType>(nestingOp.getOperation());
+ if (!cast)
+ return failure();
+ replaceNestLevelWithBranch(cast, regionsToEntry, rewriter);
+ return success();
+ }
+
+ template <typename... LevelTypes>
+ LogicalResult inlineNestDispatcher(WasmSSALabelLevelInterface nestingOp,
+ ConversionPatternRewriter &rewriter) {
+ auto sip = rewriter.saveInsertionPoint();
+ Block *blockSuccessor = nestingOp->getSuccessor(0);
+ llvm::SmallVector<Block *, 2> regionEntries;
+ LLVM_DEBUG(llvm::dbgs()
+ << "Starting inlining blocks for " << nestingOp << "\n";);
+ for (auto ®ion : nestingOp->getRegions()) {
+ if (region.empty())
+ continue;
+ regionEntries.push_back(®ion.front());
+ /// Inline blocks of nested ops
+ llvm::SmallVector<WasmSSALabelLevelInterface> nestedOps{
+ region.getOps<WasmSSALabelLevelInterface>()};
+ for (auto nestedOp : nestedOps) {
+ LLVM_DEBUG(llvm::dbgs() << " Found nested op: " << nestedOp);
+ if (failed(inlineBlocks(nestedOp, rewriter)))
+ return failure();
+ }
+ rewriter.inlineRegionBefore(region, blockSuccessor);
+ }
+ LLVM_DEBUG(llvm::dbgs() << "End of region inlining\n");
+ LLVM_DEBUG(llvm::dbgs() << "Replacing initial op with branching\n");
+ rewriter.setInsertionPoint(nestingOp);
+ auto res = success(
+ (... || succeeded(replaceNestLevelWithBranchWrapper<LevelTypes>(
+ nestingOp, regionEntries, rewriter))));
+ rewriter.restoreInsertionPoint(sip);
+ if (failed(res))
+ return emitError(nestingOp->getLoc(),
+ "Unable to inline the operation regions.");
+ return success();
+ }
+
+ /// Take a nesting level defining op and inline it in the parent region.
+ LogicalResult inlineBlocks(WasmSSALabelLevelInterface nestingOp,
+ ConversionPatternRewriter &rewriter) {
+ return inlineNestDispatcher<BlockOp, IfOp, LoopOp>(nestingOp, rewriter);
+ }
+
+ llvm::FailureOr<Block *> getBlockFor(WasmSSALabelBranchingInterface branchOp) {
+ auto destIter = branchToDest.find(branchOp);
+ if (destIter == branchToDest.end())
+ return branchOp->emitError("No indexed label op for this operation: ")
+ << branchOp;
+ return destIter->second;
+ }
+
+ inline void convertBranch(BranchIfOp brOp, Block *dest,
+ ConversionPatternRewriter &rewriter) {
+ auto flag = getCompResultAsI1(brOp.getCondition(), rewriter);
+ rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
+ brOp, flag, dest, brOp.getInputs(), brOp.getElseSuccessor(),
+ ValueRange{});
+ }
+
+ inline void convertBranch(BlockReturnOp brOp, Block *dest,
+ ConversionPatternRewriter &rewriter) {
+ rewriter.replaceOpWithNewOp<cf::BranchOp>(brOp, dest, brOp.getInputs());
+ }
+
+ template <typename LevelInterfaceT>
+ inline LogicalResult
+ convertBranchWrapper(WasmSSALabelBranchingInterface branchOp, Block *dest,
+ ConversionPatternRewriter &rewriter) {
+ auto cast = dyn_cast<LevelInterfaceT>(branchOp.getOperation());
+ if (!cast)
+ return failure();
+ auto sip = rewriter.saveInsertionPoint();
+ rewriter.setInsertionPoint(branchOp);
+ convertBranch(cast, dest, rewriter);
+ rewriter.restoreInsertionPoint(sip);
+ return success();
+ }
+
+ template <typename... BranchInterfaceT>
+ LogicalResult convertBranchDispatch(WasmSSALabelBranchingInterface branchOp,
+ ConversionPatternRewriter &rewriter) {
+ auto dest = getBlockFor(branchOp);
+ if (failed(dest))
+ return failure();
+ auto res =
+ success((... || succeeded(convertBranchWrapper<BranchInterfaceT>(
+ branchOp, *dest, rewriter))));
+ if (failed(res))
+ return emitError(branchOp->getLoc(), "No known converter for op ")
+ << branchOp;
+ return res;
+ }
+
+ LogicalResult convertBranch(WasmSSALabelBranchingInterface branchOp,
+ ConversionPatternRewriter &rewriter) {
+ return convertBranchDispatch<BlockReturnOp, BranchIfOp>(branchOp,
+ rewriter);
+ }
+
+ func::FuncOp func;
+ branch_to_dest_t branchToDest;
+
+ public:
+ CFRewriterVisitor(func::FuncOp func) : func{func} {
+ func.walk([this](WasmSSALabelBranchingInterface branchOp) {
+ branchToDest.insert({branchOp, branchOp.getTarget()});
+ });
+ }
+ LogicalResult rewrite(ConversionPatternRewriter &rewriter) {
+ llvm::SmallVector<WasmSSALabelLevelInterface> nestingOps{
+ func.getOps<WasmSSALabelLevelInterface>()};
+ for (auto nestingOp : nestingOps)
+ if (failed(inlineBlocks(nestingOp, rewriter)))
+ return failure();
+
+ auto res =
+ func->walk([this, &rewriter](WasmSSALabelBranchingInterface branchOp) {
+ if (failed(convertBranch(branchOp, rewriter)))
+ return WalkResult::interrupt();
+ return WalkResult::advance();
+ });
+ return failure(res.wasInterrupted());
+ }
+ };
LogicalResult
matchAndRewrite(FuncOp funcOp, FuncOp::Adaptor adaptor,
@@ -393,7 +574,8 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
rewriter.applySignatureConversion(oldEntryBlock, sC, getTypeConverter());
rewriter.replaceOp(funcOp, newFunc);
- return success();
+ CFRewriterVisitor cfRewriter{newFunc};
+ return cfRewriter.rewrite(rewriter);
}
};
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-blocks-to-cf.mlir b/mlir/test/Conversion/RaiseWasm/wasm-blocks-to-cf.mlir
new file mode 100644
index 0000000000000..3b275573bcbd3
--- /dev/null
+++ b/mlir/test/Conversion/RaiseWasm/wasm-blocks-to-cf.mlir
@@ -0,0 +1,366 @@
+// RUN: mlir-opt --split-input-file %s --raise-wasm-mlir -o - | FileCheck %s
+// RUN: mlir-opt --split-input-file %s --raise-wasm-mlir --canonicalize -o - | FileCheck --check-prefix=CHECK_CANONICALIZED %s
+
+// CHECK-LABEL: func.func @i_am_a_block(
+// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
+// CHECK: %[[VAL_0:.*]] = memref.alloca() : memref<i32>
+// CHECK: memref.store %[[ARG0]], %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_1:.*]] = arith.constant 17 : i32
+// CHECK: memref.store %[[VAL_1]], %[[VAL_0]][] : memref<i32>
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: %[[VAL_2:.*]] = arith.constant 42 : i32
+// CHECK: memref.store %[[VAL_2]], %[[VAL_0]][] : memref<i32>
+// CHECK: cf.br ^bb2
+// CHECK: ^bb2:
+// CHECK: %[[VAL_3:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: return %[[VAL_3]] : i32
+
+// CHECK_CANONICALIZED-LABEL: func.func @i_am_a_block(
+// CHECK_CANONICALIZED-SAME: %[[VAL_0:.*]]: i32) -> i32 {
+// CHECK_CANONICALIZED: %[[VAL_1:.*]] = arith.constant 42 : i32
+// CHECK_CANONICALIZED: %[[VAL_3:.*]] = memref.alloca() : memref<i32>
+// CHECK_CANONICALIZED: memref.store %[[VAL_1]], %[[VAL_3]][] : memref<i32>
+// CHECK_CANONICALIZED-NOT: memref.store
+// CHECK_CANONICALIZED: %[[VAL_4:.*]] = memref.load %[[VAL_3]][] : memref<i32>
+// CHECK_CANONICALIZED: return %[[VAL_4]] : i32
+wasmssa.func @i_am_a_block(%arg0 : !wasmssa<local ref to i32>) -> i32 {
+ %1 = wasmssa.const 17 : i32
+ wasmssa.local_set %arg0 : ref to i32 to %1 : i32
+ wasmssa.block : {
+ %2 = wasmssa.const 42 : i32
+ wasmssa.local_set %arg0 : ref to i32 to %2 : i32
+ wasmssa.block_return
+ }> ^bb1
+ ^bb1:
+ %res = wasmssa.local_get %arg0 : ref to i32
+ wasmssa.return %res : i32
+}
+
+
+// CHECK-LABEL: func.func @func_0(
+// CHECK-SAME: %[[ARG0:.*]]: i32) {
+// CHECK: %[[VAL_0:.*]] = arith.constant 1 : i32
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: %[[VAL_1:.*]] = arith.constant 2 : i32
+// CHECK: cf.br ^bb2
+// CHECK: ^bb2:
+// CHECK: %[[VAL_2:.*]] = arith.constant 3 : i32
+// CHECK: cf.br ^bb3
+// CHECK: ^bb3:
+// CHECK: %[[VAL_3:.*]] = arith.constant 4 : i32
+// CHECK: cf.br ^bb4
+// CHECK: ^bb4:
+// CHECK: %[[VAL_4:.*]] = arith.constant 5 : i32
+// CHECK: cf.br ^bb5
+// CHECK: ^bb5:
+// CHECK: %[[VAL_5:.*]] = arith.constant 6 : i32
+// CHECK: cf.br ^bb6
+// CHECK: ^bb6:
+// CHECK: %[[VAL_6:.*]] = arith.constant 7 : i32
+// CHECK: return
+wasmssa.func nested @func_0(%arg0: !wasmssa<local ref to i32>) {
+ %1 = wasmssa.const 1: i32
+ wasmssa.block : {
+ %2 = wasmssa.const 2: i32
+ wasmssa.block : {
+ %3 = wasmssa.const 3: i32
+ wasmssa.block : {
+ %4 = wasmssa.const 4: i32
+ wasmssa.block_return
+ }> ^bb1
+ ^bb1: // pred: ^bb0
+ %5 = wasmssa.const 5: i32
+ wasmssa.block_return
+ }> ^bb1
+ ^bb1: // pred: ^bb0
+ %6 = wasmssa.const 6: i32
+ wasmssa.block_return
+ }> ^bb1
+^bb1: // pred: ^bb0
+ %7 = wasmssa.const 7: i32
+ wasmssa.return
+}
+
+// CHECK-LABEL: func.func @func_1() {
+// CHECK: %[[VAL_0:.*]] = arith.constant 1 : i32
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: %[[VAL_1:.*]] = arith.constant 2 : i32
+// CHECK: cf.br ^bb2
+// CHECK: ^bb2:
+// CHECK: %[[VAL_2:.*]] = arith.constant 3 : i32
+// CHECK: cf.br ^bb3
+// CHECK: ^bb3:
+// CHECK: %[[VAL_3:.*]] = arith.constant 4 : i32
+// CHECK: cf.br ^bb4
+// CHECK: ^bb4:
+// CHECK: %[[VAL_4:.*]] = arith.constant 5 : i32
+// CHECK: cf.br ^bb5
+// CHECK: ^bb5:
+// CHECK: %[[VAL_5:.*]] = arith.constant 6 : i32
+// CHECK: cf.br ^bb6
+// CHECK: ^bb6:
+// CHECK: %[[VAL_6:.*]] = arith.constant 7 : i32
+// CHECK: return
+wasmssa.func nested @func_1() {
+ %1 = wasmssa.const 1: i32
+ wasmssa.block : {
+ %2 = wasmssa.const 2: i32
+ wasmssa.block_return
+ }> ^bb1
+^bb1: // pred: ^bb0
+ %3 = wasmssa.const 3: i32
+ wasmssa.block : {
+ %4 = wasmssa.const 4: i32
+ wasmssa.block_return
+ }> ^bb2
+^bb2: // pred: ^bb1
+ %5 = wasmssa.const 5: i32
+ wasmssa.block : {
+ %6 = wasmssa.const 6: i32
+ wasmssa.block_return
+ }> ^bb3
+^bb3: // pred: ^bb2
+ %7 = wasmssa.const 7: i32
+ wasmssa.return
+}
+
+// CHECK-LABEL: func.func @func_2() -> i32 {
+// CHECK: %[[VAL_0:.*]] = arith.constant 14 : i32
+// CHECK: cf.br ^bb1(%[[VAL_0]] : i32)
+// CHECK: ^bb1(%[[VAL_1:.*]]: i32):
+// CHECK: %[[VAL_2:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_3:.*]] = arith.addi %[[VAL_1]], %[[VAL_2]] : i32
+// CHECK: cf.br ^bb2(%[[VAL_3]] : i32)
+// CHECK: ^bb2(%[[VAL_4:.*]]: i32):
+// CHECK: return %[[VAL_4]] : i32
+wasmssa.func nested @func_2() -> i32 {
+ %0 = wasmssa.const 14 : i32
+ wasmssa.block(%0) : i32 : {
+ ^bb0(%arg0: i32):
+ %2 = wasmssa.const 1 : i32
+ %3 = wasmssa.add %arg0 %2 : i32
+ wasmssa.block_return %3 : i32
+ }> ^bb1
+^bb1(%arg0: i32):
+ wasmssa.return %arg0 : i32
+}
+
+// CHECK-LABEL: func.func @func_3() -> i32 {
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: %[[VAL_0:.*]] = arith.constant 17 : i32
+// CHECK: cf.br ^bb2(%[[VAL_0]] : i32)
+// CHECK: ^bb2(%[[VAL_1:.*]]: i32):
+// CHECK: return %[[VAL_1]] : i32
+wasmssa.func nested @func_3() -> i32 {
+ wasmssa.block : {
+ %1 = wasmssa.const 17 : i32
+ wasmssa.block_return %1 : i32
+ }> ^bb1
+^bb1(%arg0: i32):
+ wasmssa.return %arg0 : i32
+}
+
+//// ============= Branch instructions etc ==========
+
+// CHECK-LABEL: func.func @branch_if_taken() -> i32 {
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: %[[VAL_0:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 2 : i32
+// CHECK: %[[VAL_2:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_3:.*]] = arith.cmpi ne, %[[VAL_1]], %[[VAL_2]] : i32
+// CHECK: cf.cond_br %[[VAL_3]], ^bb3(%[[VAL_0]] : i32), ^bb2
+// CHECK: ^bb2:
+// CHECK: %[[VAL_4:.*]] = arith.constant 16 : i32
+// CHECK: %[[VAL_5:.*]] = arith.addi %[[VAL_0]], %[[VAL_4]] : i32
+// CHECK: cf.br ^bb3(%[[VAL_5]] : i32)
+// CHECK: ^bb3(%[[VAL_6:.*]]: i32):
+// CHECK: return %[[VAL_6]] : i32
+
+// CHECK_CANONICALIZED-LABEL: func.func @branch_if_taken() -> i32 {
+// CHECK_CANONICALIZED: %[[VAL_0:.*]] = arith.constant 1 : i32
+// CHECK_CANONICALIZED: return %[[VAL_0]] : i32
+
+wasmssa.func nested @branch_if_taken() -> i32 {
+ wasmssa.block : {
+ %1 = wasmssa.const 1 : i32
+ %2 = wasmssa.const 2 : i32
+ wasmssa.branch_if %2 to level 0 with args(%1 : i32) else ^bb1
+ ^bb1: // pred: ^bb0
+ %3 = wasmssa.const 16 : i32
+ %4 = wasmssa.add %1 %3 : i32
+ wasmssa.block_return %4 : i32
+ }> ^bb1
+^bb1(%0: i32): // pred: ^bb0
+ wasmssa.return %0 : i32
+}
+
+// CHECK-LABEL: func.func @branch_if_continue() -> i32 {
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: %[[VAL_0:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_2:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_3:.*]] = arith.cmpi ne, %[[VAL_1]], %[[VAL_2]] : i32
+// CHECK: cf.cond_br %[[VAL_3]], ^bb3(%[[VAL_0]] : i32), ^bb2
+// CHECK: ^bb2:
+// CHECK: %[[VAL_4:.*]] = arith.constant 16 : i32
+// CHECK: %[[VAL_5:.*]] = arith.addi %[[VAL_0]], %[[VAL_4]] : i32
+// CHECK: cf.br ^bb3(%[[VAL_5]] : i32)
+// CHECK: ^bb3(%[[VAL_6:.*]]: i32):
+// CHECK: return %[[VAL_6]] : i32
+
+// CHECK_CANONICALIZED-LABEL: func.func @branch_if_continue() -> i32 {
+// CHECK_CANONICALIZED: %[[VAL_0:.*]] = arith.constant 17 : i32
+// CHECK_CANONICALIZED: return %[[VAL_0]] : i32
+// CHECK_CANONICALIZED: }
+wasmssa.func nested @branch_if_continue() -> i32 {
+ wasmssa.block : {
+ %1 = wasmssa.const 1 : i32
+ %2 = wasmssa.const 0 : i32
+ wasmssa.branch_if %2 to level 0 with args(%1 : i32) else ^bb1
+ ^bb1: // pred: ^bb0
+ %3 = wasmssa.const 16 : i32
+ %4 = wasmssa.add %1 %3 : i32
+ wasmssa.block_return %4 : i32
+ }> ^bb1
+^bb1(%0: i32): // pred: ^bb0
+ wasmssa.return %0 : i32
+}
+
+// CHECK-LABEL: func.func @if(
+// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
+// CHECK: %[[VAL_0:.*]] = memref.alloca() : memref<i32>
+// CHECK: memref.store %[[ARG0]], %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_1:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_2:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_3:.*]] = arith.andi %[[VAL_1]], %[[VAL_2]] : i32
+// CHECK: %[[VAL_4:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_5:.*]] = arith.cmpi ne, %[[VAL_3]], %[[VAL_4]] : i32
+// CHECK: cf.cond_br %[[VAL_5]], ^bb1, ^bb2
+// CHECK: ^bb1:
+// CHECK: %[[VAL_6:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_7:.*]] = arith.constant 3 : i32
+// CHECK: %[[VAL_8:.*]] = arith.muli %[[VAL_6]], %[[VAL_7]] : i32
+// CHECK: %[[VAL_9:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_10:.*]] = arith.addi %[[VAL_8]], %[[VAL_9]] : i32
+// CHECK: cf.br ^bb3(%[[VAL_10]] : i32)
+// CHECK: ^bb2:
+// CHECK: %[[VAL_11:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_12:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_13:.*]] = arith.shrui %[[VAL_11]], %[[VAL_12]] : i32
+// CHECK: cf.br ^bb3(%[[VAL_13]] : i32)
+// CHECK: ^bb3(%[[VAL_14:.*]]: i32):
+// CHECK: return %[[VAL_14]] : i32
+wasmssa.func nested @if(%arg0: !wasmssa<local ref to i32>) -> i32 {
+ %1 = wasmssa.local_get %arg0 : ref to i32
+ %2 = wasmssa.const 1 : i32
+ %3 = wasmssa.and %1 %2 : i32
+ "wasmssa.if"(%3)[^bb1] ({
+ %5 = wasmssa.local_get %arg0 : ref to i32
+ %6 = wasmssa.const 3 : i32
+ %7 = wasmssa.mul %5 %6 : i32
+ %8 = wasmssa.const 1 : i32
+ %9 = wasmssa.add %7 %8 : i32
+ wasmssa.block_return %9 : i32
+ }, {
+ %5 = wasmssa.local_get %arg0 : ref to i32
+ %6 = wasmssa.const 1 : i32
+ %7 = wasmssa.shr_u %5 by %6 bits : i32
+ wasmssa.block_return %7 : i32
+ }) : (i32) -> ()
+^bb1(%4: i32): // pred: ^bb0
+ wasmssa.return %4 : i32
+}
+
+// CHECK-LABEL: func.func @if_else(
+// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
+// CHECK: %[[VAL_0:.*]] = memref.alloca() : memref<i32>
+// CHECK: memref.store %[[ARG0]], %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_1:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_2:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_3:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_4:.*]] = arith.andi %[[VAL_2]], %[[VAL_3]] : i32
+// CHECK: %[[VAL_5:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_6:.*]] = arith.cmpi ne, %[[VAL_4]], %[[VAL_5]] : i32
+// CHECK: cf.cond_br %[[VAL_6]], ^bb1(%[[VAL_1]] : i32), ^bb2(%[[VAL_1]] : i32)
+// CHECK: ^bb1(%[[VAL_7:.*]]: i32):
+// CHECK: %[[VAL_8:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_9:.*]] = arith.addi %[[VAL_7]], %[[VAL_8]] : i32
+// CHECK: cf.br ^bb2(%[[VAL_9]] : i32)
+// CHECK: ^bb2(%[[VAL_10:.*]]: i32):
+// CHECK: return %[[VAL_10]] : i32
+wasmssa.func nested @if_else(%arg0: !wasmssa<local ref to i32>) -> i32 {
+ %1 = wasmssa.local_get %arg0 : ref to i32
+ %2 = wasmssa.local_get %arg0 : ref to i32
+ %3 = wasmssa.const 1 : i32
+ %4 = wasmssa.and %2 %3 : i32
+ "wasmssa.if"(%4, %1)[^bb1] ({
+ ^bb0(%arg1: i32):
+ %6 = wasmssa.const 1 : i32
+ %7 = wasmssa.add %arg1 %6 : i32
+ wasmssa.block_return %7 : i32
+ }, {
+ }) : (i32, i32) -> ()
+^bb1(%5: i32): // pred: ^bb0
+ wasmssa.return %5 : i32
+}
+
+// CHECK-LABEL: func.func @if_if(
+// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32 {
+// CHECK: %[[VAL_0:.*]] = memref.alloca() : memref<i32>
+// CHECK: memref.store %[[ARG0]], %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_1:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_2:.*]] = math.cttz %[[VAL_1]] : i32
+// CHECK: %[[VAL_3:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_4:.*]] = arith.cmpi ne, %[[VAL_2]], %[[VAL_3]] : i32
+// CHECK: cf.cond_br %[[VAL_4]], ^bb1, ^bb4
+// CHECK: ^bb1:
+// CHECK: %[[VAL_5:.*]] = arith.constant 2 : i32
+// CHECK: %[[VAL_6:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_7:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_8:.*]] = arith.shrui %[[VAL_6]], %[[VAL_7]] : i32
+// CHECK: %[[VAL_9:.*]] = math.cttz %[[VAL_8]] : i32
+// CHECK: %[[VAL_10:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_11:.*]] = arith.cmpi ne, %[[VAL_9]], %[[VAL_10]] : i32
+// CHECK: cf.cond_br %[[VAL_11]], ^bb2(%[[VAL_5]] : i32), ^bb3(%[[VAL_5]] : i32)
+// CHECK: ^bb2(%[[VAL_12:.*]]: i32):
+// CHECK: %[[VAL_13:.*]] = arith.constant 2 : i32
+// CHECK: %[[VAL_14:.*]] = arith.addi %[[VAL_12]], %[[VAL_13]] : i32
+// CHECK: cf.br ^bb3(%[[VAL_14]] : i32)
+// CHECK: ^bb3(%[[VAL_15:.*]]: i32):
+// CHECK: cf.br ^bb5(%[[VAL_15]] : i32)
+// CHECK: ^bb4:
+// CHECK: %[[VAL_16:.*]] = arith.constant 1 : i32
+// CHECK: cf.br ^bb5(%[[VAL_16]] : i32)
+// CHECK: ^bb5(%[[VAL_17:.*]]: i32):
+// CHECK: return %[[VAL_17]] : i32
+wasmssa.func nested @if_if(%arg0: !wasmssa<local ref to i32>) -> i32 {
+ %1 = wasmssa.local_get %arg0 : ref to i32
+ %2 = wasmssa.ctz %1 : i32
+ "wasmssa.if"(%2)[^bb1] ({
+ %4 = wasmssa.const 2 : i32
+ %5 = wasmssa.local_get %arg0 : ref to i32
+ %6 = wasmssa.const 1 : i32
+ %7 = wasmssa.shr_u %5 by %6 bits : i32
+ %8 = wasmssa.ctz %7 : i32
+ "wasmssa.if"(%8, %4)[^bb1] ({
+ ^bb0(%arg1: i32):
+ %10 = wasmssa.const 2 : i32
+ %11 = wasmssa.add %arg1 %10 : i32
+ wasmssa.block_return %11 : i32
+ }, {
+ }) : (i32, i32) -> ()
+ ^bb1(%9: i32): // pred: ^bb0
+ wasmssa.block_return %9 : i32
+ }, {
+ %4 = wasmssa.const 1 : i32
+ wasmssa.block_return %4 : i32
+ }) : (i32) -> ()
+^bb1(%3: i32): // pred: ^bb0
+ wasmssa.return %3 : i32
+}
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir b/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir
new file mode 100644
index 0000000000000..e49909cc9417e
--- /dev/null
+++ b/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir
@@ -0,0 +1,206 @@
+// RUN: mlir-opt --split-input-file %s --raise-wasm-mlir -o - | FileCheck %s
+// RUN: mlir-opt --split-input-file %s --raise-wasm-mlir --canonicalize -o - | FileCheck --check-prefix=CHECK-CANONICAL %s
+
+module {
+ wasmssa.func nested @func_0() {
+ wasmssa.loop : {
+ wasmssa.block_return
+ }> ^bb1
+ ^bb1: // pred: ^bb0
+ wasmssa.return
+ }
+}
+
+// CHECK-LABEL: module {
+// CHECK: func.func @func_0() {
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: cf.br ^bb2
+// CHECK: ^bb2:
+// CHECK: return
+// CHECK: }
+
+// CHECK-CANONICAL-LABEL: func.func @func_0() {
+// CHECK-CANONICAL: return
+// CHECK-CANONICAL: }
+
+// -----
+
+module {
+ wasmssa.func nested @func_0() -> i32 {
+ %0 = wasmssa.local of type i32
+ wasmssa.loop : {
+ %1 = wasmssa.local_get %0 : ref to i32
+ %2 = wasmssa.const 10 : i32
+ %3 = wasmssa.lt_si %1 %2 : i32 -> i32
+ wasmssa.block_return %3 : i32
+ }> ^bb1
+ ^bb1(%1: i32): // pred: ^bb0
+ wasmssa.return %1 : i32
+ }
+}
+// CHECK-LABEL: func.func @func_0() -> i32 {
+// CHECK: %[[VAL_0:.*]] = memref.alloca() : memref<i32>
+// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK: memref.store %[[VAL_1]], %[[VAL_0]][] : memref<i32>
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: %[[VAL_2:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_3:.*]] = arith.constant 10 : i32
+// CHECK: %[[VAL_4:.*]] = arith.cmpi slt, %[[VAL_2]], %[[VAL_3]] : i32
+// CHECK: %[[VAL_5:.*]] = arith.extui %[[VAL_4]] : i1 to i32
+// CHECK: cf.br ^bb2(%[[VAL_5]] : i32)
+// CHECK: ^bb2(%[[VAL_6:.*]]: i32):
+// CHECK: return %[[VAL_6]] : i32
+// CHECK: }
+
+// CHECK-CANONICAL-LABEL: func.func @func_0() -> i32 {
+// CHECK-CANONICAL: %[[VAL_0:.*]] = arith.constant 10 : i32
+// CHECK-CANONICAL: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK-CANONICAL: %[[VAL_2:.*]] = memref.alloca() : memref<i32>
+// CHECK-CANONICAL: memref.store %[[VAL_1]], %[[VAL_2]][] : memref<i32>
+// CHECK-CANONICAL: %[[VAL_3:.*]] = memref.load %[[VAL_2]][] : memref<i32>
+// CHECK-CANONICAL: %[[VAL_4:.*]] = arith.cmpi slt, %[[VAL_3]], %[[VAL_0]] : i32
+// CHECK-CANONICAL: %[[VAL_5:.*]] = arith.extui %[[VAL_4]] : i1 to i32
+// CHECK-CANONICAL: return %[[VAL_5]] : i32
+
+// -----
+
+module {
+ wasmssa.func nested @func_0() {
+ %0 = wasmssa.local of type i32
+ wasmssa.loop : {
+ %1 = wasmssa.local_get %0 : ref to i32
+ %2 = wasmssa.const 10 : i32
+ %3 = wasmssa.lt_si %1 %2 : i32 -> i32
+ wasmssa.branch_if %3 to level 0 else ^bb1
+ ^bb1: // pred: ^bb0
+ wasmssa.block_return
+ }> ^bb1
+ ^bb1: // pred: ^bb0
+ wasmssa.return
+ }
+}
+
+// CHECK-LABEL: func.func @func_0() {
+// CHECK: %[[VAL_0:.*]] = memref.alloca() : memref<i32>
+// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK: memref.store %[[VAL_1]], %[[VAL_0]][] : memref<i32>
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: %[[VAL_2:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_3:.*]] = arith.constant 10 : i32
+// CHECK: %[[VAL_4:.*]] = arith.cmpi slt, %[[VAL_2]], %[[VAL_3]] : i32
+// CHECK: %[[VAL_5:.*]] = arith.extui %[[VAL_4]] : i1 to i32
+// CHECK: %[[VAL_6:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_7:.*]] = arith.cmpi ne, %[[VAL_5]], %[[VAL_6]] : i32
+// CHECK: cf.cond_br %[[VAL_7]], ^bb1, ^bb2
+// CHECK: ^bb2:
+// CHECK: cf.br ^bb3
+// CHECK: ^bb3:
+// CHECK: return
+// CHECK: }
+
+// CHECK-CANONICAL-LABEL: func.func @func_0() {
+// CHECK-CANONICAL: %[[VAL_0:.*]] = arith.constant 10 : i32
+// CHECK-CANONICAL: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK-CANONICAL: %[[VAL_2:.*]] = memref.alloca() : memref<i32>
+// CHECK-CANONICAL: memref.store %[[VAL_1]], %[[VAL_2]][] : memref<i32>
+// CHECK-CANONICAL: cf.br ^bb1
+// CHECK-CANONICAL: ^bb1:
+// CHECK-CANONICAL: %[[VAL_3:.*]] = memref.load %[[VAL_2]][] : memref<i32>
+// CHECK-CANONICAL: %[[VAL_4:.*]] = arith.cmpi slt, %[[VAL_3]], %[[VAL_0]] : i32
+// CHECK-CANONICAL: cf.cond_br %[[VAL_4]], ^bb1, ^bb2
+// CHECK-CANONICAL: ^bb2:
+// CHECK-CANONICAL: return
+
+// -----
+
+module {
+ wasmssa.func nested @func_0() {
+ %0 = wasmssa.local of type i32
+ %1 = wasmssa.local of type i32
+ wasmssa.loop : {
+ %2 = wasmssa.local_get %0 : ref to i32
+ %3 = wasmssa.const 1 : i32
+ %4 = wasmssa.add %2 %3 : i32
+ wasmssa.local_set %0 : ref to i32 to %4 : i32
+ wasmssa.loop : {
+ %8 = wasmssa.const 12 : i32
+ %9 = wasmssa.local_get %0 : ref to i32
+ %10 = wasmssa.gt_si %8 %9 : i32 -> i32
+ wasmssa.branch_if %10 to level 0 else ^bb1
+ ^bb1: // pred: ^bb0
+ wasmssa.block_return %8 : i32
+ }> ^bb1
+ ^bb1(%5: i32): // pred: ^bb0
+ %6 = wasmssa.const 10 : i32
+ %7 = wasmssa.lt_si %5 %6 : i32 -> i32
+ wasmssa.branch_if %7 to level 0 else ^bb2
+ ^bb2: // pred: ^bb1
+ wasmssa.block_return
+ }> ^bb1
+ ^bb1: // pred: ^bb0
+ wasmssa.return
+ }
+}
+
+// CHECK-LABEL: func.func @func_0() {
+// CHECK: %[[VAL_0:.*]] = memref.alloca() : memref<i32>
+// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i32
+// CHECK: memref.store %[[VAL_1]], %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_2:.*]] = memref.alloca() : memref<i32>
+// CHECK: %[[VAL_3:.*]] = arith.constant 0 : i32
+// CHECK: memref.store %[[VAL_3]], %[[VAL_2]][] : memref<i32>
+// CHECK: cf.br ^bb1
+// CHECK: ^bb1:
+// CHECK: %[[VAL_4:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_5:.*]] = arith.constant 1 : i32
+// CHECK: %[[VAL_6:.*]] = arith.addi %[[VAL_4]], %[[VAL_5]] : i32
+// CHECK: memref.store %[[VAL_6]], %[[VAL_0]][] : memref<i32>
+// CHECK: cf.br ^bb2
+// CHECK: ^bb2:
+// CHECK: %[[VAL_7:.*]] = arith.constant 12 : i32
+// CHECK: %[[VAL_8:.*]] = memref.load %[[VAL_0]][] : memref<i32>
+// CHECK: %[[VAL_9:.*]] = arith.cmpi sgt, %[[VAL_7]], %[[VAL_8]] : i32
+// CHECK: %[[VAL_10:.*]] = arith.extui %[[VAL_9]] : i1 to i32
+// CHECK: %[[VAL_11:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_12:.*]] = arith.cmpi ne, %[[VAL_10]], %[[VAL_11]] : i32
+// CHECK: cf.cond_br %[[VAL_12]], ^bb2, ^bb3
+// CHECK: ^bb3:
+// CHECK: cf.br ^bb4(%[[VAL_7]] : i32)
+// CHECK: ^bb4(%[[VAL_13:.*]]: i32):
+// CHECK: %[[VAL_14:.*]] = arith.constant 10 : i32
+// CHECK: %[[VAL_15:.*]] = arith.cmpi slt, %[[VAL_13]], %[[VAL_14]] : i32
+// CHECK: %[[VAL_16:.*]] = arith.extui %[[VAL_15]] : i1 to i32
+// CHECK: %[[VAL_17:.*]] = arith.constant 0 : i32
+// CHECK: %[[VAL_18:.*]] = arith.cmpi ne, %[[VAL_16]], %[[VAL_17]] : i32
+// CHECK: cf.cond_br %[[VAL_18]], ^bb1, ^bb5
+// CHECK: ^bb5:
+// CHECK: cf.br ^bb6
+// CHECK: ^bb6:
+// CHECK: return
+// CHECK: }
+
+// CHECK-CANONICAL-LABEL: func.func @func_0() {
+// CHECK-CANONICAL: %[[VAL_0:.*]] = arith.constant 10 : i32
+// CHECK-CANONICAL: %[[VAL_1:.*]] = arith.constant 12 : i32
+// CHECK-CANONICAL: %[[VAL_2:.*]] = arith.constant 1 : i32
+// CHECK-CANONICAL: %[[VAL_3:.*]] = arith.constant 0 : i32
+// CHECK-CANONICAL: %[[VAL_4:.*]] = memref.alloca() : memref<i32>
+// CHECK-CANONICAL: memref.store %[[VAL_3]], %[[VAL_4]][] : memref<i32>
+// CHECK-CANONICAL: cf.br ^bb1
+// CHECK-CANONICAL: ^bb1:
+// CHECK-CANONICAL: %[[VAL_5:.*]] = memref.load %[[VAL_4]][] : memref<i32>
+// CHECK-CANONICAL: %[[VAL_6:.*]] = arith.addi %[[VAL_5]], %[[VAL_2]] : i32
+// CHECK-CANONICAL: memref.store %[[VAL_6]], %[[VAL_4]][] : memref<i32>
+// CHECK-CANONICAL: cf.br ^bb2
+// CHECK-CANONICAL: ^bb2:
+// CHECK-CANONICAL: %[[VAL_7:.*]] = memref.load %[[VAL_4]][] : memref<i32>
+// CHECK-CANONICAL: %[[VAL_8:.*]] = arith.cmpi slt, %[[VAL_7]], %[[VAL_1]] : i32
+// CHECK-CANONICAL: cf.cond_br %[[VAL_8]], ^bb2, ^bb3(%[[VAL_1]] : i32)
+// CHECK-CANONICAL: ^bb3(%[[VAL_9:.*]]: i32):
+// CHECK-CANONICAL: %[[VAL_10:.*]] = arith.cmpi slt, %[[VAL_9]], %[[VAL_0]] : i32
+// CHECK-CANONICAL: cf.cond_br %[[VAL_10]], ^bb1, ^bb4
+// CHECK-CANONICAL: ^bb4:
+// CHECK-CANONICAL: return
>From 2af7f4e6300c16db66abf0ad83efa9e338ca21bd Mon Sep 17 00:00:00 2001
From: Ferdinand Lemaire <ferdinand.lemaire at woven-planet.global>
Date: Thu, 3 Jul 2025 13:36:04 +0900
Subject: [PATCH 4/7] [mlir][wasm] RaiseWasmMLIRPass: Add support for memory
--
Co-authored-by: Luc Forget <luc.forget at woven-planet.global>
Co-authored-by: Jessica Paquette <jessica.paquette at woven-planet.global>
---
.../Conversion/RaiseWasm/RaiseWasmMLIR.cpp | 41 +++++++++++++++++++
.../RaiseWasm/wasm-memory-to-memref.mlir | 14 +++++++
2 files changed, 55 insertions(+)
create mode 100644 mlir/test/Conversion/RaiseWasm/wasm-memory-to-memref.mlir
diff --git a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
index d9dcb756790f8..53a8900b965a3 100644
--- a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
+++ b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
@@ -677,6 +677,46 @@ struct WasmGlobalWithGetGlobalInitConversion
}
};
+struct WasmMemoryOpConversion : OpConversionPattern<MemOp> {
+ using OpConversionPattern::OpConversionPattern;
+
+ LogicalResult
+ matchAndRewrite(MemOp memOp, MemOp::Adaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ auto loc = memOp.getLoc();
+ auto bufferType =
+ MemRefType::get({ShapedType::kDynamic}, rewriter.getI8Type());
+ auto bufferPtrType = MemRefType::get({1}, bufferType);
+ auto memPtr = rewriter.replaceOpWithNewOp<memref::GlobalOp>(
+ memOp, memOp.getSymNameAttr(), memOp.getSymVisibilityAttr(),
+ TypeAttr::get(bufferPtrType), /*initialValue*/ rewriter.getUnitAttr(),
+ /*constant*/ UnitAttr{}, /*alignment*/ IntegerAttr{});
+ auto initializerName = (memPtr.getSymName() + "::initializer").str();
+ auto memInitializer = rewriter.create<func::FuncOp>(
+ loc, initializerName, FunctionType::get(getContext(), {}, {}));
+ memInitializer->setAttr(rewriter.getStringAttr("initializer"),
+ rewriter.getUnitAttr());
+ auto *initializerBody = memInitializer.addEntryBlock();
+ auto sip = rewriter.saveInsertionPoint();
+ rewriter.setInsertionPointToStart(initializerBody);
+ auto memRefPtr = rewriter.create<memref::GetGlobalOp>(
+ loc, MemRefType::get({1}, bufferType), memPtr.getSymName());
+ auto alloc = rewriter.create<memref::AllocOp>(
+ loc,
+ MemRefType::get({memOp.getLimits().getMin()}, rewriter.getI8Type()));
+ auto castOp =
+ rewriter.create<memref::CastOp>(loc, bufferType, alloc.getResult());
+ auto idx = rewriter.create<arith::ConstantIndexOp>(loc, 0);
+ rewriter.create<memref::StoreOp>(loc, castOp.getResult(),
+ memRefPtr.getResult(),
+ ValueRange{idx.getResult()});
+ rewriter.create<func::ReturnOp>(loc);
+ rewriter.restoreInsertionPoint(sip);
+ rewriter.create<func::CallOp>(loc, memInitializer);
+ return success();
+ }
+};
+
inline TypedAttr getInitializerAttr(Type t) {
assert(t.isIntOrFloat() &&
"This helper is intended to use with int and float types");
@@ -847,6 +887,7 @@ void mlir::populateRaiseWasmMLIRConversionPatterns(
WasmLtSIOpConversion,
WasmLtUIOpConversion,
WasmMaxOpConversion,
+ WasmMemoryOpConversion,
WasmMinOpConversion,
WasmMulOpConversion,
WasmNeOpConversion,
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-memory-to-memref.mlir b/mlir/test/Conversion/RaiseWasm/wasm-memory-to-memref.mlir
new file mode 100644
index 0000000000000..09b07184d97fa
--- /dev/null
+++ b/mlir/test/Conversion/RaiseWasm/wasm-memory-to-memref.mlir
@@ -0,0 +1,14 @@
+// RUN: mlir-opt %s --raise-wasm-mlir | FileCheck %s
+
+"wasmssa.memory"() <{limits = !wasmssa<limit[17:]>, sym_name = "mem_0", sym_visibility = "nested"}> : () -> ()
+
+// CHECK-LABEL: memref.global "nested" @mem_0 : memref<1xmemref<?xi8>>
+
+// CHECK-LABEL: func.func @"mem_0::initializer"() attributes {initializer} {
+// CHECK: %[[VAL_0:.*]] = memref.get_global @mem_0 : memref<1xmemref<?xi8>>
+// CHECK: %[[VAL_1:.*]] = memref.alloc() : memref<17xi8>
+// CHECK: %[[VAL_2:.*]] = memref.cast %[[VAL_1]] : memref<17xi8> to memref<?xi8>
+// CHECK: %[[VAL_3:.*]] = arith.constant 0 : index
+// CHECK: memref.store %[[VAL_2]], %[[VAL_0]]{{\[}}%[[VAL_3]]] : memref<1xmemref<?xi8>>
+// CHECK: return
+// CHECK: func.call @"mem_0::initializer"() : () -> ()
>From ec4763983eff03797ce2a4f1d60e296b250450a1 Mon Sep 17 00:00:00 2001
From: Ferdinand Lemaire <ferdinand.lemaire at woven-planet.global>
Date: Thu, 30 Apr 2026 21:00:48 +0900
Subject: [PATCH 5/7] [mlir][wasm] Deprecate rewriter.create in favor of
<op>::create and cleanup the tests
---
.../Conversion/RaiseWasm/RaiseWasmMLIR.cpp | 123 ++++++++----------
.../RaiseWasm/wasm-blocks-to-cf.mlir | 18 +--
.../wasm-comparisons-to-arith-cmp.mlir | 64 ++++-----
.../RaiseWasm/wasm-extend-to-arith-ext.mlir | 14 +-
.../Conversion/RaiseWasm/wasm-loop-to-cf.mlir | 8 +-
.../RaiseWasm/wasm-rotl-to-arith.mlir | 4 +-
.../RaiseWasm/wasm-rotr-to-arith.mlir | 4 +-
7 files changed, 112 insertions(+), 123 deletions(-)
diff --git a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
index 53a8900b965a3..0e6e0c05b79b6 100644
--- a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
+++ b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
@@ -20,6 +20,7 @@
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/Vector/IR/VectorOps.h"
#include "mlir/Dialect/WasmSSA/IR/WasmSSA.h"
+#include "mlir/Dialect/WasmSSA/IR/WasmSSAInterfaces.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/IR/ValueRange.h"
@@ -159,25 +160,22 @@ struct RotateOpConversion : OpConversionPattern<SourceOp> {
// Materialize (width - 1) for use in both sides of the expression.
auto cstWidthMinusOne =
- rewriter.create<ConstOp>(loc, IntegerAttr::get(ty, width - 1));
+ ConstOp::create(rewriter, loc, IntegerAttr::get(ty, width - 1));
// Form the left-hand side of the OR:
// (val (lhs shift op) (bits & (width - 1)))
- auto orLHS = rewriter.create<LHSShiftOp>(
- loc, val, rewriter.create<AndOp>(loc, bits, cstWidthMinusOne));
+ auto orLHS = LHSShiftOp::create(
+ rewriter, loc, val, AndOp::create(rewriter, loc, bits, cstWidthMinusOne));
// Form the right-hand side of the OR:
// (val (rhs shift op) (-bits & (width - 1)))
- auto orRHS = rewriter.create<RHSShiftOp>(
- loc, val,
+ auto orRHS = RHSShiftOp::create(
+ rewriter, loc, val,
// (-bits & (width - 1))
- rewriter.create<AndOp>(
- loc,
+ AndOp::create(
+ rewriter, loc,
// 0 - bits == -bits
- rewriter.create<SubOp>(
- loc,
- rewriter.create<ConstOp>(loc, IntegerAttr::get(ty, 0)),
- bits),
+ SubOp::create(rewriter, loc, ConstOp::create(rewriter, loc, IntegerAttr::get(ty, 0)), bits),
cstWidthMinusOne));
// OR together the two shifts and replace the rotate with the new
@@ -199,8 +197,7 @@ struct ComparisonOpConversion : OpConversionPattern<SourceOp> {
matchAndRewrite(SourceOp srcOp, typename SourceOp::Adaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
auto cmpRes =
- rewriter
- .create<TargetOp>(srcOp.getLoc(), rewriter.getI1Type(),
+ TargetOp::create(rewriter, srcOp.getLoc(), rewriter.getI1Type(),
AttrType::get(rewriter.getContext(), flag),
adaptor.getLhs(), adaptor.getRhs())
.getResult();
@@ -257,19 +254,15 @@ struct IntFpComparisonOpConversion : OpConversionPattern<SourceOp> {
Value comparisonResult;
if (srcOp.getLhs().getType().isInteger())
comparisonResult =
- rewriter
- .create<arith::CmpIOp>(
- srcOp.getLoc(), rewriter.getI1Type(),
- arith::CmpIPredicateAttr::get(rewriter.getContext(), IntFlag),
- adaptor.getLhs(), adaptor.getRhs())
+ arith::CmpIOp::create(rewriter, srcOp.getLoc(), rewriter.getI1Type(),
+ arith::CmpIPredicateAttr::get(rewriter.getContext(), IntFlag),
+ adaptor.getLhs(), adaptor.getRhs())
.getResult();
else if (srcOp.getLhs().getType().isFloat())
comparisonResult =
- rewriter
- .create<arith::CmpFOp>(srcOp.getLoc(), rewriter.getI1Type(),
- arith::CmpFPredicateAttr::get(
- rewriter.getContext(), FloatFlag),
- adaptor.getLhs(), adaptor.getRhs())
+ arith::CmpFOp::create(rewriter, srcOp.getLoc(), rewriter.getI1Type(),
+ arith::CmpFPredicateAttr::get(rewriter.getContext(), FloatFlag),
+ adaptor.getLhs(), adaptor.getRhs())
.getResult();
else
return rewriter.notifyMatchFailure(
@@ -320,13 +313,9 @@ struct WasmEqzOpConversion : OpConversionPattern<EqzOp> {
ConversionPatternRewriter &rewriter) const override {
auto loc = eqzOp->getLoc();
auto zero =
- rewriter
- .create<arith::ConstantOp>(
- loc, rewriter.getIntegerAttr(adaptor.getInput().getType(), 0))
+ arith::ConstantOp::create(rewriter, loc, rewriter.getIntegerAttr(adaptor.getInput().getType(), 0))
.getResult();
- auto cmpRes = rewriter
- .create<arith::CmpIOp>(
- loc, rewriter.getI1Type(),
+ auto cmpRes = arith::CmpIOp::create(rewriter, loc, rewriter.getI1Type(),
arith::CmpIPredicateAttr::get(
rewriter.getContext(), arith::CmpIPredicate::eq),
adaptor.getInput(), zero)
@@ -346,9 +335,7 @@ struct WasmExtendLowBitsOpConversion : OpConversionPattern<ExtendLowBitsSOp> {
ExtendLowBitsSOp::Adaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
auto truncWidth = extendLowBytesSOp.getBitsToTake().getInt();
- auto truncation = rewriter.create<arith::TruncIOp>(
- extendLowBytesSOp->getLoc(), rewriter.getIntegerType(truncWidth),
- adaptor.getInput());
+ auto truncation = arith::TruncIOp::create(rewriter, extendLowBytesSOp->getLoc(), rewriter.getIntegerType(truncWidth), adaptor.getInput());
rewriter.replaceOpWithNewOp<arith::ExtSIOp>(
extendLowBytesSOp, extendLowBytesSOp.getResult().getType(),
truncation.getResult());
@@ -380,15 +367,11 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
class CFRewriterVisitor {
private:
using branch_to_dest_t =
- llvm::DenseMap<WasmSSALabelBranchingInterface, Block *>;
+ llvm::DenseMap<LabelBranchingOpInterface, Block *>;
Value getCompResultAsI1(Value compResult,
ConversionPatternRewriter &rewriter) {
- auto testValue = rewriter.create<arith::ConstantOp>(
- compResult.getLoc(), rewriter.getI32IntegerAttr(0));
- auto flag = rewriter
- .create<arith::CmpIOp>(
- compResult.getLoc(), rewriter.getIntegerType(1),
- arith::CmpIPredicate::ne, compResult, testValue)
+ auto testValue = arith::ConstantOp::create(rewriter, compResult.getLoc(), rewriter.getI32IntegerAttr(0));
+ auto flag = arith::CmpIOp::create(rewriter, compResult.getLoc(), rewriter.getIntegerType(1), arith::CmpIPredicate::ne, compResult, testValue)
.getResult();
return flag;
}
@@ -420,7 +403,7 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
template <typename LevelType>
LogicalResult
- replaceNestLevelWithBranchWrapper(WasmSSALabelLevelInterface nestingOp,
+ replaceNestLevelWithBranchWrapper(LabelLevelOpInterface nestingOp,
llvm::ArrayRef<Block *> regionsToEntry,
ConversionPatternRewriter &rewriter) {
auto cast = dyn_cast<LevelType>(nestingOp.getOperation());
@@ -431,7 +414,7 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
}
template <typename... LevelTypes>
- LogicalResult inlineNestDispatcher(WasmSSALabelLevelInterface nestingOp,
+ LogicalResult inlineNestDispatcher(LabelLevelOpInterface nestingOp,
ConversionPatternRewriter &rewriter) {
auto sip = rewriter.saveInsertionPoint();
Block *blockSuccessor = nestingOp->getSuccessor(0);
@@ -443,8 +426,8 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
continue;
regionEntries.push_back(®ion.front());
/// Inline blocks of nested ops
- llvm::SmallVector<WasmSSALabelLevelInterface> nestedOps{
- region.getOps<WasmSSALabelLevelInterface>()};
+ llvm::SmallVector<LabelLevelOpInterface> nestedOps{
+ region.getOps<LabelLevelOpInterface>()};
for (auto nestedOp : nestedOps) {
LLVM_DEBUG(llvm::dbgs() << " Found nested op: " << nestedOp);
if (failed(inlineBlocks(nestedOp, rewriter)))
@@ -466,12 +449,12 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
}
/// Take a nesting level defining op and inline it in the parent region.
- LogicalResult inlineBlocks(WasmSSALabelLevelInterface nestingOp,
+ LogicalResult inlineBlocks(LabelLevelOpInterface nestingOp,
ConversionPatternRewriter &rewriter) {
return inlineNestDispatcher<BlockOp, IfOp, LoopOp>(nestingOp, rewriter);
}
- llvm::FailureOr<Block *> getBlockFor(WasmSSALabelBranchingInterface branchOp) {
+ llvm::FailureOr<Block *> getBlockFor(LabelBranchingOpInterface branchOp) {
auto destIter = branchToDest.find(branchOp);
if (destIter == branchToDest.end())
return branchOp->emitError("No indexed label op for this operation: ")
@@ -494,7 +477,7 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
template <typename LevelInterfaceT>
inline LogicalResult
- convertBranchWrapper(WasmSSALabelBranchingInterface branchOp, Block *dest,
+ convertBranchWrapper(LabelBranchingOpInterface branchOp, Block *dest,
ConversionPatternRewriter &rewriter) {
auto cast = dyn_cast<LevelInterfaceT>(branchOp.getOperation());
if (!cast)
@@ -507,7 +490,7 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
}
template <typename... BranchInterfaceT>
- LogicalResult convertBranchDispatch(WasmSSALabelBranchingInterface branchOp,
+ LogicalResult convertBranchDispatch(LabelBranchingOpInterface branchOp,
ConversionPatternRewriter &rewriter) {
auto dest = getBlockFor(branchOp);
if (failed(dest))
@@ -521,7 +504,7 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
return res;
}
- LogicalResult convertBranch(WasmSSALabelBranchingInterface branchOp,
+ LogicalResult convertBranch(LabelBranchingOpInterface branchOp,
ConversionPatternRewriter &rewriter) {
return convertBranchDispatch<BlockReturnOp, BranchIfOp>(branchOp,
rewriter);
@@ -532,19 +515,19 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
public:
CFRewriterVisitor(func::FuncOp func) : func{func} {
- func.walk([this](WasmSSALabelBranchingInterface branchOp) {
+ func.walk([this](LabelBranchingOpInterface branchOp) {
branchToDest.insert({branchOp, branchOp.getTarget()});
});
}
LogicalResult rewrite(ConversionPatternRewriter &rewriter) {
- llvm::SmallVector<WasmSSALabelLevelInterface> nestingOps{
- func.getOps<WasmSSALabelLevelInterface>()};
+ llvm::SmallVector<LabelLevelOpInterface> nestingOps{
+ func.getOps<LabelLevelOpInterface>()};
for (auto nestingOp : nestingOps)
if (failed(inlineBlocks(nestingOp, rewriter)))
return failure();
auto res =
- func->walk([this, &rewriter](WasmSSALabelBranchingInterface branchOp) {
+ func->walk([this, &rewriter](LabelBranchingOpInterface branchOp) {
if (failed(convertBranch(branchOp, rewriter)))
return WalkResult::interrupt();
return WalkResult::advance();
@@ -687,32 +670,38 @@ struct WasmMemoryOpConversion : OpConversionPattern<MemOp> {
auto bufferType =
MemRefType::get({ShapedType::kDynamic}, rewriter.getI8Type());
auto bufferPtrType = MemRefType::get({1}, bufferType);
+ auto memVisibility = memOp.getVisibility();
+ // Convert to StringAttr since memref::GlobalOp expects visibility as a string attribute.
+ mlir::StringAttr visAttr;
+ if (memVisibility == mlir::SymbolTable::Visibility::Public)
+ visAttr = mlir::StringAttr::get(memOp->getContext(), "public");
+ else if (memVisibility == mlir::SymbolTable::Visibility::Private)
+ visAttr = mlir::StringAttr::get(memOp->getContext(), "private");
+ else
+ visAttr = mlir::StringAttr::get(memOp->getContext(), "nested");
+
auto memPtr = rewriter.replaceOpWithNewOp<memref::GlobalOp>(
- memOp, memOp.getSymNameAttr(), memOp.getSymVisibilityAttr(),
+ memOp, memOp.getSymNameAttr(), visAttr,
TypeAttr::get(bufferPtrType), /*initialValue*/ rewriter.getUnitAttr(),
/*constant*/ UnitAttr{}, /*alignment*/ IntegerAttr{});
auto initializerName = (memPtr.getSymName() + "::initializer").str();
- auto memInitializer = rewriter.create<func::FuncOp>(
- loc, initializerName, FunctionType::get(getContext(), {}, {}));
+ auto memInitializer = func::FuncOp::create(rewriter, loc, initializerName,
+ FunctionType::get(getContext(), {}, {}));
memInitializer->setAttr(rewriter.getStringAttr("initializer"),
rewriter.getUnitAttr());
auto *initializerBody = memInitializer.addEntryBlock();
auto sip = rewriter.saveInsertionPoint();
rewriter.setInsertionPointToStart(initializerBody);
- auto memRefPtr = rewriter.create<memref::GetGlobalOp>(
- loc, MemRefType::get({1}, bufferType), memPtr.getSymName());
- auto alloc = rewriter.create<memref::AllocOp>(
- loc,
- MemRefType::get({memOp.getLimits().getMin()}, rewriter.getI8Type()));
+ auto memRefPtr = memref::GetGlobalOp::create(rewriter, loc, MemRefType::get({1}, bufferType), memPtr.getSymName());
+ auto alloc = memref::AllocOp::create(rewriter, loc, MemRefType::get({memOp.getLimits().getMin()}, rewriter.getI8Type()));
auto castOp =
- rewriter.create<memref::CastOp>(loc, bufferType, alloc.getResult());
- auto idx = rewriter.create<arith::ConstantIndexOp>(loc, 0);
- rewriter.create<memref::StoreOp>(loc, castOp.getResult(),
- memRefPtr.getResult(),
- ValueRange{idx.getResult()});
- rewriter.create<func::ReturnOp>(loc);
+ memref::CastOp::create(rewriter, loc, bufferType, alloc.getResult());
+ auto idx = arith::ConstantIndexOp::create(rewriter, loc, 0);
+ memref::StoreOp::create(rewriter, loc, castOp.getResult(),
+ memRefPtr.getResult(), ValueRange{idx.getResult()});
+ func::ReturnOp::create(rewriter, loc);
rewriter.restoreInsertionPoint(sip);
- rewriter.create<func::CallOp>(loc, memInitializer);
+ func::CallOp::create(rewriter, loc, memInitializer);
return success();
}
};
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-blocks-to-cf.mlir b/mlir/test/Conversion/RaiseWasm/wasm-blocks-to-cf.mlir
index 3b275573bcbd3..fdce71cab096b 100644
--- a/mlir/test/Conversion/RaiseWasm/wasm-blocks-to-cf.mlir
+++ b/mlir/test/Conversion/RaiseWasm/wasm-blocks-to-cf.mlir
@@ -60,7 +60,7 @@ wasmssa.func @i_am_a_block(%arg0 : !wasmssa<local ref to i32>) -> i32 {
// CHECK: ^bb6:
// CHECK: %[[VAL_6:.*]] = arith.constant 7 : i32
// CHECK: return
-wasmssa.func nested @func_0(%arg0: !wasmssa<local ref to i32>) {
+wasmssa.func @func_0(%arg0: !wasmssa<local ref to i32>) {
%1 = wasmssa.const 1: i32
wasmssa.block : {
%2 = wasmssa.const 2: i32
@@ -104,7 +104,7 @@ wasmssa.func nested @func_0(%arg0: !wasmssa<local ref to i32>) {
// CHECK: ^bb6:
// CHECK: %[[VAL_6:.*]] = arith.constant 7 : i32
// CHECK: return
-wasmssa.func nested @func_1() {
+wasmssa.func @func_1() {
%1 = wasmssa.const 1: i32
wasmssa.block : {
%2 = wasmssa.const 2: i32
@@ -136,7 +136,7 @@ wasmssa.func nested @func_1() {
// CHECK: cf.br ^bb2(%[[VAL_3]] : i32)
// CHECK: ^bb2(%[[VAL_4:.*]]: i32):
// CHECK: return %[[VAL_4]] : i32
-wasmssa.func nested @func_2() -> i32 {
+wasmssa.func @func_2() -> i32 {
%0 = wasmssa.const 14 : i32
wasmssa.block(%0) : i32 : {
^bb0(%arg0: i32):
@@ -155,7 +155,7 @@ wasmssa.func nested @func_2() -> i32 {
// CHECK: cf.br ^bb2(%[[VAL_0]] : i32)
// CHECK: ^bb2(%[[VAL_1:.*]]: i32):
// CHECK: return %[[VAL_1]] : i32
-wasmssa.func nested @func_3() -> i32 {
+wasmssa.func @func_3() -> i32 {
wasmssa.block : {
%1 = wasmssa.const 17 : i32
wasmssa.block_return %1 : i32
@@ -185,7 +185,7 @@ wasmssa.func nested @func_3() -> i32 {
// CHECK_CANONICALIZED: %[[VAL_0:.*]] = arith.constant 1 : i32
// CHECK_CANONICALIZED: return %[[VAL_0]] : i32
-wasmssa.func nested @branch_if_taken() -> i32 {
+wasmssa.func @branch_if_taken() -> i32 {
wasmssa.block : {
%1 = wasmssa.const 1 : i32
%2 = wasmssa.const 2 : i32
@@ -218,7 +218,7 @@ wasmssa.func nested @branch_if_taken() -> i32 {
// CHECK_CANONICALIZED: %[[VAL_0:.*]] = arith.constant 17 : i32
// CHECK_CANONICALIZED: return %[[VAL_0]] : i32
// CHECK_CANONICALIZED: }
-wasmssa.func nested @branch_if_continue() -> i32 {
+wasmssa.func @branch_if_continue() -> i32 {
wasmssa.block : {
%1 = wasmssa.const 1 : i32
%2 = wasmssa.const 0 : i32
@@ -256,7 +256,7 @@ wasmssa.func nested @branch_if_continue() -> i32 {
// CHECK: cf.br ^bb3(%[[VAL_13]] : i32)
// CHECK: ^bb3(%[[VAL_14:.*]]: i32):
// CHECK: return %[[VAL_14]] : i32
-wasmssa.func nested @if(%arg0: !wasmssa<local ref to i32>) -> i32 {
+wasmssa.func @if(%arg0: !wasmssa<local ref to i32>) -> i32 {
%1 = wasmssa.local_get %arg0 : ref to i32
%2 = wasmssa.const 1 : i32
%3 = wasmssa.and %1 %2 : i32
@@ -294,7 +294,7 @@ wasmssa.func nested @if(%arg0: !wasmssa<local ref to i32>) -> i32 {
// CHECK: cf.br ^bb2(%[[VAL_9]] : i32)
// CHECK: ^bb2(%[[VAL_10:.*]]: i32):
// CHECK: return %[[VAL_10]] : i32
-wasmssa.func nested @if_else(%arg0: !wasmssa<local ref to i32>) -> i32 {
+wasmssa.func @if_else(%arg0: !wasmssa<local ref to i32>) -> i32 {
%1 = wasmssa.local_get %arg0 : ref to i32
%2 = wasmssa.local_get %arg0 : ref to i32
%3 = wasmssa.const 1 : i32
@@ -339,7 +339,7 @@ wasmssa.func nested @if_else(%arg0: !wasmssa<local ref to i32>) -> i32 {
// CHECK: cf.br ^bb5(%[[VAL_16]] : i32)
// CHECK: ^bb5(%[[VAL_17:.*]]: i32):
// CHECK: return %[[VAL_17]] : i32
-wasmssa.func nested @if_if(%arg0: !wasmssa<local ref to i32>) -> i32 {
+wasmssa.func @if_if(%arg0: !wasmssa<local ref to i32>) -> i32 {
%1 = wasmssa.local_get %arg0 : ref to i32
%2 = wasmssa.ctz %1 : i32
"wasmssa.if"(%2)[^bb1] ({
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-comparisons-to-arith-cmp.mlir b/mlir/test/Conversion/RaiseWasm/wasm-comparisons-to-arith-cmp.mlir
index bc952b47520ca..6643b0389f405 100644
--- a/mlir/test/Conversion/RaiseWasm/wasm-comparisons-to-arith-cmp.mlir
+++ b/mlir/test/Conversion/RaiseWasm/wasm-comparisons-to-arith-cmp.mlir
@@ -2,194 +2,194 @@
module {
- wasmssa.func nested @func_lt_si32() -> i32 {
+ wasmssa.func @func_lt_si32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.lt_si %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_le_si32() -> i32 {
+ wasmssa.func @func_le_si32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.le_si %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_lt_ui32() -> i32 {
+ wasmssa.func @func_lt_ui32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.lt_ui %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_le_ui32() -> i32 {
+ wasmssa.func @func_le_ui32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.le_ui %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_gt_si32() -> i32 {
+ wasmssa.func @func_gt_si32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.gt_si %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_gt_ui32() -> i32 {
+ wasmssa.func @func_gt_ui32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.gt_ui %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ge_si32() -> i32 {
+ wasmssa.func @func_ge_si32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.ge_si %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ge_ui32() -> i32 {
+ wasmssa.func @func_ge_ui32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.ge_ui %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_lt_si64() -> i32 {
+ wasmssa.func @func_lt_si64() -> i32 {
%0 = wasmssa.const 12 : i64
%1 = wasmssa.const 50 : i64
%2 = wasmssa.lt_si %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_le_si64() -> i32 {
+ wasmssa.func @func_le_si64() -> i32 {
%0 = wasmssa.const 12 : i64
%1 = wasmssa.const 50 : i64
%2 = wasmssa.le_si %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_lt_ui_i64() -> i32 {
+ wasmssa.func @func_lt_ui_i64() -> i32 {
%0 = wasmssa.const 12 : i64
%1 = wasmssa.const 50 : i64
%2 = wasmssa.lt_ui %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_le_ui_i64() -> i32 {
+ wasmssa.func @func_le_ui_i64() -> i32 {
%0 = wasmssa.const 12 : i64
%1 = wasmssa.const 50 : i64
%2 = wasmssa.le_ui %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_gt_si_i64() -> i32 {
+ wasmssa.func @func_gt_si_i64() -> i32 {
%0 = wasmssa.const 12 : i64
%1 = wasmssa.const 50 : i64
%2 = wasmssa.gt_si %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_gt_ui_i64() -> i32 {
+ wasmssa.func @func_gt_ui_i64() -> i32 {
%0 = wasmssa.const 12 : i64
%1 = wasmssa.const 50 : i64
%2 = wasmssa.gt_ui %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ge_si_i64() -> i32 {
+ wasmssa.func @func_ge_si_i64() -> i32 {
%0 = wasmssa.const 12 : i64
%1 = wasmssa.const 50 : i64
%2 = wasmssa.ge_si %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ge_ui_i64() -> i32 {
+ wasmssa.func @func_ge_ui_i64() -> i32 {
%0 = wasmssa.const 12 : i64
%1 = wasmssa.const 50 : i64
%2 = wasmssa.ge_ui %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_lt_f32() -> i32 {
+ wasmssa.func @func_lt_f32() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f32
%1 = wasmssa.const 1.400000e+01 : f32
%2 = wasmssa.lt %0 %1 : f32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_le_f32() -> i32 {
+ wasmssa.func @func_le_f32() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f32
%1 = wasmssa.const 1.400000e+01 : f32
%2 = wasmssa.le %0 %1 : f32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_gt_f32() -> i32 {
+ wasmssa.func @func_gt_f32() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f32
%1 = wasmssa.const 1.400000e+01 : f32
%2 = wasmssa.gt %0 %1 : f32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ge_f32() -> i32 {
+ wasmssa.func @func_ge_f32() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f32
%1 = wasmssa.const 1.400000e+01 : f32
%2 = wasmssa.ge %0 %1 : f32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_lt_f64() -> i32 {
+ wasmssa.func @func_lt_f64() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f64
%1 = wasmssa.const 1.400000e+01 : f64
%2 = wasmssa.lt %0 %1 : f64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_le_f64() -> i32 {
+ wasmssa.func @func_le_f64() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f64
%1 = wasmssa.const 1.400000e+01 : f64
%2 = wasmssa.le %0 %1 : f64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_gt_f64() -> i32 {
+ wasmssa.func @func_gt_f64() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f64
%1 = wasmssa.const 1.400000e+01 : f64
%2 = wasmssa.gt %0 %1 : f64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ge_f64() -> i32 {
+ wasmssa.func @func_ge_f64() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f64
%1 = wasmssa.const 1.400000e+01 : f64
%2 = wasmssa.ge %0 %1 : f64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_eq_i32() -> i32 {
+ wasmssa.func @func_eq_i32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.eq %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_eq_i64() -> i32 {
+ wasmssa.func @func_eq_i64() -> i32 {
%0 = wasmssa.const 20 : i64
%1 = wasmssa.const 5 : i64
%2 = wasmssa.eq %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_eq_f32() -> i32 {
+ wasmssa.func @func_eq_f32() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f32
%1 = wasmssa.const 1.400000e+01 : f32
%2 = wasmssa.eq %0 %1 : f32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_eq_f64() -> i32 {
+ wasmssa.func @func_eq_f64() -> i32 {
%0 = wasmssa.const 1.700000e+01 : f64
%1 = wasmssa.const 0.000000e+00 : f64
%2 = wasmssa.eq %0 %1 : f64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ne_i32() -> i32 {
+ wasmssa.func @func_ne_i32() -> i32 {
%0 = wasmssa.const 12 : i32
%1 = wasmssa.const 50 : i32
%2 = wasmssa.ne %0 %1 : i32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ne_i64() -> i32 {
+ wasmssa.func @func_ne_i64() -> i32 {
%0 = wasmssa.const 20 : i64
%1 = wasmssa.const 5 : i64
%2 = wasmssa.ne %0 %1 : i64 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ne_f32() -> i32 {
+ wasmssa.func @func_ne_f32() -> i32 {
%0 = wasmssa.const 5.000000e+00 : f32
%1 = wasmssa.const 1.400000e+01 : f32
%2 = wasmssa.ne %0 %1 : f32 -> i32
wasmssa.return %2 : i32
}
- wasmssa.func nested @func_ne_f64() -> i32 {
+ wasmssa.func @func_ne_f64() -> i32 {
%0 = wasmssa.const 1.700000e+01 : f64
%1 = wasmssa.const 0.000000e+00 : f64
%2 = wasmssa.ne %0 %1 : f64 -> i32
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-extend-to-arith-ext.mlir b/mlir/test/Conversion/RaiseWasm/wasm-extend-to-arith-ext.mlir
index 90c92001a78d6..2b8bb651bbe5b 100644
--- a/mlir/test/Conversion/RaiseWasm/wasm-extend-to-arith-ext.mlir
+++ b/mlir/test/Conversion/RaiseWasm/wasm-extend-to-arith-ext.mlir
@@ -4,7 +4,7 @@
// CHECK: %[[VAL_0:.*]] = arith.constant 10 : i32
// CHECK: %[[VAL_1:.*]] = arith.extsi %[[VAL_0]] : i32 to i64
// CHECK: return %[[VAL_1]] : i64
-wasmssa.func nested @func_0() -> i64 {
+wasmssa.func @func_0() -> i64 {
%0 = wasmssa.const 10 : i32
%1 = wasmssa.extend_i32_s %0 to i64
wasmssa.return %1 : i64
@@ -14,7 +14,7 @@ wasmssa.func nested @func_0() -> i64 {
// CHECK: %[[VAL_0:.*]] = arith.constant 10 : i32
// CHECK: %[[VAL_1:.*]] = arith.extui %[[VAL_0]] : i32 to i64
// CHECK: return %[[VAL_1]] : i64
-wasmssa.func nested @func_1() -> i64 {
+wasmssa.func @func_1() -> i64 {
%0 = wasmssa.const 10 : i32
%1 = wasmssa.extend_i32_u %0 to i64
wasmssa.return %1 : i64
@@ -25,7 +25,7 @@ wasmssa.func nested @func_1() -> i64 {
// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i32 to i8
// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i8 to i32
// CHECK: return %[[VAL_2]] : i32
-wasmssa.func nested @func_2() -> i32 {
+wasmssa.func @func_2() -> i32 {
%0 = wasmssa.const 10 : i32
%1 = wasmssa.extend 8 low bits from %0: i32
wasmssa.return %1 : i32
@@ -36,7 +36,7 @@ wasmssa.func nested @func_2() -> i32 {
// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i32 to i16
// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i16 to i32
// CHECK: return %[[VAL_2]] : i32
-wasmssa.func nested @func_3() -> i32 {
+wasmssa.func @func_3() -> i32 {
%0 = wasmssa.const 10 : i32
%1 = wasmssa.extend 16 low bits from %0: i32
wasmssa.return %1 : i32
@@ -47,7 +47,7 @@ wasmssa.func nested @func_3() -> i32 {
// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i64 to i8
// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i8 to i64
// CHECK: return %[[VAL_2]] : i64
-wasmssa.func nested @func_4() -> i64 {
+wasmssa.func @func_4() -> i64 {
%0 = wasmssa.const 10 : i64
%1 = wasmssa.extend 8 low bits from %0: i64
wasmssa.return %1 : i64
@@ -58,7 +58,7 @@ wasmssa.func nested @func_4() -> i64 {
// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i64 to i16
// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i16 to i64
// CHECK: return %[[VAL_2]] : i64
-wasmssa.func nested @func_5() -> i64 {
+wasmssa.func @func_5() -> i64 {
%0 = wasmssa.const 10 : i64
%1 = wasmssa.extend 16 low bits from %0: i64
wasmssa.return %1 : i64
@@ -69,7 +69,7 @@ wasmssa.func nested @func_5() -> i64 {
// CHECK: %[[VAL_1:.*]] = arith.trunci %[[VAL_0]] : i64 to i32
// CHECK: %[[VAL_2:.*]] = arith.extsi %[[VAL_1]] : i32 to i64
// CHECK: return %[[VAL_2]] : i64
-wasmssa.func nested @func_6() -> i64 {
+wasmssa.func @func_6() -> i64 {
%0 = wasmssa.const 10 : i64
%1 = wasmssa.extend 32 low bits from %0: i64
wasmssa.return %1 : i64
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir b/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir
index e49909cc9417e..6ab812008a2f8 100644
--- a/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir
+++ b/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir
@@ -2,7 +2,7 @@
// RUN: mlir-opt --split-input-file %s --raise-wasm-mlir --canonicalize -o - | FileCheck --check-prefix=CHECK-CANONICAL %s
module {
- wasmssa.func nested @func_0() {
+ wasmssa.func @func_0() {
wasmssa.loop : {
wasmssa.block_return
}> ^bb1
@@ -27,7 +27,7 @@ module {
// -----
module {
- wasmssa.func nested @func_0() -> i32 {
+ wasmssa.func @func_0() -> i32 {
%0 = wasmssa.local of type i32
wasmssa.loop : {
%1 = wasmssa.local_get %0 : ref to i32
@@ -67,7 +67,7 @@ module {
// -----
module {
- wasmssa.func nested @func_0() {
+ wasmssa.func @func_0() {
%0 = wasmssa.local of type i32
wasmssa.loop : {
%1 = wasmssa.local_get %0 : ref to i32
@@ -117,7 +117,7 @@ module {
// -----
module {
- wasmssa.func nested @func_0() {
+ wasmssa.func @func_0() {
%0 = wasmssa.local of type i32
%1 = wasmssa.local of type i32
wasmssa.loop : {
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-rotl-to-arith.mlir b/mlir/test/Conversion/RaiseWasm/wasm-rotl-to-arith.mlir
index 91e929bd5ff35..b69f88afd4d7c 100644
--- a/mlir/test/Conversion/RaiseWasm/wasm-rotl-to-arith.mlir
+++ b/mlir/test/Conversion/RaiseWasm/wasm-rotl-to-arith.mlir
@@ -31,7 +31,7 @@
// CHECK: %[[RES:.*]] = arith.ori %[[SHRU]], %[[SHL]] : i32
// CHECK: return %[[RES]] : i32
-wasmssa.func nested @rotl_i32(%arg0: !wasmssa<local ref to i32>, %arg1: !wasmssa<local ref to i32>) -> i32 {
+wasmssa.func @rotl_i32(%arg0: !wasmssa<local ref to i32>, %arg1: !wasmssa<local ref to i32>) -> i32 {
%v0 = wasmssa.local_get %arg0 : ref to i32
%v1 = wasmssa.local_get %arg1 : ref to i32
@@ -66,7 +66,7 @@ wasmssa.func nested @rotl_i32(%arg0: !wasmssa<local ref to i32>, %arg1: !wasmssa
// Form final result.
// CHECK: %[[RES:.*]] = arith.ori %[[SHRU]], %[[SHL]] : i64
// CHECK: return %[[RES]] : i64
-wasmssa.func nested @rotl_i64(%arg0: !wasmssa<local ref to i64>, %arg1: !wasmssa<local ref to i64>) -> i64 {
+wasmssa.func @rotl_i64(%arg0: !wasmssa<local ref to i64>, %arg1: !wasmssa<local ref to i64>) -> i64 {
%v0 = wasmssa.local_get %arg0 : ref to i64
%v1 = wasmssa.local_get %arg1 : ref to i64
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-rotr-to-arith.mlir b/mlir/test/Conversion/RaiseWasm/wasm-rotr-to-arith.mlir
index 77e301c3221e3..f0b77271c86dd 100644
--- a/mlir/test/Conversion/RaiseWasm/wasm-rotr-to-arith.mlir
+++ b/mlir/test/Conversion/RaiseWasm/wasm-rotr-to-arith.mlir
@@ -32,7 +32,7 @@
// CHECK: %[[RES:.*]] = arith.ori %[[SHRU]], %[[SHL]] : i32
// CHECK: return %[[RES]] : i32
// CHECK: }
-wasmssa.func nested @rotr_i32(%arg0: !wasmssa<local ref to i32>, %arg1: !wasmssa<local ref to i32>) -> i32 {
+wasmssa.func @rotr_i32(%arg0: !wasmssa<local ref to i32>, %arg1: !wasmssa<local ref to i32>) -> i32 {
%v0 = wasmssa.local_get %arg0 : ref to i32
%v1 = wasmssa.local_get %arg1 : ref to i32
@@ -68,7 +68,7 @@ wasmssa.func nested @rotr_i32(%arg0: !wasmssa<local ref to i32>, %arg1: !wasmssa
// CHECK: %[[RES:.*]] = arith.ori %[[SHRU]], %[[SHL]] : i64
// CHECK: return %[[RES]] : i64
// CHECK: }
-wasmssa.func nested @rotr_i64(%arg0: !wasmssa<local ref to i64>, %arg1: !wasmssa<local ref to i64>) -> i64 {
+wasmssa.func @rotr_i64(%arg0: !wasmssa<local ref to i64>, %arg1: !wasmssa<local ref to i64>) -> i64 {
%v0 = wasmssa.local_get %arg0 : ref to i64
%v1 = wasmssa.local_get %arg1 : ref to i64
>From a249e73f27672c4d5dfb8552cd0057da55e9e3c0 Mon Sep 17 00:00:00 2001
From: Ferdinand Lemaire <ferdinand.lemaire at woven-planet.global>
Date: Fri, 26 Jun 2026 16:03:07 +0900
Subject: [PATCH 6/7] [mlir][wasm] Fix format
---
.../Conversion/RaiseWasm/RaiseWasmMLIR.cpp | 88 +++++++++++--------
1 file changed, 52 insertions(+), 36 deletions(-)
diff --git a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
index 0e6e0c05b79b6..b4b5006352c11 100644
--- a/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
+++ b/mlir/lib/Conversion/RaiseWasm/RaiseWasmMLIR.cpp
@@ -165,18 +165,21 @@ struct RotateOpConversion : OpConversionPattern<SourceOp> {
// Form the left-hand side of the OR:
// (val (lhs shift op) (bits & (width - 1)))
auto orLHS = LHSShiftOp::create(
- rewriter, loc, val, AndOp::create(rewriter, loc, bits, cstWidthMinusOne));
+ rewriter, loc, val,
+ AndOp::create(rewriter, loc, bits, cstWidthMinusOne));
// Form the right-hand side of the OR:
// (val (rhs shift op) (-bits & (width - 1)))
auto orRHS = RHSShiftOp::create(
rewriter, loc, val,
// (-bits & (width - 1))
- AndOp::create(
- rewriter, loc,
- // 0 - bits == -bits
- SubOp::create(rewriter, loc, ConstOp::create(rewriter, loc, IntegerAttr::get(ty, 0)), bits),
- cstWidthMinusOne));
+ AndOp::create(rewriter, loc,
+ // 0 - bits == -bits
+ SubOp::create(rewriter, loc,
+ ConstOp::create(rewriter, loc,
+ IntegerAttr::get(ty, 0)),
+ bits),
+ cstWidthMinusOne));
// OR together the two shifts and replace the rotate with the new
// expression.
@@ -198,8 +201,8 @@ struct ComparisonOpConversion : OpConversionPattern<SourceOp> {
ConversionPatternRewriter &rewriter) const override {
auto cmpRes =
TargetOp::create(rewriter, srcOp.getLoc(), rewriter.getI1Type(),
- AttrType::get(rewriter.getContext(), flag),
- adaptor.getLhs(), adaptor.getRhs())
+ AttrType::get(rewriter.getContext(), flag),
+ adaptor.getLhs(), adaptor.getRhs())
.getResult();
rewriter.replaceOpWithNewOp<arith::ExtUIOp>(srcOp, rewriter.getI32Type(),
cmpRes);
@@ -254,15 +257,17 @@ struct IntFpComparisonOpConversion : OpConversionPattern<SourceOp> {
Value comparisonResult;
if (srcOp.getLhs().getType().isInteger())
comparisonResult =
- arith::CmpIOp::create(rewriter, srcOp.getLoc(), rewriter.getI1Type(),
- arith::CmpIPredicateAttr::get(rewriter.getContext(), IntFlag),
- adaptor.getLhs(), adaptor.getRhs())
+ arith::CmpIOp::create(
+ rewriter, srcOp.getLoc(), rewriter.getI1Type(),
+ arith::CmpIPredicateAttr::get(rewriter.getContext(), IntFlag),
+ adaptor.getLhs(), adaptor.getRhs())
.getResult();
else if (srcOp.getLhs().getType().isFloat())
comparisonResult =
- arith::CmpFOp::create(rewriter, srcOp.getLoc(), rewriter.getI1Type(),
- arith::CmpFPredicateAttr::get(rewriter.getContext(), FloatFlag),
- adaptor.getLhs(), adaptor.getRhs())
+ arith::CmpFOp::create(
+ rewriter, srcOp.getLoc(), rewriter.getI1Type(),
+ arith::CmpFPredicateAttr::get(rewriter.getContext(), FloatFlag),
+ adaptor.getLhs(), adaptor.getRhs())
.getResult();
else
return rewriter.notifyMatchFailure(
@@ -312,13 +317,15 @@ struct WasmEqzOpConversion : OpConversionPattern<EqzOp> {
matchAndRewrite(EqzOp eqzOp, EqzOp::Adaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
auto loc = eqzOp->getLoc();
- auto zero =
- arith::ConstantOp::create(rewriter, loc, rewriter.getIntegerAttr(adaptor.getInput().getType(), 0))
- .getResult();
- auto cmpRes = arith::CmpIOp::create(rewriter, loc, rewriter.getI1Type(),
- arith::CmpIPredicateAttr::get(
- rewriter.getContext(), arith::CmpIPredicate::eq),
- adaptor.getInput(), zero)
+ auto zero = arith::ConstantOp::create(
+ rewriter, loc,
+ rewriter.getIntegerAttr(adaptor.getInput().getType(), 0))
+ .getResult();
+ auto cmpRes = arith::CmpIOp::create(
+ rewriter, loc, rewriter.getI1Type(),
+ arith::CmpIPredicateAttr::get(rewriter.getContext(),
+ arith::CmpIPredicate::eq),
+ adaptor.getInput(), zero)
.getResult();
rewriter.replaceOpWithNewOp<arith::ExtUIOp>(eqzOp, rewriter.getI32Type(),
cmpRes);
@@ -335,7 +342,9 @@ struct WasmExtendLowBitsOpConversion : OpConversionPattern<ExtendLowBitsSOp> {
ExtendLowBitsSOp::Adaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
auto truncWidth = extendLowBytesSOp.getBitsToTake().getInt();
- auto truncation = arith::TruncIOp::create(rewriter, extendLowBytesSOp->getLoc(), rewriter.getIntegerType(truncWidth), adaptor.getInput());
+ auto truncation = arith::TruncIOp::create(
+ rewriter, extendLowBytesSOp->getLoc(),
+ rewriter.getIntegerType(truncWidth), adaptor.getInput());
rewriter.replaceOpWithNewOp<arith::ExtSIOp>(
extendLowBytesSOp, extendLowBytesSOp.getResult().getType(),
truncation.getResult());
@@ -366,12 +375,14 @@ struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
/// flow related ops contained in a function.
class CFRewriterVisitor {
private:
- using branch_to_dest_t =
- llvm::DenseMap<LabelBranchingOpInterface, Block *>;
+ using branch_to_dest_t = llvm::DenseMap<LabelBranchingOpInterface, Block *>;
Value getCompResultAsI1(Value compResult,
ConversionPatternRewriter &rewriter) {
- auto testValue = arith::ConstantOp::create(rewriter, compResult.getLoc(), rewriter.getI32IntegerAttr(0));
- auto flag = arith::CmpIOp::create(rewriter, compResult.getLoc(), rewriter.getIntegerType(1), arith::CmpIPredicate::ne, compResult, testValue)
+ auto testValue = arith::ConstantOp::create(rewriter, compResult.getLoc(),
+ rewriter.getI32IntegerAttr(0));
+ auto flag = arith::CmpIOp::create(
+ rewriter, compResult.getLoc(), rewriter.getIntegerType(1),
+ arith::CmpIPredicate::ne, compResult, testValue)
.getResult();
return flag;
}
@@ -671,29 +682,34 @@ struct WasmMemoryOpConversion : OpConversionPattern<MemOp> {
MemRefType::get({ShapedType::kDynamic}, rewriter.getI8Type());
auto bufferPtrType = MemRefType::get({1}, bufferType);
auto memVisibility = memOp.getVisibility();
- // Convert to StringAttr since memref::GlobalOp expects visibility as a string attribute.
+ // Convert to StringAttr since memref::GlobalOp expects visibility as a
+ // string attribute.
mlir::StringAttr visAttr;
if (memVisibility == mlir::SymbolTable::Visibility::Public)
- visAttr = mlir::StringAttr::get(memOp->getContext(), "public");
+ visAttr = mlir::StringAttr::get(memOp->getContext(), "public");
else if (memVisibility == mlir::SymbolTable::Visibility::Private)
- visAttr = mlir::StringAttr::get(memOp->getContext(), "private");
+ visAttr = mlir::StringAttr::get(memOp->getContext(), "private");
else
- visAttr = mlir::StringAttr::get(memOp->getContext(), "nested");
+ visAttr = mlir::StringAttr::get(memOp->getContext(), "nested");
auto memPtr = rewriter.replaceOpWithNewOp<memref::GlobalOp>(
- memOp, memOp.getSymNameAttr(), visAttr,
- TypeAttr::get(bufferPtrType), /*initialValue*/ rewriter.getUnitAttr(),
+ memOp, memOp.getSymNameAttr(), visAttr, TypeAttr::get(bufferPtrType),
+ /*initialValue*/ rewriter.getUnitAttr(),
/*constant*/ UnitAttr{}, /*alignment*/ IntegerAttr{});
auto initializerName = (memPtr.getSymName() + "::initializer").str();
- auto memInitializer = func::FuncOp::create(rewriter, loc, initializerName,
- FunctionType::get(getContext(), {}, {}));
+ auto memInitializer =
+ func::FuncOp::create(rewriter, loc, initializerName,
+ FunctionType::get(getContext(), {}, {}));
memInitializer->setAttr(rewriter.getStringAttr("initializer"),
rewriter.getUnitAttr());
auto *initializerBody = memInitializer.addEntryBlock();
auto sip = rewriter.saveInsertionPoint();
rewriter.setInsertionPointToStart(initializerBody);
- auto memRefPtr = memref::GetGlobalOp::create(rewriter, loc, MemRefType::get({1}, bufferType), memPtr.getSymName());
- auto alloc = memref::AllocOp::create(rewriter, loc, MemRefType::get({memOp.getLimits().getMin()}, rewriter.getI8Type()));
+ auto memRefPtr = memref::GetGlobalOp::create(
+ rewriter, loc, MemRefType::get({1}, bufferType), memPtr.getSymName());
+ auto alloc = memref::AllocOp::create(
+ rewriter, loc,
+ MemRefType::get({memOp.getLimits().getMin()}, rewriter.getI8Type()));
auto castOp =
memref::CastOp::create(rewriter, loc, bufferType, alloc.getResult());
auto idx = arith::ConstantIndexOp::create(rewriter, loc, 0);
>From 528e3fc46245cf2e62904b6d60508b8aa301b5ca Mon Sep 17 00:00:00 2001
From: Ferdinand Lemaire <ferdinand.lemaire at woven-planet.global>
Date: Fri, 26 Jun 2026 21:26:54 +0900
Subject: [PATCH 7/7] [mlir][wasm] Update tests references
---
.../Conversion/RaiseWasm/wasm-loop-to-cf.mlir | 29 ++++++++-----------
1 file changed, 12 insertions(+), 17 deletions(-)
diff --git a/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir b/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir
index 6ab812008a2f8..cdaf249d9ccf6 100644
--- a/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir
+++ b/mlir/test/Conversion/RaiseWasm/wasm-loop-to-cf.mlir
@@ -183,24 +183,19 @@ module {
// CHECK: }
// CHECK-CANONICAL-LABEL: func.func @func_0() {
-// CHECK-CANONICAL: %[[VAL_0:.*]] = arith.constant 10 : i32
-// CHECK-CANONICAL: %[[VAL_1:.*]] = arith.constant 12 : i32
-// CHECK-CANONICAL: %[[VAL_2:.*]] = arith.constant 1 : i32
-// CHECK-CANONICAL: %[[VAL_3:.*]] = arith.constant 0 : i32
-// CHECK-CANONICAL: %[[VAL_4:.*]] = memref.alloca() : memref<i32>
-// CHECK-CANONICAL: memref.store %[[VAL_3]], %[[VAL_4]][] : memref<i32>
+// CHECK-CANONICAL: %[[CONSTANT_0:.*]] = arith.constant 12 : i32
+// CHECK-CANONICAL: %[[CONSTANT_1:.*]] = arith.constant 1 : i32
+// CHECK-CANONICAL: %[[CONSTANT_2:.*]] = arith.constant 0 : i32
+// CHECK-CANONICAL: %[[ALLOCA_0:.*]] = memref.alloca() : memref<i32>
+// CHECK-CANONICAL: memref.store %[[CONSTANT_2]], %[[ALLOCA_0]][] : memref<i32>
+// CHECK-CANONICAL: %[[LOAD_0:.*]] = memref.load %[[ALLOCA_0]][] : memref<i32>
+// CHECK-CANONICAL: %[[ADDI_0:.*]] = arith.addi %[[LOAD_0]], %[[CONSTANT_1]] : i32
+// CHECK-CANONICAL: memref.store %[[ADDI_0]], %[[ALLOCA_0]][] : memref<i32>
// CHECK-CANONICAL: cf.br ^bb1
// CHECK-CANONICAL: ^bb1:
-// CHECK-CANONICAL: %[[VAL_5:.*]] = memref.load %[[VAL_4]][] : memref<i32>
-// CHECK-CANONICAL: %[[VAL_6:.*]] = arith.addi %[[VAL_5]], %[[VAL_2]] : i32
-// CHECK-CANONICAL: memref.store %[[VAL_6]], %[[VAL_4]][] : memref<i32>
-// CHECK-CANONICAL: cf.br ^bb2
+// CHECK-CANONICAL: %[[LOAD_1:.*]] = memref.load %[[ALLOCA_0]][] : memref<i32>
+// CHECK-CANONICAL: %[[CMPI_0:.*]] = arith.cmpi slt, %[[LOAD_1]], %[[CONSTANT_0]] : i32
+// CHECK-CANONICAL: cf.cond_br %[[CMPI_0]], ^bb1, ^bb2
// CHECK-CANONICAL: ^bb2:
-// CHECK-CANONICAL: %[[VAL_7:.*]] = memref.load %[[VAL_4]][] : memref<i32>
-// CHECK-CANONICAL: %[[VAL_8:.*]] = arith.cmpi slt, %[[VAL_7]], %[[VAL_1]] : i32
-// CHECK-CANONICAL: cf.cond_br %[[VAL_8]], ^bb2, ^bb3(%[[VAL_1]] : i32)
-// CHECK-CANONICAL: ^bb3(%[[VAL_9:.*]]: i32):
-// CHECK-CANONICAL: %[[VAL_10:.*]] = arith.cmpi slt, %[[VAL_9]], %[[VAL_0]] : i32
-// CHECK-CANONICAL: cf.cond_br %[[VAL_10]], ^bb1, ^bb4
-// CHECK-CANONICAL: ^bb4:
// CHECK-CANONICAL: return
+// CHECK-CANONICAL: }
More information about the Mlir-commits
mailing list