MLIR 24.0.0git
InferIntRangeInterfaceImpls.cpp
Go to the documentation of this file.
1//===- InferIntRangeInterfaceImpls.cpp - Integer range impls for gpu -===//
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
10#include "mlir/IR/Matchers.h"
13#include <optional>
14
15using namespace mlir;
16using namespace mlir::gpu;
17
18// Maximum grid and block dimensions of all known GPUs are less than 2^32.
19static constexpr uint64_t kMaxDim = std::numeric_limits<uint32_t>::max();
20// Maximum cluster size.
21static constexpr uint64_t kMaxClusterDim = 16;
22// Maximum subgroups are no larger than 128.
23static constexpr uint64_t kMaxSubgroupSize = 128;
24
25static ConstantIntRanges getIndexRange(uint64_t umin, uint64_t umax) {
26 unsigned width = IndexType::kInternalStorageBitWidth;
27 return ConstantIntRanges::fromUnsigned(APInt(width, umin),
28 APInt(width, umax));
29}
30
31static uint64_t zext(uint32_t arg) { return static_cast<uint64_t>(arg); }
32
33static Value valueByDim(KernelDim3 dims, Dimension dim) {
34 switch (dim) {
35 case Dimension::x:
36 return dims.x;
37 case Dimension::y:
38 return dims.y;
39 case Dimension::z:
40 return dims.z;
41 }
42 llvm_unreachable("All dimension enum cases handled above");
43}
44
45static std::optional<uint32_t>
46getKnownLaunchAttr(GPUFuncOp func, DimensionKind dims, Dimension dim) {
47 DenseI32ArrayAttr bounds;
48 switch (dims) {
49 case DimensionKind::Other:
50 return std::nullopt;
51 case DimensionKind::Block:
52 bounds = func.getKnownBlockSizeAttr();
53 break;
54 case DimensionKind::Grid:
55 bounds = func.getKnownGridSizeAttr();
56 break;
57 case DimensionKind::Cluster:
58 bounds = func.getKnownClusterSizeAttr();
59 break;
60 }
61 if (!bounds)
62 return std::nullopt;
63 if (bounds.size() <= static_cast<uint32_t>(dim))
64 return std::nullopt;
65 return bounds[static_cast<uint32_t>(dim)];
66}
67
68static std::optional<uint32_t> getKnownLaunchAttr(FunctionOpInterface func,
69 StringRef attrName,
70 Dimension dim) {
71 auto bounds =
72 func.getOperation()->getDiscardableAttrOfType<DenseI32ArrayAttr>(
73 attrName);
74 if (!bounds)
75 return std::nullopt;
76 if (bounds.size() <= static_cast<uint32_t>(dim))
77 return std::nullopt;
78 return bounds[static_cast<uint32_t>(dim)];
79}
80
81std::optional<uint32_t>
83 Dimension dim) {
84 if (auto launch = op->getParentOfType<LaunchOp>()) {
85 KernelDim3 bounds;
86 switch (kind) {
87 case DimensionKind::Other:
88 return std::nullopt;
89 case DimensionKind::Block:
90 bounds = launch.getBlockSizeOperandValues();
91 break;
92 case DimensionKind::Grid:
93 bounds = launch.getGridSizeOperandValues();
94 break;
95 case DimensionKind::Cluster:
96 if (launch.hasClusterSize()) {
97 auto clusterBounds = launch.getClusterSizeOperandValues();
98 if (clusterBounds)
99 bounds = *clusterBounds;
100 }
101 break;
102 }
103 Value maybeBound = valueByDim(bounds, dim);
104 APInt value;
105 if (maybeBound && matchPattern(maybeBound, m_ConstantInt(&value)))
106 return value.getZExtValue();
107 }
108
109 if (auto gpuFunc = op->getParentOfType<GPUFuncOp>()) {
110 auto inherentAttr = getKnownLaunchAttr(gpuFunc, kind, dim);
111 if (inherentAttr)
112 return inherentAttr;
113 }
114 if (auto func = op->getParentOfType<FunctionOpInterface>()) {
115 StringRef attrName;
116 switch (kind) {
117 case DimensionKind::Other:
118 return std::nullopt;
119 case DimensionKind::Block:
120 attrName = GPUDialect::KnownBlockSizeAttrHelper::getNameStr();
121 break;
122 case DimensionKind::Grid:
123 attrName = GPUDialect::KnownGridSizeAttrHelper::getNameStr();
124 break;
125 case DimensionKind::Cluster:
126 attrName = GPUDialect::KnownClusterSizeAttrHelper::getNameStr();
127 break;
128 }
129 auto discardableAttr = getKnownLaunchAttr(func, attrName, dim);
130 if (discardableAttr)
131 return discardableAttr;
132 }
133 return std::nullopt;
134}
135
136void ClusterDimOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
137 SetIntRangeFn setResultRange) {
138 uint64_t max = kMaxDim;
139 if (auto specified = getUpperBound())
140 max = specified->getZExtValue();
141 setResultRange(getResult(), getIndexRange(1, max));
142}
143
144void ClusterDimBlocksOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
145 SetIntRangeFn setResultRange) {
146 if (auto known = getKnownDimensionSizeAround(*this, DimensionKind::Cluster,
147 getDimension()))
148 return setResultRange(getResult(),
149 getIndexRange(zext(*known), zext(*known)));
150
151 uint64_t max = kMaxClusterDim;
152 if (auto specified = getUpperBound())
153 max = specified->getZExtValue();
154 setResultRange(getResult(), getIndexRange(1, max));
155}
156
157void ClusterIdOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
158 SetIntRangeFn setResultRange) {
159 uint64_t max = kMaxDim;
160 if (auto specified = getUpperBound())
161 max = specified->getZExtValue();
162 setResultRange(getResult(), getIndexRange(0, max - 1ULL));
163}
164
165void ClusterBlockIdOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
166 SetIntRangeFn setResultRange) {
167 uint64_t max = kMaxClusterDim;
168 if (auto known = getKnownDimensionSizeAround(*this, DimensionKind::Cluster,
169 getDimension()))
170 max = zext(*known);
171 if (auto specified = getUpperBound())
172 max = specified->getZExtValue();
173 setResultRange(getResult(), getIndexRange(0, max - 1ULL));
174}
175
176void BlockDimOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
177 SetIntRangeFn setResultRange) {
178 std::optional<uint32_t> knownVal =
179 getKnownDimensionSizeAround(*this, DimensionKind::Block, getDimension());
180 if (knownVal)
181 return setResultRange(getResult(),
182 getIndexRange(zext(*knownVal), zext(*knownVal)));
183
184 uint64_t max = kMaxDim;
185 if (auto specified = getUpperBound())
186 max = specified->getZExtValue();
187 setResultRange(getResult(), getIndexRange(1, max));
188}
189
190void BlockIdOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
191 SetIntRangeFn setResultRange) {
192 uint64_t max = kMaxDim;
193 if (auto fromContext = getKnownDimensionSizeAround(*this, DimensionKind::Grid,
194 getDimension()))
195 max = zext(*fromContext);
196 if (auto specified = getUpperBound())
197 max = specified->getZExtValue();
198 setResultRange(getResult(), getIndexRange(0, max - 1ULL));
199}
200
201void GridDimOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
202 SetIntRangeFn setResultRange) {
203 std::optional<uint32_t> knownVal =
204 getKnownDimensionSizeAround(*this, DimensionKind::Grid, getDimension());
205 if (knownVal)
206 return setResultRange(getResult(),
207 getIndexRange(zext(*knownVal), zext(*knownVal)));
208 uint64_t max = kMaxDim;
209 if (auto specified = getUpperBound())
210 max = specified->getZExtValue();
211 setResultRange(getResult(), getIndexRange(1, max));
212}
213
214void ThreadIdOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
215 SetIntRangeFn setResultRange) {
216 uint64_t max = kMaxDim;
217 if (auto fromContext = getKnownDimensionSizeAround(
218 *this, DimensionKind::Block, getDimension()))
219 max = zext(*fromContext);
220 if (auto specified = getUpperBound())
221 max = specified->getZExtValue();
222 setResultRange(getResult(), getIndexRange(0, max - 1ULL));
223}
224
225void LaneIdOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
226 SetIntRangeFn setResultRange) {
227 uint64_t max = kMaxSubgroupSize;
228 if (auto specified = getUpperBound())
229 max = specified->getZExtValue();
230 setResultRange(getResult(), getIndexRange(0, max - 1ULL));
231}
232
233void SubgroupIdOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
234 SetIntRangeFn setResultRange) {
235 uint64_t max = kMaxDim;
236 if (auto specified = getUpperBound())
237 max = specified->getZExtValue();
238 setResultRange(getResult(), getIndexRange(0, max - 1ULL));
239}
240
241void GlobalIdOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
242 SetIntRangeFn setResultRange) {
243 if (auto specified = getUpperBound())
244 return setResultRange(getResult(),
245 getIndexRange(0, specified->getZExtValue() - 1ULL));
246
247 uint64_t blockDimMax = zext(
248 getKnownDimensionSizeAround(*this, DimensionKind::Block, getDimension())
249 .value_or(kMaxDim));
250 uint64_t gridDimMax = zext(
251 getKnownDimensionSizeAround(*this, DimensionKind::Grid, getDimension())
252 .value_or(kMaxDim));
253 setResultRange(getResult(),
254 getIndexRange(0, (blockDimMax * gridDimMax) - 1ULL));
255}
256
257void NumSubgroupsOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
258 SetIntRangeFn setResultRange) {
259 uint64_t max = kMaxDim;
260 if (auto specified = getUpperBound())
261 max = specified->getZExtValue();
262 setResultRange(getResult(), getIndexRange(1, max));
263}
264
265void SubgroupSizeOp::inferResultRanges(ArrayRef<ConstantIntRanges>,
266 SetIntRangeFn setResultRange) {
267 uint64_t max = kMaxSubgroupSize;
268 if (auto specified = getUpperBound())
269 max = specified->getZExtValue();
270 setResultRange(getResult(), getIndexRange(1, max));
271}
272
273void LaunchOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
274 SetIntRangeFn setResultRange) {
275 auto setRange = [&](const ConstantIntRanges &argRange, Value dimResult,
276 Value idxResult) {
277 if (argRange.umin().getBitWidth() != IndexType::kInternalStorageBitWidth)
278 return;
279 ConstantIntRanges dimRange =
280 argRange.intersection(getIndexRange(1, kMaxDim));
281 setResultRange(dimResult, dimRange);
282 ConstantIntRanges idxRange =
283 getIndexRange(0, dimRange.umax().getZExtValue() - 1);
284 setResultRange(idxResult, idxRange);
285 };
286
287 argRanges = argRanges.drop_front(getAsyncDependencies().size());
288 KernelDim3 gridDims = getGridSize();
289 KernelDim3 blockIds = getBlockIds();
290 setRange(argRanges[0], gridDims.x, blockIds.x);
291 setRange(argRanges[1], gridDims.y, blockIds.y);
292 setRange(argRanges[2], gridDims.z, blockIds.z);
293 KernelDim3 blockDims = getBlockSize();
294 KernelDim3 threadIds = getThreadIds();
295 setRange(argRanges[3], blockDims.x, threadIds.x);
296 setRange(argRanges[4], blockDims.y, threadIds.y);
297 setRange(argRanges[5], blockDims.z, threadIds.z);
298}
static std::optional< int64_t > getUpperBound(Value iv)
Gets the constant upper bound on an affine.for iv.
static std::optional< uint32_t > getKnownLaunchAttr(GPUFuncOp func, DimensionKind dims, Dimension dim)
static Value valueByDim(KernelDim3 dims, Dimension dim)
static constexpr uint64_t kMaxClusterDim
static constexpr uint64_t kMaxDim
static uint64_t zext(uint32_t arg)
static ConstantIntRanges getIndexRange(uint64_t umin, uint64_t umax)
static constexpr uint64_t kMaxSubgroupSize
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
A set of arbitrary-precision integers representing bounds on a given integer value.
static ConstantIntRanges fromUnsigned(const APInt &umin, const APInt &umax)
Create an ConstantIntRanges with the unsigned minimum and maximum equal to umin and umax and the sign...
ConstantIntRanges intersection(const ConstantIntRanges &other) const
Returns the intersection (computed separately for signed and unsigned bounds) of this range and other...
const APInt & umax() const
The maximum value of an integer when it is interpreted as unsigned.
const APInt & umin() const
The minimum value of an integer when it is interpreted as unsigned.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
std::optional< uint32_t > getKnownDimensionSizeAround(Operation *op, DimensionKind kind, Dimension dim)
Retrieve the constant bounds for a given dimension and dimension kind from the context surrounding op...
SmallVector< unsigned > getBlockSize(AffineMap dimToLvl)
Given the dimToLvl map, returns the block sizes in a vector.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
Definition Matchers.h:527
llvm::function_ref< void(Value, const ConstantIntRanges &)> SetIntRangeFn
The type of the setResultRanges callback provided to ops implementing InferIntRangeInterface.
detail::DenseArrayAttrImpl< int32_t > DenseI32ArrayAttr
Utility class for the GPU dialect to represent triples of Values accessible through ....
Definition GPUDialect.h:39