28#include "llvm/ADT/STLExtras.h"
29#include "llvm/ADT/SmallVector.h"
30#include "llvm/Support/DebugLog.h"
31#include "llvm/Support/InterleavedRange.h"
40#define DEBUG_TYPE "gpu-transforms"
51static FailureOr<SmallVector<Value>>
55 std::string &errorMsg) {
56 LDBG() <<
"----activeMappingSizes: " << llvm::interleaved(activeMappingSizes);
57 LDBG() <<
"----availableMappingSizes: "
58 << llvm::interleaved(availableMappingSizes);
61 for (
auto [activeId, activeMappingSize, availableMappingSize] :
62 llvm::zip_equal(activeIds, activeMappingSizes, availableMappingSizes)) {
63 if (activeMappingSize > availableMappingSize) {
64 errorMsg =
"Trying to map to fewer GPU threads than loop iterations but "
65 "overprovisioning is not yet supported. Try additional tiling "
66 "before mapping or map to more threads.";
69 if (activeMappingSize == availableMappingSize)
73 Value pred = arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::ult,
75 predicateOps.push_back(pred);
81template <
typename ThreadOrBlockIdOp>
84 LDBG() <<
"----buildLinearId with originalBasisOfr: "
85 << llvm::interleaved(originalBasisOfr);
86 assert(originalBasisOfr.size() == 3 &&
"expected 3 sizes");
92 ThreadOrBlockIdOp::create(rewriter, loc, indexType, Dimension::x)
94 ThreadOrBlockIdOp::create(rewriter, loc, indexType, Dimension::y)
96 ThreadOrBlockIdOp::create(rewriter, loc, indexType, Dimension::z)
98 originalBasisOfr[0], originalBasisOfr[1]};
100 rewriter, loc, tx + ty * bdx + tz * bdx * bdy, vals);
107template <
typename ThreadOrBlockIdOp>
110 DeviceMaskingAttrInterface mask =
nullptr) {
117 mask.getMaxNumPhysicalIds() * multiplicity) {
120 "mask representation too short to capture all physical ids: ") +
121 std::to_string(mask.getMaxNumPhysicalIds()),
130 Value physicalLinearId =
136 rewriter, loc, d0.
floorDiv(multiplicity), {physicalLinearId});
139 Value scaledLinearIdI64;
140 Value scaledLinearId =
145 scaledLinearIdI64 = arith::IndexCastUIOp::create(
146 rewriter, loc, rewriter.getI64Type(), scaledLinearId);
147 Value logicalLinearIdI64 =
148 mask.createLogicalLinearMappingId(rewriter, scaledLinearIdI64);
149 scaledLinearId = arith::IndexCastUIOp::create(
150 rewriter, loc, rewriter.getIndexType(), logicalLinearIdI64);
151 LDBG() <<
"------adjusting linearId with mask: " << scaledLinearId;
162 for (
AffineExpr e : llvm::reverse(delinearizingExprs)) {
167 std::string errorMsg;
171 Value isActiveIdPredicate =
172 mask.createIsActiveIdPredicate(rewriter, scaledLinearIdI64);
173 LDBG() <<
"------adjusting predicate with mask: " << isActiveIdPredicate;
174 predicateOps.push_back(isActiveIdPredicate);
177 FailureOr<SmallVector<Value>> maybePredicateOps =
181 if (succeeded(maybePredicateOps))
182 predicateOps = std::move(*maybePredicateOps);
187 std::move(predicateOps)};
196template <
typename ThreadOrBlockIdOp>
203 ThreadOrBlockIdOp::create(rewriter, loc, indexType, Dimension::x),
204 ThreadOrBlockIdOp::create(rewriter, loc, indexType, Dimension::y),
205 ThreadOrBlockIdOp::create(rewriter, loc, indexType, Dimension::z)};
210 rewriter, loc, d0.
floorDiv(multiplicity), {scaledIds[0]}));
213 forallMappingSizeInOriginalBasis[0] *= multiplicity;
215 std::string errorMsg;
217 FailureOr<SmallVector<Value>> maybePredicateOps =
219 originalBasis, errorMsg);
220 if (succeeded(maybePredicateOps))
221 predicateOps = std::move(*maybePredicateOps);
224 std::move(scaledIds),
225 std::move(predicateOps)};
240 Value physicalLinearId =
246 rewriter, loc, d0 % warpSize, {physicalLinearId});
256 for (
AffineExpr e : llvm::reverse(delinearizingExprs)) {
262 std::string errorMsg;
265 rewriter, loc, cast<Value>(laneId),
computeProduct(forallMappingSizes),
267 if (succeeded(maybePredicateOps))
268 predicateOps = std::move(*maybePredicateOps);
272 std::move(predicateOps)};
285 if (useLinearMapping) {
286 for (uint64_t d =
static_cast<uint64_t
>(MappingId::LinearDim0),
287 e = getMaxEnumValForMappingId();
291 for (uint64_t d =
static_cast<uint64_t
>(MappingId::DimX),
292 e =
static_cast<uint64_t
>(MappingId::DimZ);
299 DeviceMaskingAttrInterface mask)
301 return GPUBlockMappingAttr::get(ctx,
id);
303 assert((!mask || useLinearMapping) &&
"mask requires linear mapping");
304 idBuilder = useLinearMapping
310 bool useLinearMapping,
311 DeviceMaskingAttrInterface mask)
314 return GPUWarpgroupMappingAttr::get(ctx,
id);
317 assert((!mask || useLinearMapping) &&
"mask requires linear mapping");
318 idBuilder = useLinearMapping
320 kNumWarpsPerGroup * warpSize, mask)
322 kNumWarpsPerGroup * warpSize);
326 bool useLinearMapping,
327 DeviceMaskingAttrInterface mask)
330 return GPUWarpMappingAttr::get(ctx,
id);
333 assert((!mask || useLinearMapping) &&
"mask requires linear mapping");
334 idBuilder = useLinearMapping
341 DeviceMaskingAttrInterface mask)
343 return GPUThreadMappingAttr::get(ctx,
id);
352 bool unused, DeviceMaskingAttrInterface mask)
355 return GPULaneMappingAttr::get(ctx,
id);
358 assert(!mask &&
"mask NYI for lanes, unclear it should be at all");
363 std::optional<int64_t> gridDimX,
364 std::optional<int64_t> gridDimY,
365 std::optional<int64_t> gridDimZ,
366 std::optional<int64_t> blockDimX,
367 std::optional<int64_t> blockDimY,
368 std::optional<int64_t> blockDimZ) {
372 if ((blockDimX.value_or(1) * blockDimY.value_or(1) * blockDimZ.value_or(1)) >
374 (gridDimX.value_or(1) * gridDimY.value_or(1) * gridDimZ.value_or(1)) >
382 return transformOp.emitSilenceableError()
383 <<
"Trying to launch a GPU kernel with grid_dims = ("
384 << gridDimX.value_or(1) <<
", " << gridDimY.value_or(1) <<
", "
385 << gridDimZ.value_or(1) <<
") block_dims = ("
386 << blockDimX.value_or(1) <<
", " << blockDimY.value_or(1) <<
", "
387 << blockDimZ.value_or(1) <<
"). It is larger than the limits.";
394 LaunchOp &launchOp, std::optional<int64_t> gridDimX,
395 std::optional<int64_t> gridDimY, std::optional<int64_t> gridDimZ,
396 std::optional<int64_t> blockDimX, std::optional<int64_t> blockDimY,
397 std::optional<int64_t> blockDimZ) {
399 checkGpuLimits(transformOp, gridDimX, gridDimY, gridDimZ, blockDimX,
400 blockDimY, blockDimZ);
401 if (!
diag.succeeded())
409 Value gridSizeX = gridDimX.has_value() ?
createConst(gridDimX.value()) : one;
410 Value gridSizeY = gridDimY.has_value() ?
createConst(gridDimY.value()) : one;
411 Value gridSizeZ = gridDimZ.has_value() ?
createConst(gridDimZ.value()) : one;
412 Value blkSizeX = blockDimX.has_value() ?
createConst(blockDimX.value()) : one;
413 Value blkSizeY = blockDimY.has_value() ?
createConst(blockDimY.value()) : one;
414 Value blkSizeZ = blockDimZ.has_value() ?
createConst(blockDimZ.value()) : one;
415 launchOp = LaunchOp::create(rewriter, loc, gridSizeX, gridSizeY, gridSizeZ,
416 blkSizeX, blkSizeY, blkSizeZ);
418 TerminatorOp::create(rewriter, loc);
425 TransformOpInterface transformOp, std::optional<int64_t> gridDimX,
426 std::optional<int64_t> gridDimY, std::optional<int64_t> gridDimZ,
427 std::optional<int64_t> blockDimX, std::optional<int64_t> blockDimY,
428 std::optional<int64_t> blockDimZ) {
430 checkGpuLimits(transformOp, gridDimX, gridDimY, gridDimZ, blockDimX,
431 blockDimY, blockDimZ);
432 if (!
diag.succeeded())
435 KernelDim3 currentBlockdim = gpuLaunch.getBlockSizeOperandValues();
438 auto createConstValue = [&](
int dim) {
443 if (gridDimX.has_value())
444 gpuLaunch.getGridSizeXMutable().assign(createConstValue(gridDimX.value()));
445 if (gridDimY.has_value())
446 gpuLaunch.getGridSizeYMutable().assign(createConstValue(gridDimY.value()));
447 if (gridDimZ.has_value())
448 gpuLaunch.getGridSizeZMutable().assign(createConstValue(gridDimZ.value()));
449 if (blockDimX.has_value())
450 gpuLaunch.getBlockSizeXMutable().assign(
451 createConstValue(blockDimX.value()));
452 if (blockDimY.has_value())
453 gpuLaunch.getBlockSizeYMutable().assign(
454 createConstValue(blockDimY.value()));
455 if (blockDimZ.has_value())
456 gpuLaunch.getBlockSizeZMutable().assign(
457 createConstValue(blockDimZ.value()));
true
Given two iterators into the same block, return "true" if a is before `b.
static std::string diag(const llvm::Value &value)
constexpr int kMaxGriddimz
constexpr int kMaxTotalBlockdim
constexpr int kMaxGriddimy
constexpr int kMaxBlockdimx
constexpr int kMaxBlockdimz
constexpr int kMaxGriddimx
constexpr int kMaxBlockdimy
constexpr int kMaxTotalGriddim
Base type for affine expression.
AffineExpr floorDiv(uint64_t v) const
MLIRContext * getContext() const
The result of a transform IR operation application.
static DiagnosedSilenceableFailure success()
Constructs a DiagnosedSilenceableFailure in the success state.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
RAII guard to reset the insertion point of the builder when destroyed.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
void setInsertionPointAfterValue(Value val)
Sets the insertion point to the node after the specified value.
This class represents a single result from folding an operation.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Location getLoc() const
Return the location of this value.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
Include the generated interface declarations.
OpFoldResult getAsIndexOpFoldResult(MLIRContext *ctx, int64_t val)
Convert int64_t to integer attributes of index type and return them as OpFoldResult.
SmallVector< int64_t > computeStrides(ArrayRef< int64_t > sizes)
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
SmallVector< int64_t > delinearize(int64_t linearIndex, ArrayRef< int64_t > strides)
Given the strides together with a linear index in the dimension space, return the vector-space offset...
int64_t computeProduct(ArrayRef< int64_t > basis)
Self-explicit.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
Utility class for the GPU dialect to represent triples of Values accessible through ....