MLIR 24.0.0git
ParallelLoopMapper.cpp
Go to the documentation of this file.
1//===- ParallelLoopMapper.cpp - Utilities for mapping parallel loops to 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//
9// This file implements utilities to generate mappings for parallel loops to
10// GPU devices.
11//
12//===----------------------------------------------------------------------===//
13
15
20#include "mlir/IR/AffineMap.h"
21
22namespace mlir {
23#define GEN_PASS_DEF_GPUMAPPARALLELLOOPSPASS
24#include "mlir/Dialect/GPU/Transforms/Passes.h.inc"
25} // namespace mlir
26
27namespace mlir {
28
29using scf::ParallelOp;
30
31StringRef gpu::getMappingAttrName() { return "mapping"; }
32
33LogicalResult
34gpu::setMappingAttr(ParallelOp ploopOp,
36 // Verify that each processor is mapped to only once.
37 llvm::DenseSet<gpu::Processor> specifiedMappings;
38 for (auto dimAttr : mapping) {
39 gpu::Processor processor = dimAttr.getProcessor();
40 if (processor != gpu::Processor::Sequential &&
41 specifiedMappings.count(processor))
42 return ploopOp.emitError(
43 "invalid mapping multiple loops to same processor");
44 specifiedMappings.insert(processor);
45 }
46 ArrayRef<Attribute> mappingAsAttrs(mapping.data(), mapping.size());
47 ploopOp->setDiscardableAttr(
48 getMappingAttrName(),
49 ArrayAttr::get(ploopOp.getContext(), mappingAsAttrs));
50 return success();
51}
52
53namespace gpu {
54namespace {
55enum MappingLevel { MapGrid = 0, MapBlock = 1, Sequential = 2 };
56enum class MappingPolicy { OutermostFirst, InnermostFirst };
57} // namespace
58
59static constexpr int kNumHardwareIds = 3;
60
61/// Bounded increment on MappingLevel. Increments to the next
62/// level unless Sequential was already reached.
63static MappingLevel &operator++(MappingLevel &mappingLevel) {
64 if (mappingLevel < Sequential) {
65 mappingLevel = static_cast<MappingLevel>(mappingLevel + 1);
66 }
67 return mappingLevel;
68}
69
70// Map the policy string to a typed mapping policy.
71// TODO: Revisit this and possibly use a loop interchange pass instead.
72static FailureOr<MappingPolicy> getMappingPolicyFromStr(StringRef policy) {
73 std::string policyCanonical = policy.trim().lower();
74
75 std::optional<MappingPolicy> option =
77 .Case("innermost-first", MappingPolicy::InnermostFirst)
78 .Case("outermost-first", MappingPolicy::OutermostFirst)
79 .Default(std::nullopt);
80
81 if (!option)
82 return failure();
83 return *option;
84}
85
86/// Computed the hardware id to use for a given mapping level. Will
87/// assign x,y and z hardware ids for the first 3 dimensions and use
88/// sequential after.
89static Processor getHardwareIdForMapping(MappingLevel level, int dimension) {
90
91 if (dimension >= kNumHardwareIds || level == Sequential)
92 return Processor::Sequential;
93
94 switch (level) {
95 case MapGrid:
96 switch (dimension) {
97 case 0:
98 return Processor::BlockX;
99 case 1:
100 return Processor::BlockY;
101 case 2:
102 return Processor::BlockZ;
103 default:
104 return Processor::Sequential;
105 }
106 break;
107 case MapBlock:
108 switch (dimension) {
109 case 0:
110 return Processor::ThreadX;
111 case 1:
112 return Processor::ThreadY;
113 case 2:
114 return Processor::ThreadZ;
115 default:
116 return Processor::Sequential;
117 }
118 default:;
119 }
120 return Processor::Sequential;
121}
122
123/// Add mapping information to the given parallel loop. Do not add
124/// mapping information if the loop already has it. Also, don't
125/// start a mapping at a nested loop.
126static void
127mapParallelOp(ParallelOp parallelOp, MappingLevel mappingLevel = MapGrid,
128 MappingPolicy mappingPolicy = MappingPolicy::OutermostFirst) {
129 // Do not try to add a mapping to already mapped loops or nested loops.
130 if (parallelOp->getDiscardableAttr(getMappingAttrName()) ||
131 ((mappingLevel == MapGrid) && parallelOp->getParentOfType<ParallelOp>()))
132 return;
133
134 const int numLoops = static_cast<int>(parallelOp.getNumLoops());
135 const int loopsToMap = std::min(numLoops, kNumHardwareIds);
136
137 MLIRContext *ctx = parallelOp.getContext();
138 Builder b(ctx);
140 attrs.reserve(numLoops);
141
142 for (int i = 0; i < numLoops; ++i) {
143
144 // Determine the mapping to use for this loop.
145 // If the are more loops to map than HW IDs map to sequential.
146 int hwMapping = kNumHardwareIds;
147 if (i < loopsToMap) {
148 hwMapping = (mappingPolicy == MappingPolicy::OutermostFirst)
149 ? i
150 : (loopsToMap - 1 - i);
151 }
152
153 attrs.push_back(b.getAttr<ParallelLoopDimMappingAttr>(
154 getHardwareIdForMapping(mappingLevel, hwMapping), b.getDimIdentityMap(),
155 b.getDimIdentityMap()));
156 }
157 (void)setMappingAttr(parallelOp, attrs);
158 ++mappingLevel;
159 // Parallel loop operations are immediately nested, so do not use
160 // walk but just iterate over the operations.
161 for (Operation &op : *parallelOp.getBody()) {
162 if (ParallelOp nested = dyn_cast<ParallelOp>(op))
163 mapParallelOp(nested, mappingLevel, mappingPolicy);
164 }
165}
166
167namespace {
168struct GpuMapParallelLoopsPass
169 : public impl::GpuMapParallelLoopsPassBase<GpuMapParallelLoopsPass> {
170 using Base::Base;
171
172 void runOnOperation() override {
173 // Parse the mapping policy.
174 FailureOr<MappingPolicy> policyOrFailure =
175 getMappingPolicyFromStr(mappingPolicyStr);
176 if (failed(policyOrFailure)) {
177 getOperation()->emitError() << "Invalid mapping policy specified.";
178 return signalPassFailure();
179 }
180
181 MappingPolicy policy = *policyOrFailure;
182 MappingLevel topLevel = MappingLevel::MapGrid;
183
184 for (Region &region : getOperation()->getRegions()) {
185 region.walk([&](ParallelOp parallelOp) {
186 mapParallelOp(parallelOp, topLevel, policy);
187 });
188 }
189 }
190};
191
192} // namespace
193} // namespace gpu
194} // namespace mlir
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
static Processor getHardwareIdForMapping(MappingLevel level, int dimension)
Computed the hardware id to use for a given mapping level.
static FailureOr< MappingPolicy > getMappingPolicyFromStr(StringRef policy)
static MappingLevel & operator++(MappingLevel &mappingLevel)
Bounded increment on MappingLevel.
LogicalResult setMappingAttr(scf::ParallelOp ploopOp, ArrayRef< ParallelLoopDimMappingAttr > mapping)
Sets the mapping attribute of a scf.parallel operation.
static constexpr int kNumHardwareIds
StringRef getMappingAttrName()
Name of the mapping attribute produced by loop mappers.
static void mapParallelOp(ParallelOp parallelOp, MappingLevel mappingLevel=MapGrid, MappingPolicy mappingPolicy=MappingPolicy::OutermostFirst)
Add mapping information to the given parallel loop.
Include the generated interface declarations.