MLIR 24.0.0git
GPUTransformOps.cpp
Go to the documentation of this file.
1//===- GPUTransformOps.cpp - Implementation of GPU transform ops ----------===//
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
32#include "mlir/IR/AffineExpr.h"
33#include "mlir/IR/Builders.h"
35#include "mlir/IR/IRMapping.h"
36#include "mlir/IR/MLIRContext.h"
38#include "mlir/IR/Visitors.h"
39#include "mlir/Support/LLVM.h"
41#include "llvm/ADT/STLExtras.h"
42#include "llvm/ADT/SmallVector.h"
43#include "llvm/ADT/TypeSwitch.h"
44#include "llvm/Support/DebugLog.h"
45#include "llvm/Support/ErrorHandling.h"
46#include "llvm/Support/InterleavedRange.h"
47#include "llvm/Support/LogicalResult.h"
48#include <optional>
49#include <type_traits>
50
51using namespace mlir;
52using namespace mlir::gpu;
53using namespace mlir::transform;
54using namespace mlir::transform::gpu;
55
56#define DEBUG_TYPE "gpu-transforms"
57
58//===----------------------------------------------------------------------===//
59// Apply...ConversionPatternsOp
60//===----------------------------------------------------------------------===//
61
62void transform::ApplyGPUToNVVMConversionPatternsOp::populatePatterns(
63 TypeConverter &typeConverter, RewritePatternSet &patterns) {
64 auto &llvmTypeConverter = static_cast<LLVMTypeConverter &>(typeConverter);
65 configureGpuToNVVMTypeConverter(llvmTypeConverter);
66 // Set higher benefit, so patterns will run before generic LLVM lowering.
67 populateGpuToNVVMConversionPatterns(llvmTypeConverter, patterns,
68 getBenefit());
69}
70
71LogicalResult
72transform::ApplyGPUToNVVMConversionPatternsOp::verifyTypeConverter(
73 transform::TypeConverterBuilderOpInterface builder) {
74 if (builder.getTypeConverterType() != "LLVMTypeConverter")
75 return emitOpError("expected LLVMTypeConverter");
76 return success();
77}
78
79void transform::ApplyGPUWwmaToNVVMConversionPatternsOp::populatePatterns(
80 TypeConverter &typeConverter, RewritePatternSet &patterns) {
81 auto &llvmTypeConverter = static_cast<LLVMTypeConverter &>(typeConverter);
82 populateGpuWMMAToNVVMConversionPatterns(llvmTypeConverter, patterns);
83}
84
85LogicalResult
86transform::ApplyGPUWwmaToNVVMConversionPatternsOp::verifyTypeConverter(
87 transform::TypeConverterBuilderOpInterface builder) {
88 if (builder.getTypeConverterType() != "LLVMTypeConverter")
89 return emitOpError("expected LLVMTypeConverter");
90 return success();
91}
92
93void transform::ApplyGPUSubgroupReduceToNVVMConversionPatternsOp::
94 populatePatterns(TypeConverter &typeConverter,
95 RewritePatternSet &patterns) {
96 auto &llvmTypeConverter = static_cast<LLVMTypeConverter &>(typeConverter);
97 populateGpuSubgroupReduceOpLoweringPattern(llvmTypeConverter, patterns);
98}
99
100LogicalResult transform::ApplyGPUSubgroupReduceToNVVMConversionPatternsOp::
101 verifyTypeConverter(transform::TypeConverterBuilderOpInterface builder) {
102 if (builder.getTypeConverterType() != "LLVMTypeConverter")
103 return emitOpError("expected LLVMTypeConverter");
104 return success();
105}
106
107void transform::ApplyGPUToROCDLConversionPatternsOp::populatePatterns(
108 TypeConverter &typeConverter, RewritePatternSet &patterns) {
109 auto &llvmTypeConverter = static_cast<LLVMTypeConverter &>(typeConverter);
111 // The verifier has already rejected anything unparseable.
112 FailureOr<ROCDL::TargetInfo> targetInfo =
113 ROCDL::TargetInfo::get(getArch(), getWavesize().value_or(0));
114 assert(llvm::succeeded(targetInfo) && "verifier accepted this target");
116 llvmTypeConverter, patterns, mlir::gpu::amd::Runtime::HIP, *targetInfo);
117}
118
119LogicalResult
120transform::ApplyGPUToROCDLConversionPatternsOp::verifyTypeConverter(
121 transform::TypeConverterBuilderOpInterface builder) {
122 if (failed(ROCDL::TargetInfo::get(getArch(), getWavesize().value_or(0),
123 [&] { return emitOpError(); })))
124 return failure();
125 if (builder.getTypeConverterType() != "LLVMTypeConverter")
126 return emitOpError("expected LLVMTypeConverter");
127 return success();
128}
129
130//===----------------------------------------------------------------------===//
131// Apply...PatternsOp
132//===----------------------------------------------------------------------===//s
133
134void ApplyGPURewritePatternsOp::populatePatterns(RewritePatternSet &patterns) {
136}
137
138void transform::ApplyGPUPromoteShuffleToAMDGPUPatternsOp::populatePatterns(
139 RewritePatternSet &patterns) {
140 std::optional<StringRef> archName = getArch();
141 std::optional<ROCDL::TargetInfo> targetInfo;
142 if (archName) {
143 // The verifier has already rejected anything unparseable.
144 FailureOr<ROCDL::TargetInfo> parsed = ROCDL::TargetInfo::get(*archName);
145 assert(llvm::succeeded(parsed) && "verifier accepted this target");
146 targetInfo = *parsed;
147 }
148
149 populateGpuPromoteShuffleToAMDGPUPatterns(patterns, targetInfo);
150}
151
152LogicalResult transform::ApplyGPUPromoteShuffleToAMDGPUPatternsOp::verify() {
153 std::optional<StringRef> archName = getArch();
154 if (!archName)
155 return success();
156
157 if (failed(ROCDL::TargetInfo::get(*archName, /*waveSize=*/0,
158 [&] { return emitOpError(); })))
159 return failure();
160 return success();
161}
162
163//===----------------------------------------------------------------------===//
164// ApplyUnrollVectorsSubgroupMmaOp
165//===----------------------------------------------------------------------===//
166
167/// Pick an unrolling order that will allow tensorcore operation to reuse LHS
168/// register.
169static std::optional<SmallVector<int64_t>>
170gpuMmaUnrollOrder(vector::ContractionOp contract) {
172 // First make reduction the outer dimensions.
173 for (auto [index, iter] : llvm::enumerate(contract.getIteratorTypes())) {
174 if (vector::isReductionIterator(iter)) {
175 order.push_back(index);
176 }
177 }
178
179 llvm::SmallDenseSet<int64_t> dims;
180 for (AffineExpr expr : contract.getIndexingMapsArray()[0].getResults()) {
181 dims.insert(cast<AffineDimExpr>(expr).getPosition());
182 }
183 // Then parallel dimensions that are part of Lhs as we want to re-use Lhs.
184 for (auto [index, iter] : llvm::enumerate(contract.getIteratorTypes())) {
185 if (vector::isParallelIterator(iter) && dims.count(index)) {
186 order.push_back(index);
187 }
188 }
189 // Then the remaining parallel loops.
190 for (auto [index, iter] : llvm::enumerate(contract.getIteratorTypes())) {
191 if (vector::isParallelIterator(iter) && !dims.count(index)) {
192 order.push_back(index);
193 }
194 }
195 return order;
196}
197
198/// Returns the target vector size for the target operation based on the native
199/// vector size specified with `m`, `n`, and `k`.
200static std::optional<SmallVector<int64_t>>
202 if (auto contract = dyn_cast<vector::ContractionOp>(op)) {
203 int64_t contractRank = contract.getIteratorTypes().size();
204 if (contractRank < 3)
205 return std::nullopt;
206 SmallVector<int64_t> nativeSize(contractRank - 3, 1);
207 nativeSize.append({m, n, k});
208 return nativeSize;
209 }
210 if (auto writeOp = dyn_cast<vector::TransferWriteOp>(op)) {
211 int64_t writeRank = writeOp.getVectorType().getRank();
212 if (writeRank < 2)
213 return std::nullopt;
214 SmallVector<int64_t> nativeSize(writeRank - 2, 1);
215 nativeSize.append({m, n});
216 return nativeSize;
217 }
218 if (auto readOp = dyn_cast<vector::TransferReadOp>(op)) {
219 // Transfer read ops may need different shapes based on how they are being
220 // used. For simplicity just match the shape used by the extract strided op.
221 VectorType sliceType;
222 for (Operation *users : op->getUsers()) {
223 auto extract = dyn_cast<vector::ExtractStridedSliceOp>(users);
224 if (!extract)
225 return std::nullopt;
226 auto vecType = cast<VectorType>(extract.getResult().getType());
227 if (sliceType && sliceType != vecType)
228 return std::nullopt;
229 sliceType = vecType;
230 }
231 return llvm::to_vector(sliceType.getShape());
232 }
234 if (auto vecType = dyn_cast<VectorType>(op->getResultTypes()[0])) {
235 // TODO: The condition for unrolling elementwise should be restricted
236 // only to operations that need unrolling (connected to the contract).
237 if (vecType.getRank() < 2)
238 return std::nullopt;
239
240 // First check whether there is a slice to infer the shape from. This is
241 // required for cases where the accumulator type differs from the input
242 // types, in which case we will see an `arith.ext_` between the contract
243 // and transfer_read which needs to be unrolled.
244 VectorType sliceType;
245 for (Operation *users : op->getUsers()) {
246 auto extract = dyn_cast<vector::ExtractStridedSliceOp>(users);
247 if (!extract)
248 return std::nullopt;
249 auto vecType = cast<VectorType>(extract.getResult().getType());
250 if (sliceType && sliceType != vecType)
251 return std::nullopt;
252 sliceType = vecType;
253 }
254 if (sliceType)
255 return llvm::to_vector(sliceType.getShape());
256
257 // Else unroll for trailing elementwise.
258 SmallVector<int64_t> nativeSize(vecType.getRank() - 2, 1);
259 // Map elementwise ops to the output shape.
260 nativeSize.append({m, n});
261 return nativeSize;
262 }
263 }
264 return std::nullopt;
265}
266
267void transform::ApplyUnrollVectorsSubgroupMmaOp::populatePatterns(
268 RewritePatternSet &patterns) {
269 auto unrollOrder = [](Operation *op) -> std::optional<SmallVector<int64_t>> {
270 auto contract = dyn_cast<vector::ContractionOp>(op);
271 if (!contract)
272 return std::nullopt;
274 };
275
276 int64_t m = getM();
277 int64_t n = getN();
278 int64_t k = getK();
279 auto nativeShapeFn =
280 [m, n, k](Operation *op) -> std::optional<SmallVector<int64_t>> {
281 return getSubgroupMmaNativeVectorSize(op, m, n, k);
282 };
283 vector::populateVectorUnrollPatterns(
285 .setNativeShapeFn(nativeShapeFn)
286 .setUnrollTraversalOrderFn(unrollOrder));
287}
288
289//===----------------------------------------------------------------------===//
290// EliminateBarriersOp
291//===----------------------------------------------------------------------===//
292
293void EliminateBarriersOp::populatePatterns(RewritePatternSet &patterns) {
295}
296
297//===----------------------------------------------------------------------===//
298// Block and thread mapping utilities.
299//===----------------------------------------------------------------------===//
300
301namespace {
302/// Local types used for mapping verification.
303struct MappingKind {};
304struct BlockMappingKind : MappingKind {};
305struct ThreadMappingKind : MappingKind {};
306} // namespace
307
309definiteFailureHelper(std::optional<TransformOpInterface> transformOp,
310 Operation *target, const Twine &message) {
311 if (transformOp.has_value())
312 return transformOp->emitDefiniteFailure() << message;
313 return emitDefiniteFailure(target, message);
314}
315
316/// Check if given mapping attributes are one of the desired attributes
317template <typename MappingKindType>
319checkMappingAttributeTypes(std::optional<TransformOpInterface> transformOp,
320 scf::ForallOp forallOp) {
321 if (!forallOp.getMapping().has_value()) {
322 return definiteFailureHelper(transformOp, forallOp,
323 "scf.forall op requires a mapping attribute");
324 }
325
326 bool hasBlockMapping = llvm::any_of(forallOp.getMapping().value(),
327 llvm::IsaPred<GPUBlockMappingAttr>);
328 bool hasWarpgroupMapping = llvm::any_of(
329 forallOp.getMapping().value(), llvm::IsaPred<GPUWarpgroupMappingAttr>);
330 bool hasWarpMapping = llvm::any_of(forallOp.getMapping().value(),
331 llvm::IsaPred<GPUWarpMappingAttr>);
332 bool hasThreadMapping = llvm::any_of(forallOp.getMapping().value(),
333 llvm::IsaPred<GPUThreadMappingAttr>);
334 bool hasLaneMapping = llvm::any_of(forallOp.getMapping().value(),
335 llvm::IsaPred<GPULaneMappingAttr>);
336 int64_t countMappingTypes = 0;
337 countMappingTypes += hasBlockMapping ? 1 : 0;
338 countMappingTypes += hasWarpgroupMapping ? 1 : 0;
339 countMappingTypes += hasWarpMapping ? 1 : 0;
340 countMappingTypes += hasThreadMapping ? 1 : 0;
341 countMappingTypes += hasLaneMapping ? 1 : 0;
342 if (countMappingTypes > 1) {
344 transformOp, forallOp,
345 "cannot mix different mapping types, use nesting");
346 }
347 if (std::is_same<MappingKindType, BlockMappingKind>::value &&
348 !hasBlockMapping) {
350 transformOp, forallOp,
351 "scf.forall op requires a mapping attribute of kind 'block'");
352 }
353 if (std::is_same<MappingKindType, ThreadMappingKind>::value &&
354 !hasLaneMapping && !hasThreadMapping && !hasWarpMapping &&
355 !hasWarpgroupMapping) {
356 return definiteFailureHelper(transformOp, forallOp,
357 "scf.forall op requires a mapping attribute "
358 "of kind 'thread' or 'warp'");
359 }
360
362 for (Attribute map : forallOp.getMapping()->getValue()) {
363 if (seen.contains(map)) {
365 transformOp, forallOp,
366 "duplicate attribute, cannot map different loops "
367 "to the same mapping id");
368 }
369 seen.insert(map);
370 }
371
372 auto isLinear = [](DeviceMappingAttrInterface attr) {
373 return attr.isLinearMapping();
374 };
375 if (llvm::any_of(forallOp.getDeviceMappingAttrs(), isLinear) &&
376 !llvm::all_of(forallOp.getDeviceMappingAttrs(), isLinear)) {
378 transformOp, forallOp,
379 "cannot mix linear and non-linear mapping modes");
380 }
381
382 FailureOr<DeviceMaskingAttrInterface> maybeMaskingAttr =
383 forallOp.getDeviceMaskingAttr();
384 if (succeeded(maybeMaskingAttr) && *maybeMaskingAttr &&
385 !forallOp.usesLinearMapping()) {
387 transformOp, forallOp,
388 "device masking is only available in linear mapping mode");
389 }
390
392}
393
394template <typename MappingKindType>
396verifyGpuMapping(std::optional<TransformOpInterface> transformOp,
397 scf::ForallOp forallOp) {
398 // Check the types of the mapping attributes match.
400 checkMappingAttributeTypes<MappingKindType>(transformOp, forallOp);
401 if (!typeRes.succeeded())
402 return typeRes;
403
404 // Perform other non-types verifications.
405 if (!forallOp.isNormalized())
406 return definiteFailureHelper(transformOp, forallOp,
407 "unsupported non-normalized loops");
408 if (forallOp.getNumResults() > 0)
409 return definiteFailureHelper(transformOp, forallOp,
410 "only bufferized scf.forall can be mapped");
411 bool useLinearMapping = forallOp.usesLinearMapping();
412 // TODO: This would be more natural with support for Optional<EnumParameter>
413 // in GPUDeviceMappingAttr.
414 int64_t maxNumMappingsSupported =
415 useLinearMapping ? (getMaxEnumValForMappingId() -
416 static_cast<uint64_t>(MappingId::DimZ))
417 : 3;
418 if (forallOp.getRank() > maxNumMappingsSupported) {
419 return definiteFailureHelper(transformOp, forallOp,
420 "scf.forall with rank > ")
421 << maxNumMappingsSupported
422 << " does not lower for the specified mapping attribute type";
423 }
424 auto numParallelIterations =
425 getConstantIntValues(forallOp.getMixedUpperBound());
426 if (!forallOp.isNormalized() || !numParallelIterations.has_value()) {
428 transformOp, forallOp,
429 "requires statically sized, normalized forall op");
430 }
432}
433
434/// Struct to return the result of the rewrite of a forall operation.
439
440/// Helper to replace ids of dimensions known to be 1 by 0 to simplify the IR.
441template <typename OpTy, typename OperationOrBlock>
442static void
444 OperationOrBlock *parent, Value replacement,
445 ArrayRef<int64_t> availableMappingSizes) {
446 parent->walk([&](OpTy idOp) {
447 if (availableMappingSizes[static_cast<int64_t>(idOp.getDimension())] == 1)
448 rewriter.replaceAllUsesWith(idOp.getResult(), replacement);
449 });
450}
451
453 RewriterBase &rewriter, std::optional<TransformOpInterface> transformOp,
454 scf::ForallOp forallOp, ArrayRef<int64_t> availableMappingSizes,
455 ForallRewriteResult &result, const GpuIdBuilder &gpuIdBuilder) {
456 LDBG() << "--start rewriteOneForallCommonImpl";
457
458 // Step 1. Complete the mapping to a full mapping (with 1s) if necessary.
459 auto numParallelIterations =
460 getConstantIntValues(forallOp.getMixedUpperBound());
461 assert(forallOp.isNormalized() && numParallelIterations.has_value() &&
462 "requires statically sized, normalized forall op");
463 SmallVector<int64_t> tmpMappingSizes = numParallelIterations.value();
464 SmallVector<DeviceMappingAttrInterface> forallMappingAttrsVec =
465 forallOp.getDeviceMappingAttrs();
466 SetVector<Attribute> forallMappingAttrs;
467 forallMappingAttrs.insert_range(forallMappingAttrsVec);
468 auto comparator = [](Attribute a, Attribute b) -> bool {
469 return cast<DeviceMappingAttrInterface>(a).getMappingId() <
470 cast<DeviceMappingAttrInterface>(b).getMappingId();
471 };
472
473 // Step 1.b. In the linear case, compute the max mapping to avoid needlessly
474 // mapping all dimensions. In the 3-D mapping case we need to map all
475 // dimensions.
476 DeviceMappingAttrInterface maxMapping = cast<DeviceMappingAttrInterface>(
477 *llvm::max_element(forallMappingAttrs, comparator));
478 DeviceMappingAttrInterface maxLinearMapping;
479 if (maxMapping.isLinearMapping())
480 maxLinearMapping = maxMapping;
481 for (auto attr : gpuIdBuilder.mappingAttributes) {
482 // If attr overflows, just skip.
483 if (maxLinearMapping && comparator(maxLinearMapping, attr))
484 continue;
485 // Try to insert. If element was already present, just continue.
486 if (!forallMappingAttrs.insert(attr))
487 continue;
488 // Otherwise, we have a new insertion without a size -> use size 1.
489 tmpMappingSizes.push_back(1);
490 }
491 LDBG() << "----tmpMappingSizes extracted from scf.forall op: "
492 << llvm::interleaved(tmpMappingSizes);
493
494 // Step 2. sort the values by the corresponding DeviceMappingAttrInterface.
495 SmallVector<int64_t> forallMappingSizes = getValuesSortedByKey(
496 forallMappingAttrs.getArrayRef(), tmpMappingSizes, comparator);
497 LDBG() << "----forallMappingSizes: " << llvm::interleaved(forallMappingSizes);
498 LDBG() << "----forallMappingAttrs: " << llvm::interleaved(forallMappingAttrs);
499
500 // Step 3. Generate the mappingIdOps using the provided generator.
501 Location loc = forallOp.getLoc();
502 OpBuilder::InsertionGuard guard(rewriter);
503 rewriter.setInsertionPoint(forallOp);
504 SmallVector<int64_t> originalBasis(availableMappingSizes);
505 bool originalBasisWasProvided = !originalBasis.empty();
506 if (!originalBasisWasProvided) {
507 LDBG() << "----originalBasis was not provided, deriving it and there will "
508 "be no "
509 "predication";
510 originalBasis = forallMappingSizes;
511 while (originalBasis.size() < 3)
512 originalBasis.push_back(1);
513 } else {
514 LDBG() << "----originalBasis was provided, using it, there will be "
515 "predication";
516 }
517 LDBG() << "------originalBasis: " << llvm::interleaved(originalBasis);
518
519 IdBuilderResult builderResult =
520 gpuIdBuilder.idBuilder(rewriter, loc, forallMappingSizes, originalBasis);
521 if (!builderResult.errorMsg.empty())
522 return definiteFailureHelper(transformOp, forallOp, builderResult.errorMsg);
523
524 LDBG() << builderResult;
525
526 // Step 4. Map the induction variables to the mappingIdOps, this may involve
527 // a permutation.
528 SmallVector<Value> mappingIdOps = builderResult.mappingIdOps;
529 IRMapping bvm;
530 for (auto [iv, dim] : llvm::zip_equal(
531 forallOp.getInductionVars(),
532 forallMappingAttrs.getArrayRef().take_front(forallOp.getRank()))) {
533 auto mappingAttr = cast<DeviceMappingAttrInterface>(dim);
534 Value peIdOp = mappingIdOps[mappingAttr.getRelativeIndex()];
535 LDBG() << "----map: " << iv << " to " << peIdOp;
536 bvm.map(iv, peIdOp);
537 }
538
539 // Step 5. If the originalBasis is already known, create conditionals to
540 // predicate the region. Otherwise, the current forall determines the
541 // originalBasis and no predication occurs.
542 Value predicate;
543 if (originalBasisWasProvided) {
544 for (Value tmpPredicate : builderResult.predicateOps) {
545 predicate = predicate ? arith::AndIOp::create(rewriter, loc, predicate,
546 tmpPredicate)
547 : tmpPredicate;
548 }
549 }
550
551 // Step 6. Move the body of forallOp.
552 // Erase the terminator first, it will not be used.
553 rewriter.eraseOp(forallOp.getTerminator());
554 Block *targetBlock;
555 Block::iterator insertionPoint;
556 if (predicate) {
557 // Step 6.a. If predicated, move at the beginning.
558 auto ifOp = scf::IfOp::create(rewriter, loc, predicate,
559 /*withElseRegion=*/false);
560 targetBlock = ifOp.thenBlock();
561 insertionPoint = ifOp.thenBlock()->begin();
562 } else {
563 // Step 6.b. Otherwise, move inline just at the rewriter insertion
564 // point.
565 targetBlock = forallOp->getBlock();
566 insertionPoint = rewriter.getInsertionPoint();
567 }
568 Block &sourceBlock = forallOp.getRegion().front();
569 targetBlock->getOperations().splice(insertionPoint,
570 sourceBlock.getOperations());
571
572 // Step 7. RAUW indices.
573 for (Value loopIndex : forallOp.getInductionVars()) {
574 Value threadIdx = bvm.lookup(loopIndex);
575 rewriter.replaceAllUsesWith(loopIndex, threadIdx);
576 }
577
578 // Step 8. Erase old op.
579 rewriter.eraseOp(forallOp);
580
581 LDBG() << "----result forallMappingSizes: "
582 << llvm::interleaved(forallMappingSizes);
583 LDBG() << "----result mappingIdOps: " << llvm::interleaved(mappingIdOps);
584
585 result = ForallRewriteResult{forallMappingSizes, mappingIdOps};
587}
588
589//===----------------------------------------------------------------------===//
590// MapForallToBlocks
591//===----------------------------------------------------------------------===//
592
594 RewriterBase &rewriter, TransformOpInterface transformOp,
595 scf::ForallOp forallOp, SmallVectorImpl<int64_t> &gridDims,
596 const GpuIdBuilder &gpuIdBuilder) {
597 LDBG() << "Start mapForallToBlocksImpl";
598
599 {
600 // GPU-specific verifications. There is no better place to anchor
601 // those right now: the ForallOp is target-independent and the transform
602 // op does not apply to individual ForallOp.
604 verifyGpuMapping<BlockMappingKind>(transformOp, forallOp);
605 if (!diag.succeeded())
606 return diag;
607 }
608
609 Location loc = forallOp.getLoc();
610 Block *parentBlock = forallOp->getBlock();
611 Value zero;
612 {
613 // Create an early zero index value for replacements and immediately reset
614 // the insertion point.
615 OpBuilder::InsertionGuard guard(rewriter);
616 rewriter.setInsertionPointToStart(parentBlock);
617 zero = arith::ConstantIndexOp::create(rewriter, loc, 0);
618 }
619
620 ForallRewriteResult rewriteResult;
622 rewriter, transformOp, forallOp,
623 /*availableMappingSizes=*/gridDims, rewriteResult, gpuIdBuilder);
624
625 // Return if anything goes wrong, use silenceable failure as a match
626 // failure.
627 if (!diag.succeeded())
628 return diag;
629
630 // If gridDims was not provided already, set it from the return.
631 if (gridDims.empty()) {
632 gridDims = rewriteResult.mappingSizes;
633 while (gridDims.size() < 3)
634 gridDims.push_back(1);
635 }
636 assert(gridDims.size() == 3 && "Need 3-D gridDims");
637
638 // Replace ids of dimensions known to be 1 by 0 to simplify the IR.
639 // Here, the result of mapping determines the available mapping sizes.
640 replaceUnitMappingIdsHelper<BlockDimOp>(rewriter, loc, parentBlock, zero,
641 rewriteResult.mappingSizes);
642
644}
645
648 scf::ForallOp &topLevelForallOp,
649 TransformOpInterface transformOp) {
650 auto walkResult = target->walk([&](scf::ForallOp forallOp) {
651 if (forallOp->getParentOfType<scf::ForallOp>())
652 return WalkResult::advance();
653 if (topLevelForallOp)
654 // TODO: Handle multiple forall if they are independent.
655 return WalkResult::interrupt();
656 topLevelForallOp = forallOp;
657 return WalkResult::advance();
658 });
659
660 if (walkResult.wasInterrupted() || !topLevelForallOp)
661 return transformOp.emitSilenceableError()
662 << "could not find a unique topLevel scf.forall";
664}
665
666DiagnosedSilenceableFailure transform::MapForallToBlocks::applyToOne(
669 LaunchOp gpuLaunch = dyn_cast<LaunchOp>(target);
670 auto transformOp = cast<TransformOpInterface>(getOperation());
671
672 if (!getGenerateGpuLaunch() && !gpuLaunch) {
674 emitSilenceableError()
675 << "Given target is not gpu.launch, set `generate_gpu_launch` "
676 "attribute";
677 diag.attachNote(target->getLoc()) << "when applied to this payload op";
678 return diag;
679 }
680
681 scf::ForallOp topLevelForallOp;
683 target, topLevelForallOp, transformOp);
684 if (!diag.succeeded()) {
685 diag.attachNote(target->getLoc()) << "when applied to this payload op";
686 return diag;
687 }
688 assert(topLevelForallOp && "expect an scf.forall");
689
690 SmallVector<int64_t> gridDims{getGridDims()};
691 if (!getGenerateGpuLaunch() && gridDims.size() != 3)
692 return transformOp.emitDefiniteFailure("transform require size-3 mapping");
693
694 OpBuilder::InsertionGuard guard(rewriter);
695 rewriter.setInsertionPoint(topLevelForallOp);
696
697 // Generate gpu launch here and move the forall inside
698 if (getGenerateGpuLaunch()) {
700 createGpuLaunch(rewriter, target->getLoc(), transformOp, gpuLaunch);
701 if (!diag.succeeded())
702 return diag;
703
704 rewriter.setInsertionPointToStart(&gpuLaunch.getBody().front());
705 Operation *newForallOp = rewriter.clone(*topLevelForallOp);
706 rewriter.eraseOp(topLevelForallOp);
707 topLevelForallOp = cast<scf::ForallOp>(newForallOp);
708 }
709
710 // The BlockIdBuilder adapts to whatever is thrown at it.
711 bool useLinearMapping = false;
712 if (topLevelForallOp.getMapping())
713 useLinearMapping = topLevelForallOp.usesLinearMapping();
714
715 FailureOr<DeviceMaskingAttrInterface> maybeMaskingAttr =
716 topLevelForallOp.getDeviceMaskingAttr();
717 assert(succeeded(maybeMaskingAttr) && "unexpected failed maybeMaskingAttr");
718 assert((!*maybeMaskingAttr || useLinearMapping) &&
719 "masking requires linear mapping");
720
721 GpuBlockIdBuilder gpuBlockIdBuilder(getContext(), useLinearMapping,
722 *maybeMaskingAttr);
723
725 rewriter, transformOp, topLevelForallOp, gridDims, gpuBlockIdBuilder);
726 if (!diag.succeeded())
727 return diag;
728
729 // Set the GPU launch configuration for the grid dims late, this is
730 // subject to IR inspection.
731 diag = alterGpuLaunch(rewriter, gpuLaunch,
732 cast<TransformOpInterface>(getOperation()), gridDims[0],
733 gridDims[1], gridDims[2]);
734
735 results.push_back(gpuLaunch);
736 return diag;
737}
738
739LogicalResult transform::MapForallToBlocks::verify() {
740 if (!getGridDims().empty() && getGridDims().size() != 3) {
741 return emitOpError() << "transform requires empty or size-3 grid_dims";
742 }
743 return success();
744}
745
746//===----------------------------------------------------------------------===//
747// MapNestedForallToThreads
748//===----------------------------------------------------------------------===//
749
751 std::optional<TransformOpInterface> transformOp, scf::ForallOp forallOp,
752 ArrayRef<int64_t> numParallelIterations, ArrayRef<int64_t> blockOrGridSizes,
753 int factor, bool useLinearMapping = false) {
754 if (llvm::any_of(blockOrGridSizes, [](int64_t i) { return i <= 0; })) {
755 return definiteFailureHelper(transformOp, forallOp,
756 "block/grid sizes must be strictly positive");
757 }
758 if (!useLinearMapping && blockOrGridSizes.front() % factor != 0) {
760 transformOp, forallOp,
761 Twine("3-D mapping: size of threadIdx.x must be a multiple of ") +
762 Twine(factor));
763 return diag;
764 }
765 bool hasZeroParallelIteration =
766 llvm::any_of(numParallelIterations, [](int64_t i) { return i == 0; });
767 // `computeProduct` requires strictly positive inputs, so handle the
768 // zero-iteration case explicitly to avoid asserting on valid degenerate
769 // loop bounds.
770 int64_t requiredResourceCount =
771 hasZeroParallelIteration ? 0
772 : computeProduct(numParallelIterations) * factor;
773 int64_t availableResourceCount = computeProduct(blockOrGridSizes);
774 if (requiredResourceCount > availableResourceCount) {
776 transformOp, forallOp,
777 Twine("the number of required parallel resources (blocks or "
778 "threads) ") +
779 Twine(requiredResourceCount) +
780 " overflows the number of available resources " +
781 Twine(availableResourceCount));
782 return diag;
783 }
785}
786
788getThreadIdBuilder(std::optional<TransformOpInterface> transformOp,
789 scf::ForallOp forallOp, ArrayRef<int64_t> blockSizes,
790 int64_t warpSize, GpuIdBuilder &gpuIdBuilder) {
791 DeviceMappingAttrInterface mappingAttr =
792 forallOp.getDeviceMappingAttrs().front();
793 bool useLinearMapping = mappingAttr.isLinearMapping();
794
795 // Sanity checks that may result in runtime verification errors.
796 auto numParallelIterations =
797 getConstantIntValues((forallOp.getMixedUpperBound()));
798 if (!forallOp.isNormalized() || !numParallelIterations.has_value()) {
800 transformOp, forallOp,
801 "requires statically sized, normalized forall op");
802 }
803 int64_t factor = 1;
804 if (isa<GPUWarpgroupMappingAttr>(mappingAttr)) {
806 } else if (isa<GPUWarpMappingAttr>(mappingAttr)) {
807 factor = warpSize;
808 }
810 checkMappingSpec(transformOp, forallOp, numParallelIterations.value(),
811 blockSizes, factor, useLinearMapping);
812 if (!diag.succeeded())
813 return diag;
814
815 FailureOr<DeviceMaskingAttrInterface> maybeMaskingAttr =
816 forallOp.getDeviceMaskingAttr();
817 assert(succeeded(maybeMaskingAttr) && "unexpected failed maybeMaskingAttr");
818 assert((!*maybeMaskingAttr || useLinearMapping) &&
819 "masking requires linear mapping");
820
821 // Start mapping.
822 MLIRContext *ctx = forallOp.getContext();
823 gpuIdBuilder =
825 .Case([&](GPUWarpgroupMappingAttr) {
826 return GpuWarpgroupIdBuilder(ctx, warpSize, useLinearMapping,
827 *maybeMaskingAttr);
828 })
829 .Case([&](GPUWarpMappingAttr) {
830 return GpuWarpIdBuilder(ctx, warpSize, useLinearMapping,
831 *maybeMaskingAttr);
832 })
833 .Case([&](GPUThreadMappingAttr) {
834 return GpuThreadIdBuilder(ctx, useLinearMapping, *maybeMaskingAttr);
835 })
836 .Case([&](GPULaneMappingAttr) {
837 return GpuLaneIdBuilder(ctx, warpSize, useLinearMapping,
838 *maybeMaskingAttr);
839 })
840 .DefaultUnreachable("unknown mapping attribute");
842}
843
845 RewriterBase &rewriter, std::optional<TransformOpInterface> transformOp,
846 scf::ForallOp forallOp, ArrayRef<int64_t> blockSizes, int64_t warpSize,
847 bool syncAfterDistribute) {
848
849 {
850 // GPU-specific verifications. There is no better place to anchor
851 // those right now: the ForallOp is target-independent and the transform
852 // op does not apply to individual ForallOp.
854 verifyGpuMapping<ThreadMappingKind>(transformOp, forallOp);
855 if (!diag.succeeded())
856 return diag;
857 }
858
859 GpuIdBuilder gpuIdBuilder;
860 {
861 // Try to construct the id builder, if it fails, return.
863 transformOp, forallOp, blockSizes, warpSize, gpuIdBuilder);
864 if (!diag.succeeded())
865 return diag;
866 }
867
868 Location loc = forallOp.getLoc();
869 OpBuilder::InsertionGuard g(rewriter);
870 // Insert after to allow for syncthreads after `forall` is erased.
871 rewriter.setInsertionPointAfter(forallOp);
872 ForallRewriteResult rewriteResult;
874 rewriter, transformOp, forallOp, blockSizes, rewriteResult, gpuIdBuilder);
875 if (!diag.succeeded())
876 return diag;
877 // Add a syncthreads if needed. TODO: warpsync
878 if (syncAfterDistribute)
879 BarrierOp::create(rewriter, loc);
880
882}
883
885 RewriterBase &rewriter, std::optional<TransformOpInterface> transformOp,
886 Operation *target, ArrayRef<int64_t> blockDims, int64_t warpSize,
887 bool syncAfterDistribute) {
888 LDBG() << "Start mapNestedForallToThreadsImpl";
889 if (blockDims.size() != 3) {
890 return definiteFailureHelper(transformOp, target,
891 "requires size-3 thread mapping");
892 }
893
894 // Create an early zero index value for replacements.
895 Location loc = target->getLoc();
896 Value zero = arith::ConstantIndexOp::create(rewriter, loc, 0);
898 WalkResult walkResult = target->walk([&](scf::ForallOp forallOp) {
900 rewriter, transformOp, forallOp, blockDims, warpSize,
901 syncAfterDistribute);
902 if (diag.isDefiniteFailure())
903 return WalkResult::interrupt();
904 if (diag.succeeded())
905 return WalkResult::skip();
906 return WalkResult::advance();
907 });
908 if (walkResult.wasInterrupted())
909 return diag;
910
911 // Replace ids of dimensions known to be 1 by 0 to simplify the IR.
912 // Here, the result of mapping determines the available mapping sizes.
914 blockDims);
915
917}
918
919DiagnosedSilenceableFailure transform::MapNestedForallToThreads::applyToOne(
921 ApplyToEachResultList &results, TransformState &state) {
922 LaunchOp gpuLaunch = dyn_cast<LaunchOp>(target);
923 auto transformOp = cast<TransformOpInterface>(getOperation());
924
925 // Basic high-level verifications.
926 if (!gpuLaunch)
927 return emitSilenceableError() << "Given target is not a gpu.launch";
928
929 // Mapping to block ids.
930 SmallVector<int64_t> blockDims{getBlockDims()};
932 checkGpuLimits(transformOp, std::nullopt, std::nullopt, std::nullopt,
933 blockDims[0], blockDims[1], blockDims[2]);
934 if (diag.isSilenceableFailure()) {
935 diag.attachNote(getLoc()) << getBlockDimsAttrName() << " is too large";
936 return diag;
937 }
938
939 // Set the GPU launch configuration for the block dims early, this is not
940 // subject to IR inspection.
941 diag = alterGpuLaunch(rewriter, gpuLaunch, transformOp, std::nullopt,
942 std::nullopt, std::nullopt, blockDims[0], blockDims[1],
943 blockDims[2]);
944
945 rewriter.setInsertionPointToStart(&gpuLaunch.getBody().front());
946 diag =
947 mapNestedForallToThreadsImpl(rewriter, transformOp, gpuLaunch, blockDims,
948 getWarpSize(), getSyncAfterDistribute());
949
950 results.push_back(gpuLaunch.getOperation());
951 return diag;
952}
953
954//===----------------------------------------------------------------------===//
955// Transform op registration
956//===----------------------------------------------------------------------===//
957
958namespace {
959/// Registers new ops and declares PDL as dependent dialect since the
960/// additional ops are using PDL types for operands and results.
961class GPUTransformDialectExtension
963 GPUTransformDialectExtension> {
964public:
965 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(GPUTransformDialectExtension)
966
967 GPUTransformDialectExtension() {
968 declareGeneratedDialect<GPUDialect>();
969 declareGeneratedDialect<amdgpu::AMDGPUDialect>();
970 declareGeneratedDialect<arith::ArithDialect>();
971 declareGeneratedDialect<scf::SCFDialect>();
972 registerTransformOps<
973#define GET_OP_LIST
974#include "mlir/Dialect/GPU/TransformOps/GPUTransformOps.cpp.inc"
975 >();
976 }
977};
978} // namespace
979
980#define GET_OP_CLASSES
981#include "mlir/Dialect/GPU/TransformOps/GPUTransformOps.cpp.inc"
982
984 registry.addExtensions<GPUTransformDialectExtension>();
985}
return success()
static DiagnosedSilenceableFailure checkMappingAttributeTypes(std::optional< TransformOpInterface > transformOp, scf::ForallOp forallOp)
Check if given mapping attributes are one of the desired attributes.
static DiagnosedSilenceableFailure rewriteOneForallCommonImpl(RewriterBase &rewriter, std::optional< TransformOpInterface > transformOp, scf::ForallOp forallOp, ArrayRef< int64_t > availableMappingSizes, ForallRewriteResult &result, const GpuIdBuilder &gpuIdBuilder)
static std::optional< SmallVector< int64_t > > getSubgroupMmaNativeVectorSize(Operation *op, int64_t m, int64_t n, int64_t k)
Returns the target vector size for the target operation based on the native vector size specified wit...
static DiagnosedSilenceableFailure definiteFailureHelper(std::optional< TransformOpInterface > transformOp, Operation *target, const Twine &message)
static DiagnosedSilenceableFailure checkMappingSpec(std::optional< TransformOpInterface > transformOp, scf::ForallOp forallOp, ArrayRef< int64_t > numParallelIterations, ArrayRef< int64_t > blockOrGridSizes, int factor, bool useLinearMapping=false)
static void replaceUnitMappingIdsHelper(RewriterBase &rewriter, Location loc, OperationOrBlock *parent, Value replacement, ArrayRef< int64_t > availableMappingSizes)
Helper to replace ids of dimensions known to be 1 by 0 to simplify the IR.
static DiagnosedSilenceableFailure verifyGpuMapping(std::optional< TransformOpInterface > transformOp, scf::ForallOp forallOp)
static DiagnosedSilenceableFailure getThreadIdBuilder(std::optional< TransformOpInterface > transformOp, scf::ForallOp forallOp, ArrayRef< int64_t > blockSizes, int64_t warpSize, GpuIdBuilder &gpuIdBuilder)
static std::optional< SmallVector< int64_t > > gpuMmaUnrollOrder(vector::ContractionOp contract)
Pick an unrolling order that will allow tensorcore operation to reuse LHS register.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static void contract(RootOrderingGraph &graph, ArrayRef< Value > cycle, const DenseMap< Value, unsigned > &parentDepths, DenseMap< Value, Value > &actualSource, DenseMap< Value, Value > &actualTarget)
Contracts the specified cycle in the given graph in-place.
#define MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CLASS_NAME)
Definition TypeID.h:331
Base type for affine expression.
Definition AffineExpr.h:68
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:34
OpListType::iterator iterator
Definition Block.h:165
OpListType & getOperations()
Definition Block.h:162
Operation & front()
Definition Block.h:178
iterator begin()
Definition Block.h:168
The result of a transform IR operation application.
static DiagnosedSilenceableFailure success()
Constructs a DiagnosedSilenceableFailure in the success state.
bool succeeded() const
Returns true if this is a success.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
void addExtensions()
Add the given extensions to the registry.
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
auto lookup(T from) const
Lookup a mapped value within the map.
Definition IRMapping.h:72
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
Conversion from types to the LLVM IR dialect.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
Block::iterator getInsertionPoint() const
Returns the current insertion point of the builder.
Definition Builders.h:448
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:581
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:415
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
result_type_range getResultTypes()
Definition Operation.h:453
user_range getUsers()
Returns a range of all users.
Definition Operation.h:925
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:429
static FailureOr< TargetInfo > get(StringRef arch, unsigned waveSize=0, function_ref< InFlightDiagnostic()> emitError=nullptr)
Resolves a target description.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
A utility result that is used to signal how to proceed with an ongoing walk:
Definition WalkResult.h:29
static WalkResult skip()
Definition WalkResult.h:48
static WalkResult advance()
Definition WalkResult.h:47
bool wasInterrupted() const
Returns true if the walk was interrupted.
Definition WalkResult.h:51
static WalkResult interrupt()
Definition WalkResult.h:46
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:434
A list of results of applying a transform op with ApplyEachOpTrait to a single payload operation,...
void push_back(Operation *op)
Appends an element to the list.
Base class for extensions of the Transform dialect that supports injecting operations into the Transf...
This is a special rewriter to be used in transform op implementations, providing additional helper fu...
The state maintained across applications of various ops implementing the TransformOpInterface.
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
void populateCommonGPUTypeAndAttributeConversions(TypeConverter &typeConverter)
Remap common GPU memory spaces (Workgroup, Private, etc) to LLVM address spaces.
void registerTransformDialectExtension(DialectRegistry &registry)
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
uint64_t getN(LevelType lt)
Definition Enums.h:442
uint64_t getM(LevelType lt)
Definition Enums.h:443
DiagnosedSilenceableFailure findTopLevelForallOp(Operation *target, scf::ForallOp &topLevelForallOp, TransformOpInterface transformOp)
Find the unique top level scf::ForallOp within a given target op.
DiagnosedSilenceableFailure alterGpuLaunch(RewriterBase &rewriter, mlir::gpu::LaunchOp gpuLaunch, TransformOpInterface transformOp, std::optional< int64_t > gridDimX=std::nullopt, std::optional< int64_t > gridDimY=std::nullopt, std::optional< int64_t > gridDimZ=std::nullopt, std::optional< int64_t > blockDimX=std::nullopt, std::optional< int64_t > blockDimY=std::nullopt, std::optional< int64_t > blockDimZ=std::nullopt)
Alter kernel configuration of the given kernel.
DiagnosedSilenceableFailure createGpuLaunch(RewriterBase &rewriter, Location loc, TransformOpInterface transformOp, mlir::gpu::LaunchOp &launchOp, std::optional< int64_t > gridDimX=std::nullopt, std::optional< int64_t > gridDimY=std::nullopt, std::optional< int64_t > gridDimZ=std::nullopt, std::optional< int64_t > blockDimX=std::nullopt, std::optional< int64_t > blockDimY=std::nullopt, std::optional< int64_t > blockDimZ=std::nullopt)
Create an empty-body gpu::LaunchOp using the provided kernel settings and put a terminator within.
DiagnosedSilenceableFailure mapForallToBlocksImpl(RewriterBase &rewriter, TransformOpInterface transformOp, scf::ForallOp forallOp, SmallVectorImpl< int64_t > &gridDims, const GpuIdBuilder &gpuIdBuilder)
Map the top level scf.forall op to GPU blocks.
DiagnosedSilenceableFailure checkGpuLimits(TransformOpInterface transformOp, std::optional< int64_t > gridDimX, std::optional< int64_t > gridDimY, std::optional< int64_t > gridDimZ, std::optional< int64_t > blockDimX, std::optional< int64_t > blockDimY, std::optional< int64_t > blockDimZ)
Determine if the size of the kernel configuration is supported by the GPU architecture being used.
Definition Utils.cpp:362
DiagnosedSilenceableFailure mapNestedForallToThreadsImpl(RewriterBase &rewriter, std::optional< TransformOpInterface > transformOp, Operation *target, ArrayRef< int64_t > blockDims, int64_t warpSize, bool syncAfterDistribute)
Search scf.forall ops nested under target and map each such op to an explicit GPU implementation alon...
DiagnosedSilenceableFailure mapOneForallToThreadsImpl(RewriterBase &rewriter, std::optional< TransformOpInterface > transformOp, scf::ForallOp forallOp, ArrayRef< int64_t > blockSizes, int64_t warpSize, bool syncAfterDistribute)
Search scf.forall ops nested under target and map each such op to an explicit GPU implementation alon...
bool isReductionIterator(Attribute attr)
Returns true if attr has "reduction" iterator type semantics.
Definition VectorOps.h:156
bool isParallelIterator(Attribute attr)
Returns true if attr has "parallel" iterator type semantics.
Definition VectorOps.h:151
Include the generated interface declarations.
void populateGpuPromoteShuffleToAMDGPUPatterns(RewritePatternSet &patterns, std::optional< ROCDL::TargetInfo > target)
Tries to promote gpu.shuffles to specialized AMDGPU intrinsics.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:125
void populateGpuRewritePatterns(RewritePatternSet &patterns)
Collect all patterns to rewrite ops within the GPU dialect.
Definition Passes.h:75
void populateGpuToROCDLConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, gpu::amd::Runtime runtime, const ROCDL::TargetInfo &target)
Collect a set of patterns to convert from the GPU dialect to ROCDL.
int64_t computeProduct(ArrayRef< int64_t > basis)
Self-explicit.
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:128
DiagnosedDefiniteFailure emitDefiniteFailure(Location loc, const Twine &message={})
Emits a definite failure with the given message.
void configureGpuToNVVMTypeConverter(LLVMTypeConverter &converter)
Configure the LLVM type convert to convert types and address spaces from the GPU dialect to NVVM.
void populateGpuSubgroupReduceOpLoweringPattern(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit=1)
Populate GpuSubgroupReduce pattern to NVVM.
void populateGpuToNVVMConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of patterns to convert from the GPU dialect to NVVM.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:147
std::optional< SmallVector< int64_t > > getConstantIntValues(ArrayRef< OpFoldResult > ofrs)
If all ofrs are constant integers or IntegerAttrs, return the integers.
SmallVector< Value > getValuesSortedByKey(ArrayRef< Attribute > keys, ArrayRef< Value > values, llvm::function_ref< bool(Attribute, Attribute)> compare)
Helper to sort values according to matching keys.
void populateGpuEliminateBarriersPatterns(RewritePatternSet &patterns)
Erase barriers that do not enforce conflicting memory side effects.
void populateGpuWMMAToNVVMConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of patterns to convert WMMA ops from GPU dialect to NVVM.
Struct to return the result of the rewrite of a forall operation.
SmallVector< Value > mappingIds
SmallVector< int64_t > mappingSizes
Builder for gpu::BlockIdOps used to map scf.forall to blocks.
Definition Utils.h:80
Helper struct for configuring the rewrite of mapped scf.forall ops to various gpu id configurations.
Definition Utils.h:59
SmallVector< DeviceMappingAttrInterface > mappingAttributes
The mapping attributes targeted by this generator.
Definition Utils.h:68
GpuIdBuilderFnType idBuilder
The constructor that builds the concrete IR for mapping ids.
Definition Utils.h:71
Builder for warp ids used to map scf.forall to reindexed threads.
Definition Utils.h:119
Builder for warp ids used to map scf.forall to reindexed warps.
Definition Utils.h:106
Builder for warpgroup ids used to map scf.forall to reindexed warpgroups.
Definition Utils.h:91
static constexpr int64_t kNumWarpsPerGroup
In the future this may be configured by the transformation.
Definition Utils.h:97
Helper type for functions that generate ids for the mapping of a scf.forall.
Definition Utils.h:30
std::string errorMsg
Error message, if not empty then building the ids failed.
Definition Utils.h:32
SmallVector< Value > predicateOps
Values used to predicate the forall body when activeMappingSizes is smaller than the available mappin...
Definition Utils.h:37
SmallVector< Value > mappingIdOps
Values used to replace the forall induction variables.
Definition Utils.h:34
Options that control the vector unrolling.