MLIR 23.0.0git
OpenACCUtilsCG.cpp
Go to the documentation of this file.
1//===- OpenACCUtilsCG.cpp - OpenACC Code Generation Utilities -------------===//
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 utility functions for OpenACC code generation.
10//
11//===----------------------------------------------------------------------===//
12
16#include "mlir/IR/BuiltinOps.h"
17#include "mlir/IR/IRMapping.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/SmallVector.h"
20#include "llvm/ADT/TypeSwitch.h"
21
22namespace mlir {
23namespace acc {
24
25std::optional<DataLayout> getDataLayout(Operation *op, bool allowDefault) {
26 if (!op)
27 return std::nullopt;
28
29 // Walk up the parent chain to find the nearest operation with an explicit
30 // data layout spec. Check ModuleOp explicitly since it does not actually
31 // implement DataLayoutOpInterface as a trait (it just has the same methods).
32 Operation *current = op;
33 while (current) {
34 // Check for ModuleOp with explicit data layout spec
35 if (auto mod = llvm::dyn_cast<ModuleOp>(current)) {
36 if (mod.getDataLayoutSpec())
37 return DataLayout(mod);
38 } else if (auto dataLayoutOp =
39 llvm::dyn_cast<DataLayoutOpInterface>(current)) {
40 // Check other DataLayoutOpInterface implementations
41 if (dataLayoutOp.getDataLayoutSpec())
42 return DataLayout(dataLayoutOp);
43 }
44 current = current->getParentOp();
45 }
46
47 // No explicit data layout found; return default if allowed
48 if (allowDefault) {
49 // Check if op itself is a ModuleOp
50 if (auto mod = llvm::dyn_cast<ModuleOp>(op))
51 return DataLayout(mod);
52 // Otherwise check parents
53 if (auto mod = op->getParentOfType<ModuleOp>())
54 return DataLayout(mod);
55 }
56
57 return std::nullopt;
58}
59
60ComputeRegionOp buildComputeRegion(Location loc, ValueRange launchArgs,
61 ValueRange inputArgs, llvm::StringRef origin,
62 Region &regionToClone,
63 RewriterBase &rewriter, IRMapping &mapping,
64 ValueRange output,
65 FlatSymbolRefAttr kernelFuncName,
66 FlatSymbolRefAttr kernelModuleName,
67 Value stream, ValueRange inputArgsToMap) {
68 SmallVector<Type> resultTypes;
69 for (auto val : output)
70 resultTypes.push_back(val.getType());
71 auto computeRegion =
72 ComputeRegionOp::create(rewriter, loc, resultTypes, launchArgs, inputArgs,
73 stream, origin, kernelFuncName, kernelModuleName);
74
75 assert(!regionToClone.getBlocks().empty() &&
76 "empty region for acc.compute_region");
77 OpBuilder::InsertionGuard guard(rewriter);
78
79 ValueRange mapKeys = inputArgsToMap.empty() ? inputArgs : inputArgsToMap;
80 assert(mapKeys.size() == inputArgs.size() &&
81 "inputArgsToMap must have same size as inputArgs when provided");
82
83 Type indexType = rewriter.getIndexType();
84 Block *entryBlock = rewriter.createBlock(&computeRegion.getRegion());
85 for (size_t i = 0; i < launchArgs.size(); ++i)
86 entryBlock->addArgument(indexType, loc);
87 for (Value input : inputArgs)
88 entryBlock->addArgument(input.getType(), loc);
89 for (size_t i = 0; i < inputArgs.size(); ++i)
90 mapping.map(mapKeys[i], entryBlock->getArgument(launchArgs.size() + i));
91 rewriter.setInsertionPointToStart(entryBlock);
92 if (regionToClone.getBlocks().size() == 1) {
93 for (auto &op : regionToClone.front().getOperations()) {
94 if (op.hasTrait<OpTrait::IsTerminator>())
95 break;
96 rewriter.clone(op, mapping);
97 }
98 SmallVector<Value> yieldOperands;
99 for (auto val : output)
100 yieldOperands.push_back(mapping.lookup(val));
101 rewriter.setInsertionPointToEnd(entryBlock);
102 YieldOp::create(rewriter, loc, yieldOperands);
103 } else {
105 regionToClone, mapping, loc, rewriter);
106 if (!exeRegion) {
107 rewriter.eraseOp(computeRegion);
108 return nullptr;
109 }
111 llvm::to_vector(exeRegion.getOps<scf::YieldOp>()));
112 assert(!yieldOps.empty() &&
113 "multi-block region must contain at least one scf.yield");
114 assert(llvm::all_of(yieldOps,
115 [&output](scf::YieldOp yieldOp) {
116 return yieldOp.getNumOperands() ==
117 static_cast<int64_t>(output.size()) &&
118 llvm::all_of(
119 llvm::zip(yieldOp.getOperands(), output),
120 [](auto pair) {
121 return std::get<0>(pair).getType() ==
122 std::get<1>(pair).getType();
123 });
124 }) &&
125 "each scf.yield operand count and types must match output");
126 rewriter.setInsertionPointToEnd(entryBlock);
127 YieldOp::create(rewriter, loc, exeRegion.getResults());
128 }
129
130 return computeRegion;
131}
132
135 GPUParallelDimAttr parDim) {
136 return llvm::lower_bound(
137 parDims, parDim,
138 [](const GPUParallelDimAttr &lhs, const GPUParallelDimAttr &rhs) {
139 return lhs.getOrder() > rhs.getOrder();
140 });
141}
142
144 GPUParallelDimAttr parDim) {
146 if (lb == parDims.end() || *lb != parDim)
147 parDims.insert(lb, parDim);
148}
149
151 GPUParallelDimAttr parDim) {
153 if (lb != parDims.end() && *lb == parDim)
154 parDims.erase(lb);
155}
156
157#define ACC_OP_WITH_PAR_DIMS_LIST \
158 PrivatizeOp, ReductionAccumulateOp, ReductionAccumulateArrayOp
159
160GPUParallelDimsAttr getParDimsAttr(Operation *op) {
163 [](auto parOp) { return parOp.getParDimsAttr(); })
164 .Default([](Operation *op) -> GPUParallelDimsAttr {
165 if (Attribute attr = op->getAttr(GPUParallelDimsAttr::name)) {
166 GPUParallelDimsAttr parDimsAttr = dyn_cast<GPUParallelDimsAttr>(attr);
167 assert(parDimsAttr && "acc.par_dims must be a GPUParallelDimsAttr");
168 return parDimsAttr;
169 }
170 return nullptr;
171 });
172}
173
174bool hasParDimsAttr(Operation *op) { return getParDimsAttr(op) != nullptr; }
175
177 if (GPUParallelDimsAttr parDimsAttr = getParDimsAttr(op))
178 return parDimsAttr.isSeq();
179 return false;
180}
181
182void setParDimsAttr(Operation *op, GPUParallelDimsAttr attr) {
183 assert(!hasParDimsAttr(op) && "parallel dimensions attribute is already set");
186 [&](auto parOp) { parOp.setParDimsAttr(attr); })
187 .Default(
188 [&](Operation *op) { op->setAttr(GPUParallelDimsAttr::name, attr); });
189}
190
191void updateParDimsAttr(Operation *op, GPUParallelDimsAttr attr) {
192 assert(hasParDimsAttr(op) &&
193 "expected parallel dimensions attribute to already be set");
196 [&](auto parOp) { parOp.setParDimsAttr(attr); })
197 .Default(
198 [&](Operation *op) { op->setAttr(GPUParallelDimsAttr::name, attr); });
199}
200
201#undef ACC_OP_WITH_PAR_DIMS_LIST
202
204 assert(hasParDimsAttr(from) &&
205 "expected parallel dimensions attribute to already be set");
207}
208
209} // namespace acc
210} // namespace mlir
lhs
#define ACC_OP_WITH_PAR_DIMS_LIST
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:33
BlockArgument getArgument(unsigned i)
Definition Block.h:153
OpListType & getOperations()
Definition Block.h:161
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
IndexType getIndexType()
Definition Builders.cpp:55
The main mechanism for performing data layout queries.
A symbol reference with a reference path containing a single element.
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
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:350
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:435
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:567
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:433
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:438
This class provides the API for ops that are known to be terminators.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
Definition Operation.h:559
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Definition Operation.h:607
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
BlockListType & getBlocks()
Definition Region.h:45
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.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
GPUParallelDimsAttr getParDimsAttr(Operation *op)
Obtain the parallel dimensions carried by op, if any.
std::optional< DataLayout > getDataLayout(Operation *op, bool allowDefault=true)
Get the data layout for an operation.
void insertParDim(llvm::SmallVector< GPUParallelDimAttr > &parDims, GPUParallelDimAttr parDim)
Insert parDim into parDims while preserving dimension ordering.
bool hasParDimsAttr(Operation *op)
Return whether op carries parallel dimensions.
ComputeRegionOp buildComputeRegion(Location loc, ValueRange launchArgs, ValueRange inputArgs, llvm::StringRef origin, Region &regionToClone, RewriterBase &rewriter, IRMapping &mapping, ValueRange output={}, FlatSymbolRefAttr kernelFuncName={}, FlatSymbolRefAttr kernelModuleName={}, Value stream={}, ValueRange inputArgsToMap={})
Build an acc.compute_region operation by cloning a source region.
scf::ExecuteRegionOp wrapMultiBlockRegionWithSCFExecuteRegion(Region &region, IRMapping &mapping, Location loc, RewriterBase &rewriter)
Wrap a multi-block region in an scf.execute_region.
void updateParDimsAttr(Operation *op, GPUParallelDimsAttr attr)
Update parallel dimensions on op.
bool hasSeqParDims(Operation *op)
Return whether op carries sequential parallel dimensions.
void copyParDimsAttr(Operation *from, Operation *to)
Copy parallel dimensions from from to to.
void removeParDim(llvm::SmallVector< GPUParallelDimAttr > &parDims, GPUParallelDimAttr parDim)
Remove parDim from parDims if present.
void setParDimsAttr(Operation *op, GPUParallelDimsAttr attr)
Set parallel dimensions on op.
static SmallVector< GPUParallelDimAttr >::iterator findParDim(SmallVector< GPUParallelDimAttr > &parDims, GPUParallelDimAttr parDim)
Include the generated interface declarations.