60#include "llvm/ADT/STLExtras.h"
61#include "llvm/ADT/TypeSwitch.h"
62#include "llvm/Support/Debug.h"
63#include "llvm/Support/ErrorHandling.h"
67#define GEN_PASS_DEF_ACCRECIPEMATERIALIZATION
68#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
72#define DEBUG_TYPE "acc-recipe-materialization"
87static void saveVarName(StringRef name,
Value dst) {
93 if (isa<ACC_DATA_ENTRY_OPS>(dstOp))
96 acc::VarNameAttr::get(dstOp->getContext(), name));
99 auto blockArg = dyn_cast<BlockArgument>(dst);
102 Block *block = blockArg.getOwner();
109 auto funcOp = dyn_cast<FunctionOpInterface>(parent);
112 unsigned argIdx = blockArg.getArgNumber();
113 if (argIdx >= funcOp.getNumArguments())
118 acc::VarNameAttr::get(parent->
getContext(), name));
121static void saveVarName(
Value src,
Value dst) {
128 for (
auto it = block->
begin(); it != std::next(ip); ++it) {
130 if (attr && attr.getName() == placeholder) {
135 acc::VarNameAttr::get(it->getContext(), name));
143template <
typename RecipeOpTy>
148 Region &destroyRegion = recipe.getDestroyRegion();
149 assert(destroyRegion.
getBlocks().front().getNumArguments() ==
151 "unexpected acc recipe destroy block arguments");
153 setLocation(destroyRegion, loc);
155 mapping.
map(destroyRegion.
getBlocks().front().getArguments(), arguments);
160class ACCRecipeMaterialization
164 ACCRecipeMaterialization>::ACCRecipeMaterializationBase;
165 void runOnOperation()
override;
181 void handleFirstprivateMapping(acc::FirstprivateOp firstprivateOp)
const;
182 template <
typename OpTy>
183 void removeRecipe(OpTy op, ModuleOp moduleOp)
const;
184 template <
typename OpTy,
typename RecipeOpTy,
typename AccOpTy>
185 LogicalResult materialize(OpTy op, RecipeOpTy recipe, AccOpTy accOp,
188 template <
typename OpTy>
193void ACCRecipeMaterialization::handleFirstprivateMapping(
194 acc::FirstprivateOp firstprivateOp)
const {
196 auto mapFirstprivateOp = acc::FirstprivateMapInitialOp::create(
197 builder, firstprivateOp.getLoc(), firstprivateOp.getVar(),
198 firstprivateOp.getStructured(), firstprivateOp.getImplicit(),
199 firstprivateOp.getBounds());
200 mapFirstprivateOp.setName(firstprivateOp.getName());
201 firstprivateOp.getVarMutable().assign(mapFirstprivateOp.getAccVar());
204template <
typename OpTy>
205void ACCRecipeMaterialization::removeRecipe(OpTy op, ModuleOp moduleOp)
const {
206 auto recipeName = op.getNameAttr();
208 LLVM_DEBUG(llvm::dbgs() <<
"erasing recipe: " << recipeName <<
"\n");
212 std::optional<SymbolTable::UseRange> symbolUses =
213 op.getSymbolUses(moduleOp);
214 if (symbolUses.has_value()) {
216 llvm::dbgs() <<
"symbol use: ";
217 symbolUse.getUser()->dump();
221 llvm_unreachable(
"expected no use of recipe symbol");
225template <
typename OpTy,
typename RecipeOpTy,
typename AccOpTy>
226LogicalResult ACCRecipeMaterialization::materialize(
229 Region ®ion = accOp.getRegion();
230 Value origPtr = op.getVar();
231 Value accPtr = op.getAccVar();
232 assert(accPtr &&
"invalid op: null acc var");
238 Region &initRegion = recipe.getInitRegion();
239 unsigned initNumArguments =
240 initRegion.
getBlocks().front().getArguments().size();
241 if (initNumArguments > 1) {
244 if ((initNumArguments - 1) % 3 != 0) {
246 "privatization of array section with extents");
257 initRegion.
getBlocks().front().getArgument(argIdx++).getType(),
261 auto dataBound = bound.getDefiningOp<acc::DataBoundsOp>();
263 "acc.reduction's bound must be defined by acc.bounds");
268 castValueToArgType(dataBound.getLoc(), dataBound.getLowerbound());
270 castValueToArgType(dataBound.getLoc(), dataBound.getUpperbound());
272 castValueToArgType(dataBound.getLoc(), dataBound.getStride());
273 triples.append({lb,
ub, step});
275 assert(triples.size() + 1 == initNumArguments &&
276 "mismatch between number bounds and number of recipe init block "
282 initArgs.append(triples);
283 mapping.
map(initRegion.
getBlocks().front().getArguments(), initArgs);
286 setLocation(initRegion, loc);
288 if constexpr (std::is_same_v<OpTy, acc::PrivateOp>) {
292 &initRegion, block, block->
begin(), mapping, {accPtr});
293 assert(results.size() == 1 &&
"expected single result from init region");
294 saveVarName(op.getAccVar(), results[0]);
297 if (!recipe.getDestroyRegion().empty()) {
298 results.insert(results.begin(), origPtr);
299 results.append(triples);
300 cloneDestroy(loc, recipe, block, std::prev(block->
end()), results);
302 }
else if constexpr (std::is_same_v<OpTy, acc::FirstprivateOp>) {
306 &initRegion, block, block->
begin(), mapping, {accPtr});
307 assert(results.size() == 1 &&
"expected single result from init region");
308 saveVarName(op.getAccVar(), results[0]);
311 results.insert(results.begin(), origPtr);
312 results.append(triples);
316 mapping.
map(recipe.getCopyRegion().front().getArguments(), results);
318 Region ©Region = recipe.getCopyRegion();
319 setLocation(copyRegion, loc);
321 if (!recipe.getDestroyRegion().empty()) {
323 cloneDestroy(loc, recipe, block, std::prev(block->
end()), results);
325 }
else if constexpr (std::is_same_v<OpTy, acc::ReductionOp>) {
326 auto cloneRegionIntoAccRegion = [&](
Region *src,
Region *dest,
331 b.setInsertionPoint(terminator);
333 acc::YieldOp::create(
b, op.getLoc(), terminator->
getOperands());
335 acc::YieldOp::create(
b, op.getLoc(),
ValueRange{});
340 if constexpr (std::is_same_v<AccOpTy, acc::ParallelOp>)
341 b.setInsertionPointToStart(®ion.
front());
342 else if constexpr (std::is_same_v<AccOpTy, acc::LoopOp>)
343 b.setInsertionPoint(op);
345 llvm_unreachable(
"unexpected acc op with reduction recipe");
347 auto reductionOp = acc::ReductionInitOp::create(
348 b, op.getLoc(), origPtr, recipe.getReductionOperatorAttr());
349 saveVarName(op.getAccVar(), reductionOp.getResult());
350 cloneRegionIntoAccRegion(&initRegion, &reductionOp.getRegion(),
352 Block *initBlock = &reductionOp.getRegion().
front();
353 resolveVarNamePlaceholders(initBlock, std::prev(initBlock->
end()),
360 Region &combinerRegion = recipe.getCombinerRegion();
361 setLocation(combinerRegion, loc);
365 if constexpr (std::is_same_v<AccOpTy, acc::ParallelOp>)
367 else if constexpr (std::is_same_v<AccOpTy, acc::LoopOp>)
368 b.setInsertionPointAfter(accOp);
370 llvm_unreachable(
"unexpected acc op with reduction recipe");
378 argsRemapping.append(triples);
381 auto combineRegionOp = acc::ReductionCombineRegionOp::create(
382 b, op.getLoc(), origPtr, reductionOp.getResult());
383 cloneRegionIntoAccRegion(&combinerRegion, &combineRegionOp.getRegion(),
386 auto *ctx =
b.getContext();
392 if constexpr (std::is_same_v<AccOpTy, acc::ParallelOp>) {
393 acc::GPUParallelDimsAttr parDimsAttr;
394 if (accOp.isEffectivelySerial()) {
406 auto setSeqParDimsForRecipeLoops = [&](
Region *r) {
407 r->walk([&](LoopLikeOpInterface loopLike) {
411 setSeqParDimsForRecipeLoops(&reductionOp.getRegion());
412 setSeqParDimsForRecipeLoops(&combineRegionOp.getRegion());
414 if (!recipe.getDestroyRegion().empty()) {
416 results.append(triples);
418 cloneDestroy(loc, recipe, combineRegionOp->getBlock(), ip, results);
421 llvm_unreachable(
"unexpected op type");
428template <
typename OpTy>
429LogicalResult ACCRecipeMaterialization::materializeForACCOp(
432 assert(isa<ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS>(accOp));
434 if (!accOp.getFirstprivateOperands().empty()) {
438 accOp.getFirstprivateOperandsMutable().clear();
439 for (
Value operand : operands) {
440 auto firstprivateOp = cast<acc::FirstprivateOp>(operand.getDefiningOp());
441 auto symbolRef = cast<SymbolRefAttr>(firstprivateOp.getRecipeAttr());
443 auto recipeOp = cast<acc::FirstprivateRecipeOp>(decl);
444 LLVM_DEBUG(llvm::dbgs() <<
"materializing: " << firstprivateOp <<
"\n"
445 << symbolRef <<
"\n");
446 handleFirstprivateMapping(firstprivateOp);
448 materialize(firstprivateOp, recipeOp, accOp, accSupport, policy)))
453 if (!accOp.getPrivateOperands().empty()) {
457 accOp.getPrivateOperandsMutable().clear();
458 for (
Value operand : operands) {
459 auto privateOp = cast<acc::PrivateOp>(operand.getDefiningOp());
460 auto symbolRef = cast<SymbolRefAttr>(privateOp.getRecipeAttr());
462 auto recipeOp = cast<acc::PrivateRecipeOp>(decl);
463 LLVM_DEBUG(llvm::dbgs() <<
"materializing: " << privateOp <<
"\n"
464 << symbolRef <<
"\n");
465 if (failed(materialize(privateOp, recipeOp, accOp, accSupport, policy)))
470 if (!accOp.getReductionOperands().empty()) {
474 accOp.getReductionOperandsMutable().clear();
475 for (
Value operand : operands) {
476 auto reductionOp = cast<acc::ReductionOp>(operand.getDefiningOp());
477 auto symbolRef = cast<SymbolRefAttr>(reductionOp.getRecipeAttr());
479 auto recipeOp = cast<acc::ReductionRecipeOp>(decl);
480 LLVM_DEBUG(llvm::dbgs() <<
"materializing: " << reductionOp <<
"\n"
481 << symbolRef <<
"\n");
482 if (failed(materialize(reductionOp, recipeOp, accOp, accSupport, policy)))
489void ACCRecipeMaterialization::runOnOperation() {
490 ModuleOp moduleOp = getOperation();
496 bool anyFailed =
false;
501 [&](
auto constructOp) {
502 if (failed(materializeForACCOp(constructOp, accSupport, policy)))
513 if (
auto recipe = dyn_cast<acc::ReductionRecipeOp>(op))
514 removeRecipe(recipe, moduleOp);
515 else if (
auto recipe = dyn_cast<acc::PrivateRecipeOp>(op))
516 removeRecipe(recipe, moduleOp);
517 else if (
auto recipe = dyn_cast<acc::FirstprivateRecipeOp>(op))
518 removeRecipe(recipe, moduleOp);
Block represents an ordered list of Operations.
OpListType::iterator iterator
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.
void setLoc(Location loc)
Set the source location the operation was defined or derived from.
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()
RetT walk(FnT &&callback)
Walk all nested operations, blocks or regions (including this region), depending on the type of callb...
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.
Default policy that provides the standard GPU mapping: gang(dim:1) -> BlockX (gridDim....
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...
GPUParallelDimsAttr getGangDim1ParDimsAttr(MLIRContext *ctx, ACCToGPUMappingPolicy &policy)
Create a gang dim 1 GPUParallelDimsAttr based on the mapping policy.
mlir::SmallVector< mlir::Value > getBounds(mlir::Operation *accDataClauseOp)
Used to obtain bounds from an acc data clause operation.
GPUParallelDimsAttr getSeqParDimsAttr(MLIRContext *ctx, ACCToGPUMappingPolicy &policy)
Create a sequential GPUParallelDimsAttr based on the mapping policy.
llvm::StringLiteral getVarNamePlaceholder()
Returns a placeholder string for use as an acc.var_name attribute value when the actual variable name...
static constexpr StringLiteral getVarNameAttrName()
void setParDimsAttr(Operation *op, GPUParallelDimsAttr attr)
Set parallel dimensions on op.
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.
ACCParMappingPolicy< mlir::acc::GPUParallelDimAttr > ACCToGPUMappingPolicy
Type alias for the GPU-specific mapping policy.
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