58#include "llvm/ADT/STLExtras.h"
59#include "llvm/ADT/TypeSwitch.h"
60#include "llvm/Support/Debug.h"
61#include "llvm/Support/ErrorHandling.h"
65#define GEN_PASS_DEF_ACCRECIPEMATERIALIZATION
66#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
70#define DEBUG_TYPE "acc-recipe-materialization"
76static void saveVarName(StringRef name,
Value dst) {
82 if (isa<ACC_DATA_ENTRY_OPS>(dstOp))
85 acc::VarNameAttr::get(dstOp->getContext(), name));
88 auto blockArg = dyn_cast<BlockArgument>(dst);
91 Block *block = blockArg.getOwner();
98 auto funcOp = dyn_cast<FunctionOpInterface>(parent);
101 unsigned argIdx = blockArg.getArgNumber();
102 if (argIdx >= funcOp.getNumArguments())
107 acc::VarNameAttr::get(parent->
getContext(), name));
110static void saveVarName(
Value src,
Value dst) {
117template <
typename RecipeOpTy>
118static void cloneDestroy(RecipeOpTy recipe,
mlir::Block *block,
121 Region &destroyRegion = recipe.getDestroyRegion();
122 assert(destroyRegion.
getBlocks().front().getNumArguments() ==
124 "unexpected acc recipe destroy block arguments");
125 mapping.
map(destroyRegion.
getBlocks().front().getArguments(), arguments);
131class ACCRecipeMaterialization
132 :
public acc::impl::ACCRecipeMaterializationBase<ACCRecipeMaterialization> {
134 using acc::impl::ACCRecipeMaterializationBase<
135 ACCRecipeMaterialization>::ACCRecipeMaterializationBase;
136 void runOnOperation()
override;
152 void handleFirstprivateMapping(acc::FirstprivateOp firstprivateOp)
const;
153 template <
typename OpTy>
154 void removeRecipe(OpTy op, ModuleOp moduleOp)
const;
155 template <
typename OpTy,
typename RecipeOpTy,
typename AccOpTy>
156 LogicalResult materialize(OpTy op, RecipeOpTy recipe, AccOpTy accOp,
158 template <
typename OpTy>
159 LogicalResult materializeForACCOp(OpTy accOp,
163void ACCRecipeMaterialization::handleFirstprivateMapping(
164 acc::FirstprivateOp firstprivateOp)
const {
166 auto mapFirstprivateOp = acc::FirstprivateMapInitialOp::create(
167 builder, firstprivateOp.getLoc(), firstprivateOp.getVar(),
168 firstprivateOp.getStructured(), firstprivateOp.getImplicit(),
169 firstprivateOp.getBounds());
170 mapFirstprivateOp.setName(firstprivateOp.getName());
171 firstprivateOp.getVarMutable().assign(mapFirstprivateOp.getAccVar());
174template <
typename OpTy>
175void ACCRecipeMaterialization::removeRecipe(OpTy op, ModuleOp moduleOp)
const {
176 auto recipeName = op.getNameAttr();
178 LLVM_DEBUG(llvm::dbgs() <<
"erasing recipe: " << recipeName <<
"\n");
182 std::optional<SymbolTable::UseRange> symbolUses =
183 op.getSymbolUses(moduleOp);
184 if (symbolUses.has_value()) {
186 llvm::dbgs() <<
"symbol use: ";
187 symbolUse.getUser()->dump();
191 llvm_unreachable(
"expected no use of recipe symbol");
195template <
typename OpTy,
typename RecipeOpTy,
typename AccOpTy>
197ACCRecipeMaterialization::materialize(OpTy op, RecipeOpTy recipe, AccOpTy accOp,
199 Region ®ion = accOp.getRegion();
200 Value origPtr = op.getVar();
201 Value accPtr = op.getAccVar();
202 assert(accPtr &&
"invalid op: null acc var");
208 Region &initRegion = recipe.getInitRegion();
209 unsigned initNumArguments =
210 initRegion.
getBlocks().front().getArguments().size();
211 if (initNumArguments > 1) {
214 if ((initNumArguments - 1) % 3 != 0) {
216 "privatization of array section with extents");
227 initRegion.
getBlocks().front().getArgument(argIdx++).getType(),
231 auto dataBound = bound.getDefiningOp<acc::DataBoundsOp>();
233 "acc.reduction's bound must be defined by acc.bounds");
238 castValueToArgType(dataBound.getLoc(), dataBound.getLowerbound());
240 castValueToArgType(dataBound.getLoc(), dataBound.getUpperbound());
242 castValueToArgType(dataBound.getLoc(), dataBound.getStride());
243 triples.append({lb,
ub, step});
245 assert(triples.size() + 1 == initNumArguments &&
246 "mismatch between number bounds and number of recipe init block "
252 initArgs.append(triples);
253 mapping.
map(initRegion.
getBlocks().front().getArguments(), initArgs);
255 if constexpr (std::is_same_v<OpTy, acc::PrivateOp>) {
259 &initRegion, block, block->
begin(), mapping, {accPtr});
260 assert(results.size() == 1 &&
"expected single result from init region");
261 saveVarName(op.getAccVar(), results[0]);
263 if (!recipe.getDestroyRegion().empty()) {
264 results.insert(results.begin(), origPtr);
265 results.append(triples);
266 cloneDestroy(recipe, block, results);
268 }
else if constexpr (std::is_same_v<OpTy, acc::FirstprivateOp>) {
272 &initRegion, block, block->
begin(), mapping, {accPtr});
273 assert(results.size() == 1 &&
"expected single result from init region");
274 saveVarName(op.getAccVar(), results[0]);
276 results.insert(results.begin(), origPtr);
277 results.append(triples);
281 mapping.
map(recipe.getCopyRegion().front().getArguments(), results);
285 if (!recipe.getDestroyRegion().empty()) {
287 cloneDestroy(recipe, block, results);
289 }
else if constexpr (std::is_same_v<OpTy, acc::ReductionOp>) {
290 auto cloneRegionIntoAccRegion = [&](
Region *src,
Region *dest,
295 b.setInsertionPoint(terminator);
297 acc::YieldOp::create(
b, op.getLoc(), terminator->
getOperands());
299 acc::YieldOp::create(
b, op.getLoc(),
ValueRange{});
304 if constexpr (std::is_same_v<AccOpTy, acc::ParallelOp>)
305 b.setInsertionPointToStart(®ion.
front());
306 else if constexpr (std::is_same_v<AccOpTy, acc::LoopOp>)
307 b.setInsertionPoint(op);
309 llvm_unreachable(
"unexpected acc op with reduction recipe");
311 auto reductionOp = acc::ReductionInitOp::create(
312 b, op.getLoc(), origPtr, recipe.getReductionOperatorAttr());
313 saveVarName(op.getAccVar(), reductionOp.getResult());
314 cloneRegionIntoAccRegion(&initRegion, &reductionOp.getRegion(),
321 Region &combinerRegion = recipe.getCombinerRegion();
324 if constexpr (std::is_same_v<AccOpTy, acc::ParallelOp>)
326 else if constexpr (std::is_same_v<AccOpTy, acc::LoopOp>)
327 b.setInsertionPointAfter(accOp);
329 llvm_unreachable(
"unexpected acc op with reduction recipe");
337 argsRemapping.append(triples);
340 auto combineRegionOp = acc::ReductionCombineRegionOp::create(
341 b, op.getLoc(), origPtr, reductionOp.getResult());
342 cloneRegionIntoAccRegion(&combinerRegion, &combineRegionOp.getRegion(),
345 auto setSeqParDimsForRecipeLoops = [](
Region *r) {
346 r->walk([](LoopLikeOpInterface loopLike) {
348 acc::GPUParallelDimsAttr::name,
349 acc::GPUParallelDimsAttr::seq(loopLike->getContext()));
352 setSeqParDimsForRecipeLoops(&reductionOp.getRegion());
353 setSeqParDimsForRecipeLoops(&combineRegionOp.getRegion());
355 if (!recipe.getDestroyRegion().empty()) {
358 "OpenACC reduction variable that requires destruction code");
362 llvm_unreachable(
"unexpected op type");
369template <
typename OpTy>
370LogicalResult ACCRecipeMaterialization::materializeForACCOp(
372 assert(isa<ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS>(accOp));
374 if (!accOp.getFirstprivateOperands().empty()) {
378 accOp.getFirstprivateOperandsMutable().clear();
379 for (
Value operand : operands) {
380 auto firstprivateOp = cast<acc::FirstprivateOp>(operand.getDefiningOp());
381 auto symbolRef = cast<SymbolRefAttr>(firstprivateOp.getRecipeAttr());
383 auto recipeOp = cast<acc::FirstprivateRecipeOp>(decl);
384 LLVM_DEBUG(llvm::dbgs() <<
"materializing: " << firstprivateOp <<
"\n"
385 << symbolRef <<
"\n");
386 handleFirstprivateMapping(firstprivateOp);
387 if (failed(materialize(firstprivateOp, recipeOp, accOp, accSupport)))
392 if (!accOp.getPrivateOperands().empty()) {
396 accOp.getPrivateOperandsMutable().clear();
397 for (
Value operand : operands) {
398 auto privateOp = cast<acc::PrivateOp>(operand.getDefiningOp());
399 auto symbolRef = cast<SymbolRefAttr>(privateOp.getRecipeAttr());
401 auto recipeOp = cast<acc::PrivateRecipeOp>(decl);
402 LLVM_DEBUG(llvm::dbgs() <<
"materializing: " << privateOp <<
"\n"
403 << symbolRef <<
"\n");
404 if (failed(materialize(privateOp, recipeOp, accOp, accSupport)))
409 if (!accOp.getReductionOperands().empty()) {
413 accOp.getReductionOperandsMutable().clear();
414 for (
Value operand : operands) {
415 auto reductionOp = cast<acc::ReductionOp>(operand.getDefiningOp());
416 auto symbolRef = cast<SymbolRefAttr>(reductionOp.getRecipeAttr());
418 auto recipeOp = cast<acc::ReductionRecipeOp>(decl);
419 LLVM_DEBUG(llvm::dbgs() <<
"materializing: " << reductionOp <<
"\n"
420 << symbolRef <<
"\n");
421 if (failed(materialize(reductionOp, recipeOp, accOp, accSupport)))
428void ACCRecipeMaterialization::runOnOperation() {
429 ModuleOp moduleOp = getOperation();
433 bool anyFailed =
false;
438 [&](
auto constructOp) {
439 if (failed(materializeForACCOp(constructOp, accSupport)))
450 if (
auto recipe = dyn_cast<acc::ReductionRecipeOp>(op))
451 removeRecipe(recipe, moduleOp);
452 else if (
auto recipe = dyn_cast<acc::PrivateRecipeOp>(op))
453 removeRecipe(recipe, moduleOp);
454 else if (
auto recipe = dyn_cast<acc::FirstprivateRecipeOp>(op))
455 removeRecipe(recipe, moduleOp);
Block represents an ordered list of Operations.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgListType getArguments()
bool isEntryBlock()
Return if this block is the entry block in the parent region.
This is a utility class for mapping one set of IR entities to another.
void clear()
Clears all mappings held by the mapper.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
Operation is the basic unit of execution within MLIR.
operand_range getOperands()
Returns an iterator on the underlying Value's.
MLIRContext * getContext()
Return the context this operation is associated with.
void erase()
Remove this operation from its parent block and delete it.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
void cloneInto(Region *dest, IRMapping &mapper)
Clone the internal blocks from this region into dest.
Operation * getParentOp()
Return the parent operation this region is attached to.
BlockListType & getBlocks()
This class represents a specific symbol use.
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
static bool symbolKnownUseEmpty(StringAttr symbol, Operation *from)
Return if the given symbol is known to have no uses that are nested within the given operation 'from'...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
#define ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS
std::string getVariableName(mlir::Value v)
Attempts to extract the variable name from a value by walking through view-like operations until an a...
mlir::SmallVector< mlir::Value > getBounds(mlir::Operation *accDataClauseOp)
Used to obtain bounds from an acc data clause operation.
static constexpr StringLiteral getVarNameAttrName()
std::pair< llvm::SmallVector< Value >, Block::iterator > cloneACCRegionInto(Region *src, Block *dest, Block::iterator inlinePoint, IRMapping &mapping, ValueRange resultsToReplace)
Clone an ACC region into a destination block at the given insertion point.
Include the generated interface declarations.
Value convertScalarToDtype(OpBuilder &b, Location loc, Value operand, Type toType, bool isUnsignedCast)
Converts a scalar value operand to type toType.
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region ®ion)
Replace all uses of orig within the given region with replacement.
llvm::TypeSwitch< T, ResultT > TypeSwitch