Skip to content

Commit 7385772

Browse files
authored
[mlir] [liveness] Conservatively mark operands of return-like op inside non-callable and non-regionbranch op as live (llvm#140793)
Currently the liveness analysis always marks operands yielded in regions that aren't classified as `RegionBranchOpInterface` or `CallableOpInterface` as non-live. Examples for these ops include linalg.generic (with `linalg.yield` as terminator) or gpu ops (with `gpu.yield` as terminator). This in turn makes the `remove-dead-values` pass always incorrectly remove the bodies of these ops, leading to invalid IR. Because these ops define their own semantics, I have conservatively marked all operands of these yield ops to be live.
1 parent ada2fbf commit 7385772

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ ChangeResult Liveness::meet(const AbstractSparseLattice &other) {
5151
/// A value is considered "live" iff it:
5252
/// (1) has memory effects OR
5353
/// (2) is returned by a public function OR
54-
/// (3) is used to compute a value of type (1) or (2).
54+
/// (3) is used to compute a value of type (1) or (2) OR
55+
/// (4) is returned by a return-like op whose parent isn't a callable
56+
/// nor a RegionBranchOpInterface (e.g.: linalg.yield, gpu.yield,...)
57+
/// These ops have their own semantics, so we conservatively mark the
58+
/// the yield value as live.
5559
/// It is also to be noted that a value could be of multiple types (1/2/3) at
5660
/// the same time.
5761
///
@@ -73,8 +77,8 @@ ChangeResult Liveness::meet(const AbstractSparseLattice &other) {
7377
LogicalResult
7478
LivenessAnalysis::visitOperation(Operation *op, ArrayRef<Liveness *> operands,
7579
ArrayRef<const Liveness *> results) {
76-
// This marks values of type (1.a) liveness as "live".
77-
if (!isMemoryEffectFree(op)) {
80+
// This marks values of type (1.a) and (4) liveness as "live".
81+
if (!isMemoryEffectFree(op) || op->hasTrait<OpTrait::ReturnLike>()) {
7882
for (auto *operand : operands)
7983
propagateIfChanged(operand, operand->markLive());
8084
}

mlir/test/Transforms/remove-dead-values.mlir

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,45 @@ func.func private @no_block_func_declaration() -> ()
468468

469469
// CHECK: llvm.func @no_block_external_func()
470470
llvm.func @no_block_external_func() attributes {sym_visibility = "private"}
471+
472+
// -----
473+
474+
// Check that yielded values aren't incorrectly removed in gpu regions
475+
gpu.module @test_module_3 {
476+
gpu.func @gpu_all_reduce_region() {
477+
%arg0 = arith.constant 1 : i32
478+
%result = gpu.all_reduce %arg0 uniform {
479+
^bb(%lhs : i32, %rhs : i32):
480+
%xor = arith.xori %lhs, %rhs : i32
481+
"gpu.yield"(%xor) : (i32) -> ()
482+
} : (i32) -> (i32)
483+
gpu.return
484+
}
485+
}
486+
487+
// CHECK-LABEL: func @gpu_all_reduce_region()
488+
// CHECK: %[[yield:.*]] = arith.xori %{{.*}}, %{{.*}} : i32
489+
// CHECK: gpu.yield %[[yield]] : i32
490+
491+
// -----
492+
493+
// Check that yielded values aren't incorrectly removed in linalg regions
494+
module {
495+
func.func @linalg_red_add(%arg0: tensor<?xf32>, %arg1: tensor<1xf32>) -> tensor<1xf32> {
496+
%0 = linalg.generic {
497+
indexing_maps = [affine_map<(d0) -> (d0)>, affine_map<(d0) -> (0)>],
498+
iterator_types = ["reduction"]
499+
} ins(%arg0 : tensor<?xf32>) outs(%arg1 : tensor<1xf32>) {
500+
^bb0(%in: f32, %out: f32):
501+
%1 = arith.addf %in, %out : f32
502+
%2 = arith.subf %1, %out : f32 // this should still be removed
503+
linalg.yield %1 : f32
504+
} -> tensor<1xf32>
505+
return %0 : tensor<1xf32>
506+
}
507+
}
508+
509+
// CHECK-LABEL: func @linalg_red_add
510+
// CHECK: %[[yield:.*]] = arith.addf %{{.*}}, %{{.*}} : f32
511+
// CHECK: linalg.yield %[[yield]] : f32
512+
// CHECK-NOT: arith.subf

0 commit comments

Comments
 (0)