MLIR 24.0.0git
SubgroupReduceToDPP.cpp
Go to the documentation of this file.
1//===- SubgroupReduceToDPP.cpp - subgroup_reduce to AMDGPU DPP lowering ---===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Implements lowering of `gpu.subgroup_reduce` ops to AMDGPU DPP operations.
10//
11//===----------------------------------------------------------------------===//
12
14
23#include "llvm/Support/FormatVariadic.h"
24#include <cassert>
25
26using namespace mlir;
27
28namespace {
29static FailureOr<Value>
30createSubgroupDPPReduction(PatternRewriter &rewriter, gpu::SubgroupReduceOp op,
31 Value input, gpu::AllReduceOperation mode,
34 Location loc = op.getLoc();
35 Value dpp;
36 Value res = input;
37 constexpr int allRows = 0xf;
38 constexpr int allBanks = 0xf;
39 const bool boundCtrl = true;
40 if (ci.clusterSize >= 2) {
41 // Perform reduction between all lanes N <-> N+1.
42 dpp = amdgpu::DPPOp::create(
43 rewriter, loc, res.getType(), res, res, amdgpu::DPPPerm::quad_perm,
44 rewriter.getI32ArrayAttr({1, 0, 3, 2}), allRows, allBanks, boundCtrl);
45 res = vector::makeArithReduction(rewriter, loc,
46 gpu::convertReductionKind(mode), res, dpp);
47 }
48
49 if (ci.clusterSize >= 4) {
50 // Perform reduction between all lanes N <-> N+2.
51 dpp = amdgpu::DPPOp::create(
52 rewriter, loc, res.getType(), res, res, amdgpu::DPPPerm::quad_perm,
53 rewriter.getI32ArrayAttr({2, 3, 0, 1}), allRows, allBanks, boundCtrl);
54 res = vector::makeArithReduction(rewriter, loc,
55 gpu::convertReductionKind(mode), res, dpp);
56 }
57 if (ci.clusterSize >= 8) {
58 // Perform reduction between all lanes N <-> 7-N,
59 // e.g lane[0] <-> lane[7], lane[1] <-> lane[6]..., lane[3] <-> lane[4].
60 dpp = amdgpu::DPPOp::create(rewriter, loc, res.getType(), res, res,
61 amdgpu::DPPPerm::row_half_mirror,
62 rewriter.getUnitAttr(), allRows, allBanks,
63 boundCtrl);
64 res = vector::makeArithReduction(rewriter, loc,
65 gpu::convertReductionKind(mode), res, dpp);
66 }
67 if (ci.clusterSize >= 16) {
68 // Perform reduction between all lanes N <-> 15-N,
69 // e.g lane[0] <-> lane[15], lane[1] <-> lane[14]..., lane[7] <-> lane[8].
70 dpp = amdgpu::DPPOp::create(
71 rewriter, loc, res.getType(), res, res, amdgpu::DPPPerm::row_mirror,
72 rewriter.getUnitAttr(), allRows, allBanks, boundCtrl);
73 res = vector::makeArithReduction(rewriter, loc,
74 gpu::convertReductionKind(mode), res, dpp);
75 }
76 if (ci.clusterSize >= 32) {
77 if (!target.has(llvm::AMDGPU::FEAT_GFX10_INSTS)) {
78 // Broadcast last value from each row to next row.
79 // Use row mask to avoid polluting row 0 (and row 2 if wave-64).
80 dpp = amdgpu::DPPOp::create(rewriter, loc, res.getType(), res, res,
81 amdgpu::DPPPerm::row_bcast_15,
82 rewriter.getUnitAttr(), 0xa, allBanks,
83 /*bound_ctrl*/ false);
85 rewriter, loc, gpu::convertReductionKind(mode), res, dpp);
86
87 // For subgroupSize = 64, at this point lanes [16, 32) contain the full
88 // reduction over lanes [0, 32), but lanes [0, 16) do not. Similarly,
89 // lanes [48, 64) contain the full reduction over lanes [32, 64), but
90 // lanes [32, 48) do not.
91 //
92 // If subgroup size is 64 and cluster size is 64, we don't need lanes [0,
93 // 16) and [32, 48) to have the correct cluster-32 reduction values at
94 // this point, because only lane 63's value will ultimately be read in
95 // this full-cluster case.
96 //
97 // If subgroup size is 64 and cluster size is 32, we need to ensure that
98 // lanes [0, 16) and [32, 48) have the correct final cluster-32 reduction
99 // values (subgroup_reduce guarantees that all lanes within each cluster
100 // contain the final reduction value). We do this by broadcasting lane
101 // 31's value to lanes [0, 16) and lanes 63's value to lanes [32, 48).
102 //
103 // See https://gpuopen.com/learn/amd-gcn-assembly-cross-lane-operations
104 // for an illustration of how this within-cluster broadcast works with a
105 // swizzle.
106 if (ci.subgroupSize == 64 && ci.clusterSize == 32) {
107 res =
108 amdgpu::SwizzleBitModeOp::create(rewriter, loc, res, /*and_mask=*/0,
109 /*or_mask=*/31,
110 /*xor_mask=*/0);
111 }
112 } else if (!target.has(llvm::AMDGPU::FEAT_GFX13_INSTS)) {
113 // Use a permute lane to cross rows (row 1 <-> row 0, row 3 <-> row 2).
114 Value uint32Max = arith::ConstantOp::create(
115 rewriter, loc, rewriter.getI32Type(), rewriter.getI32IntegerAttr(-1));
116 dpp = ROCDL::PermlaneX16Op::create(rewriter, loc, res.getType(), res, res,
117 uint32Max, uint32Max,
118 /*fi=*/true,
119 /*boundControl=*/false);
121 rewriter, loc, gpu::convertReductionKind(mode), res, dpp);
122 } else {
123 return rewriter.notifyMatchFailure(
124 op, "Subgroup reduce lowering to DPP not currently supported for "
125 "this device.");
126 }
127 if (ci.subgroupSize == 32) {
128 Value lane31 = arith::ConstantOp::create(
129 rewriter, loc, rewriter.getI32Type(), rewriter.getI32IntegerAttr(31));
130 res =
131 ROCDL::ReadlaneOp::create(rewriter, loc, res.getType(), res, lane31);
132 }
133 }
134 if (ci.clusterSize >= 64) {
135 if (!target.has(llvm::AMDGPU::FEAT_GFX10_INSTS)) {
136 // Broadcast 31st lane value to rows 2 and 3.
137 dpp = amdgpu::DPPOp::create(rewriter, loc, res.getType(), res, res,
138 amdgpu::DPPPerm::row_bcast_31,
139 rewriter.getUnitAttr(), 0xf, allBanks,
140 /*bound_ctrl*/ true);
142 rewriter, loc, gpu::convertReductionKind(mode), dpp, res);
143 // Obtain reduction from last rows, the previous rows are polluted.
144 Value lane63 = arith::ConstantOp::create(
145 rewriter, loc, rewriter.getI32Type(), rewriter.getI32IntegerAttr(63));
146 res =
147 ROCDL::ReadlaneOp::create(rewriter, loc, res.getType(), res, lane63);
148
149 } else if (!target.has(llvm::AMDGPU::FEAT_GFX13_INSTS)) {
150 // Assume reduction across 32 lanes has been done.
151 // Perform final reduction manually by summing values in lane 0 and
152 // lane 32.
153 Value lane31 = arith::ConstantOp::create(
154 rewriter, loc, rewriter.getI32Type(), rewriter.getI32IntegerAttr(31));
155 Value lane63 = arith::ConstantOp::create(
156 rewriter, loc, rewriter.getI32Type(), rewriter.getI32IntegerAttr(63));
157 lane31 =
158 ROCDL::ReadlaneOp::create(rewriter, loc, res.getType(), res, lane31);
159 lane63 =
160 ROCDL::ReadlaneOp::create(rewriter, loc, res.getType(), res, lane63);
162 rewriter, loc, gpu::convertReductionKind(mode), lane31, lane63);
163 } else {
164 return rewriter.notifyMatchFailure(
165 op, "Subgroup reduce lowering to DPP not currently supported for "
166 "this device.");
167 }
168 }
169 assert(res.getType() == input.getType());
170 return res;
171}
172
173/// Collect a set of patterns to lower `gpu.subgroup_reduce` into `amdgpu.dpp`
174/// ops over scalar types. Assumes that the subgroup has
175/// `subgroupSize` lanes. Applicable only to AMD GPUs.
176struct ScalarSubgroupReduceToDPP final
177 : OpRewritePattern<gpu::SubgroupReduceOp> {
178 ScalarSubgroupReduceToDPP(MLIRContext *ctx, unsigned subgroupSize,
179 bool matchClustered,
180 const ROCDL::TargetInfo &target,
181 PatternBenefit benefit)
182 : OpRewritePattern(ctx, benefit), subgroupSize(subgroupSize),
183 matchClustered(matchClustered), target(target) {}
184
185 LogicalResult matchAndRewrite(gpu::SubgroupReduceOp op,
186 PatternRewriter &rewriter) const override {
187 if (op.getClusterSize().has_value() != matchClustered) {
188 return rewriter.notifyMatchFailure(
189 op, llvm::formatv("op is {0}clustered but pattern is configured to "
190 "only match {1}clustered ops",
191 matchClustered ? "non-" : "",
192 matchClustered ? "" : "non-"));
193 }
194 auto ci = gpu::getSubgroupReduceClusterInfo(op, subgroupSize);
195 if (failed(ci))
196 return failure();
197
198 if (ci->clusterStride != 1)
199 return rewriter.notifyMatchFailure(
200 op, "Subgroup reductions using DPP are currently only available for "
201 "clusters of contiguous lanes.");
202
203 Type valueTy = op.getType();
204 if (!valueTy.isIntOrFloat())
205 return rewriter.notifyMatchFailure(
206 op, "Value type is not a compatible scalar.");
207
208 FailureOr<Value> dpp = createSubgroupDPPReduction(
209 rewriter, op, op.getValue(), op.getOp(), *ci, target);
210 if (failed(dpp))
211 return failure();
212
213 rewriter.replaceOp(op, dpp.value());
214 return success();
215 }
216
217private:
218 unsigned subgroupSize = 0;
219 bool matchClustered = false;
220 ROCDL::TargetInfo target;
221};
222} // namespace
223
225 RewritePatternSet &patterns, unsigned subgroupSize,
226 const ROCDL::TargetInfo &target, PatternBenefit benefit) {
227 patterns.add<ScalarSubgroupReduceToDPP>(patterns.getContext(), subgroupSize,
228 /*matchClustered=*/false, target,
229 benefit);
230}
231
233 RewritePatternSet &patterns, unsigned subgroupSize,
234 const ROCDL::TargetInfo &target, PatternBenefit benefit) {
235 patterns.add<ScalarSubgroupReduceToDPP>(patterns.getContext(), subgroupSize,
236 /*matchClustered=*/true, target,
237 benefit);
238}
return success()
UnitAttr getUnitAttr()
Definition Builders.cpp:106
IntegerAttr getI32IntegerAttr(int32_t value)
Definition Builders.cpp:208
ArrayAttr getI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:285
IntegerType getI32Type()
Definition Builders.cpp:71
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
Describes the AMDGPU target a lowering is producing code for: the triple's subarch (which identifies ...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
FailureOr< SubgroupReduceClusterInfo > getSubgroupReduceClusterInfo(SubgroupReduceOp op, unsigned subgroupSize)
Returns the cluster layout of op in a subgroup of subgroupSize lanes, which must be a power of two.
Definition Utils.cpp:48
vector::CombiningKind convertReductionKind(gpu::AllReduceOperation mode)
Returns the matching vector combining kind.
Definition Utils.cpp:19
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
Value makeArithReduction(OpBuilder &b, Location loc, CombiningKind kind, Value v1, Value acc, arith::FastMathFlagsAttr fastmath=nullptr, Value mask=nullptr)
Returns the result value of reducing two scalar/vector values with the corresponding arith operation.
Include the generated interface declarations.
void populateGpuLowerClusteredSubgroupReduceToDPPPatterns(RewritePatternSet &patterns, unsigned subgroupSize, const ROCDL::TargetInfo &target, PatternBenefit benefit=1)
Disjoint counterpart of populateGpuLowerSubgroupReduceToDPPPatterns that only matches gpu....
void populateGpuLowerSubgroupReduceToDPPPatterns(RewritePatternSet &patterns, unsigned subgroupSize, const ROCDL::TargetInfo &target, PatternBenefit benefit=1)
Collect a set of patterns to lower gpu.subgroup_reduce into amdgpu.dpp ops over scalar types.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
Lane layout of the clusters reduced by a gpu.subgroup_reduce op.
Definition GPUUtils.h:34