[Mlir-commits] [mlir] [mlir][gpu] Store the all-reduce result from the first lane only (PR #221532)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sat Sep 5 23:52:02 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-gpu
Author: Alessandro Potenza (alepot55)
<details>
<summary>Changes</summary>
The final step of the gpu.all_reduce lowering reduces the per-subgroup partial results with createSubgroupReduce, whose contract says that only the first lane holds the result and every other lane holds an undefined value. All the reducing lanes then stored to the same workgroup buffer slot, so with a number of subgroups that is not a power of two (three warps, say) lanes that missed a butterfly step wrote a wrong partial to the same address as the correct one, and the answer depended on which store the hardware let win. This guards the store with isFirstLane, the way the first phase of the lowering already does.
Found with an executable lock-step semantics of the LLVM dialect that reports two lanes of one store writing different values to the same address: on 501 generated kernels the only reports were the 7 whose block had three warps, all at this store. On current NVIDIA hardware the surviving write happens to be the right one, so the bug is latent there, but it is undefined by the lowering's own contract. The two autogenerated tests are updated by hand, as the last upstream change to them did, since generate-test-checks.py now emits a different naming style.
Assisted-by: Claude Code
---
Full diff: https://github.com/llvm/llvm-project/pull/221532.diff
3 Files Affected:
- (modified) mlir/lib/Dialect/GPU/Transforms/AllReduceLowering.cpp (+6-2)
- (modified) mlir/test/Dialect/GPU/all-reduce-add.mlir (+8-4)
- (modified) mlir/test/Dialect/GPU/all-reduce-maxf.mlir (+8-4)
``````````diff
diff --git a/mlir/lib/Dialect/GPU/Transforms/AllReduceLowering.cpp b/mlir/lib/Dialect/GPU/Transforms/AllReduceLowering.cpp
index d76c3194f025e..e1956ea982357 100644
--- a/mlir/lib/Dialect/GPU/Transforms/AllReduceLowering.cpp
+++ b/mlir/lib/Dialect/GPU/Transforms/AllReduceLowering.cpp
@@ -124,14 +124,18 @@ struct GpuAllReduceRewriter {
// Use the first numSubgroups invocations to reduce the intermediate results
// from workgroup memory. The final result is written to workgroup memory
- // again.
+ // again, by the first lane only: the subgroup reduction leaves the result
+ // in the first lane and an undefined value in every other lane, so letting
+ // all of them store would make the outcome depend on which lane's store
+ // wins.
Value zero = create<arith::ConstantIndexOp>(0);
createPredicatedBlock(isValidSubgroup, [&] {
Value index = create<arith::IndexCastOp>(indexType, invocationIdx);
Value value = create<memref::LoadOp>(valueType, buffer, index);
Value result =
createSubgroupReduce(numSubgroups, laneId, value, accumFactory);
- create<memref::StoreOp>(result, buffer, zero);
+ createPredicatedBlock(
+ isFirstLane, [&] { create<memref::StoreOp>(result, buffer, zero); });
});
// Synchronize workgroup and load result from workgroup memory.
diff --git a/mlir/test/Dialect/GPU/all-reduce-add.mlir b/mlir/test/Dialect/GPU/all-reduce-add.mlir
index bca9ea3f50bd5..ec25b52228954 100644
--- a/mlir/test/Dialect/GPU/all-reduce-add.mlir
+++ b/mlir/test/Dialect/GPU/all-reduce-add.mlir
@@ -108,7 +108,7 @@ gpu.module @kernels {
// CHECK: [[VAL_72:%.*]] = arith.addi [[VAL_28]], [[VAL_2]] : i32
// CHECK: [[VAL_73:%.*]] = arith.divsi [[VAL_72]], [[VAL_5]] : i32
// CHECK: [[VAL_74:%.*]] = arith.cmpi slt, [[VAL_27]], [[VAL_73]] : i32
- // CHECK: cf.cond_br [[VAL_74]], ^bb22, ^bb41
+ // CHECK: cf.cond_br [[VAL_74]], ^bb22, ^bb43
// CHECK: ^bb22:
// CHECK: [[VAL_75:%.*]] = arith.index_cast [[VAL_27]] : i32 to index
// CHECK: [[VAL_76:%.*]] = memref.load [[VAL_1]]{{\[}}[[VAL_75]]] : memref<32xf32, #gpu.address_space<workgroup>>
@@ -169,11 +169,15 @@ gpu.module @kernels {
// CHECK: [[VAL_112:%.*]] = arith.addf [[VAL_109]], [[VAL_110]] : f32
// CHECK: cf.br ^bb40([[VAL_112]] : f32)
// CHECK: ^bb40([[VAL_113:%.*]]: f32):
- // CHECK: store [[VAL_113]], [[VAL_1]]{{\[}}[[VAL_4]]] : memref<32xf32, #gpu.address_space<workgroup>>
- // CHECK: cf.br ^bb42
+ // CHECK: cf.cond_br [[VAL_30]], ^bb41, ^bb42
// CHECK: ^bb41:
- // CHECK: cf.br ^bb42
+ // CHECK: store [[VAL_113]], [[VAL_1]]{{\[}}[[VAL_4]]] : memref<32xf32, #gpu.address_space<workgroup>>
+ // CHECK: cf.br ^bb43
// CHECK: ^bb42:
+ // CHECK: cf.br ^bb43
+ // CHECK: ^bb43:
+ // CHECK: cf.br ^bb44
+ // CHECK: ^bb44:
// CHECK: gpu.barrier memfence [#gpu.address_space<workgroup>]
%sum = gpu.all_reduce add %arg0 uniform {} : (f32) -> (f32)
gpu.return
diff --git a/mlir/test/Dialect/GPU/all-reduce-maxf.mlir b/mlir/test/Dialect/GPU/all-reduce-maxf.mlir
index 9c32249b2230f..f368b02d4f339 100644
--- a/mlir/test/Dialect/GPU/all-reduce-maxf.mlir
+++ b/mlir/test/Dialect/GPU/all-reduce-maxf.mlir
@@ -108,7 +108,7 @@ gpu.module @kernels {
// CHECK: [[VAL_82:%.*]] = arith.addi [[VAL_28]], [[VAL_2]] : i32
// CHECK: [[VAL_83:%.*]] = arith.divsi [[VAL_82]], [[VAL_5]] : i32
// CHECK: [[VAL_84:%.*]] = arith.cmpi slt, [[VAL_27]], [[VAL_83]] : i32
- // CHECK: cf.cond_br [[VAL_84]], ^bb22, ^bb41
+ // CHECK: cf.cond_br [[VAL_84]], ^bb22, ^bb43
// CHECK: ^bb22:
// CHECK: [[VAL_85:%.*]] = arith.index_cast [[VAL_27]] : i32 to index
// CHECK: [[VAL_86:%.*]] = memref.load [[VAL_1]]{{\[}}[[VAL_85]]] : memref<32xf32, #gpu.address_space<workgroup>>
@@ -169,11 +169,15 @@ gpu.module @kernels {
// CHECK: [[VAL_132:%.*]] = arith.maxnumf [[VAL_128]], [[VAL_129]] : f32
// CHECK: cf.br ^bb40([[VAL_132]] : f32)
// CHECK: ^bb40([[VAL_133:%.*]]: f32):
- // CHECK: store [[VAL_133]], [[VAL_1]]{{\[}}[[VAL_4]]] : memref<32xf32, #gpu.address_space<workgroup>>
- // CHECK: cf.br ^bb42
+ // CHECK: cf.cond_br [[VAL_30]], ^bb41, ^bb42
// CHECK: ^bb41:
- // CHECK: cf.br ^bb42
+ // CHECK: store [[VAL_133]], [[VAL_1]]{{\[}}[[VAL_4]]] : memref<32xf32, #gpu.address_space<workgroup>>
+ // CHECK: cf.br ^bb43
// CHECK: ^bb42:
+ // CHECK: cf.br ^bb43
+ // CHECK: ^bb43:
+ // CHECK: cf.br ^bb44
+ // CHECK: ^bb44:
// CHECK: gpu.barrier memfence [#gpu.address_space<workgroup>]
%sum = gpu.all_reduce maxnumf %arg0 uniform {} : (f32) -> (f32)
gpu.return
``````````
</details>
https://github.com/llvm/llvm-project/pull/221532
More information about the Mlir-commits
mailing list