[Mlir-commits] [mlir] [MLIR][SCF] Include IndexSwitchOp in scf.yield aliasing (PR #236644)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sat Oct 31 10:52:47 PDT 2026
https://github.com/zishu0131-png updated https://github.com/llvm/llvm-project/pull/236644
>From 6469d0518363ef4d60d7c9046f26e8d268c4f169 Mon Sep 17 00:00:00 2001
From: zishu0131-png <zishu0131 at gmail.com>
Date: Sun, 1 Nov 2026 01:52:36 +0800
Subject: [PATCH] [MLIR][SCF] Include IndexSwitchOp in scf.yield aliasing
The bufferization interface for scf.yield only recognized scf.if and
scf.execute_region parents, so values yielded from scf.index_switch lost
their alias relation with the switch result. One-shot bufferization could
then write through a non-writable input. The same model's bufferize() already
handles scf::IndexSwitchOp, so this adds it to the aliasing side as well.
Fixes #236567.
Assisted-by: Claude Code
---
.../BufferizableOpInterfaceImpl.cpp | 3 +-
.../one-shot-bufferize-analysis.mlir | 41 +++++++++++++++++++
.../Transforms/one-shot-bufferize.mlir | 24 +++++++++++
3 files changed, 66 insertions(+), 2 deletions(-)
diff --git a/mlir/lib/Dialect/SCF/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/SCF/Transforms/BufferizableOpInterfaceImpl.cpp
index 35357b150e60f5..26f9088e0b87f4 100644
--- a/mlir/lib/Dialect/SCF/Transforms/BufferizableOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/SCF/Transforms/BufferizableOpInterfaceImpl.cpp
@@ -1139,10 +1139,9 @@ struct YieldOpInterface
AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
const AnalysisState &state) const {
- if (auto ifOp = dyn_cast<scf::IfOp>(op->getParentOp())) {
+ if (isa<scf::IfOp, scf::IndexSwitchOp>(op->getParentOp()))
return {{op->getParentOp()->getResult(opOperand.getOperandNumber()),
BufferRelation::Equivalent, /*isDefinite=*/false}};
- }
if (isa<scf::ExecuteRegionOp>(op->getParentOp()))
return {{op->getParentOp()->getResult(opOperand.getOperandNumber()),
BufferRelation::Equivalent}};
diff --git a/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-analysis.mlir b/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-analysis.mlir
index 7d7b1c8eb9bee1..ebbe562d6e3d81 100644
--- a/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-analysis.mlir
+++ b/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-analysis.mlir
@@ -201,3 +201,44 @@ func.func @shorter_inplace_attr(%arg0: tensor<4xi32>) -> (tensor<4xi32>, tensor<
// tensor operands. The analysis must expand the annotation without crashing.
return {__inplace_operands_attr__ = ["false"]} %arg0, %arg0 : tensor<4xi32>, tensor<4xi32>
}
+
+// -----
+
+// A tensor yielded from `scf.index_switch` aliases with the switch's result,
+// just like `scf.if`. Writing to that result must therefore not modify a
+// non-writable input in place.
+// CHECK-LABEL: func @index_switch_non_writable(
+// CHECK-ALIAS-SETS-LABEL: func @index_switch_non_writable(
+func.func @index_switch_non_writable(%t: tensor<4xf32> {bufferization.writable = false},
+ %v: vector<4xf32>, %i: index) -> tensor<4xf32> {
+ %c0 = arith.constant 0 : index
+ // CHECK-ALIAS-SETS: %[[switch:.*]] = scf.index_switch
+ // CHECK-ALIAS-SETS-SAME: __opresult_alias_set_attr__ = {{.*}}%[[switch]]{{.*}}%arg0
+ %0 = scf.index_switch %i -> tensor<4xf32>
+ default {
+ scf.yield %t : tensor<4xf32>
+ }
+ // CHECK: vector.transfer_write {{.*}} {__inplace_operands_attr__ = ["none", "false", "none"]
+ %1 = vector.transfer_write %v, %0[%c0] {in_bounds = [true]} : vector<4xf32>, tensor<4xf32>
+ return %1 : tensor<4xf32>
+}
+
+// -----
+
+// Each `scf.index_switch` result aliases the correspondingly-indexed operand
+// yielded by every case and by the default region.
+// CHECK-ALIAS-SETS-LABEL: func @index_switch_multi_result(
+func.func @index_switch_multi_result(%a: tensor<4xf32> {bufferization.writable = false},
+ %b: tensor<4xf32>, %i: index)
+ -> (tensor<4xf32>, tensor<4xf32>) {
+ // CHECK-ALIAS-SETS: %[[sw:.*]]:2 = scf.index_switch
+ // CHECK-ALIAS-SETS-SAME: __opresult_alias_set_attr__ = {{.*}}"%[[sw]]#0", "%arg0"{{.*}}"%[[sw]]#1", "%arg1"
+ %0, %1 = scf.index_switch %i -> tensor<4xf32>, tensor<4xf32>
+ case 1 {
+ scf.yield %a, %b : tensor<4xf32>, tensor<4xf32>
+ }
+ default {
+ scf.yield %a, %b : tensor<4xf32>, tensor<4xf32>
+ }
+ return %0, %1 : tensor<4xf32>, tensor<4xf32>
+}
diff --git a/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize.mlir b/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize.mlir
index 2e834c4f158c6c..f07c1a29b3af29 100644
--- a/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize.mlir
+++ b/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize.mlir
@@ -284,3 +284,27 @@ func.func @materialize_in_dest_raw(%f: f32, %f2: f32, %idx: index) -> (tensor<5x
return %0, %r : tensor<5xf32>, f32
}
+
+// -----
+
+// Writing through a value yielded by `scf.index_switch` must not modify a
+// non-writable input in place: the write goes to a fresh buffer.
+// CHECK-LABEL: func @index_switch_non_writable
+func.func @index_switch_non_writable(%t: tensor<4xf32> {bufferization.writable = false},
+ %v: vector<4xf32>, %i: index) -> tensor<4xf32> {
+ %c0 = arith.constant 0 : index
+ // CHECK: scf.index_switch
+ %0 = scf.index_switch %i -> tensor<4xf32>
+ case 1 {
+ %e = tensor.empty() : tensor<4xf32>
+ scf.yield %e : tensor<4xf32>
+ }
+ // CHECK: default
+ default {
+ scf.yield %t : tensor<4xf32>
+ }
+ // CHECK: %[[ALLOC:.*]] = memref.alloc
+ // CHECK-NEXT: vector.transfer_write {{.*}}, %[[ALLOC]]
+ %1 = vector.transfer_write %v, %0[%c0] {in_bounds = [true]} : vector<4xf32>, tensor<4xf32>
+ return %1 : tensor<4xf32>
+}
More information about the Mlir-commits
mailing list