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
161 :
public acc::impl::ACCRecipeMaterializationBase<ACCRecipeMaterialization> {
163 using acc::impl::ACCRecipeMaterializationBase<
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");
349 acc::ReductionInitOp::create(
b, op.getLoc(), origPtr, reductionBounds,
350 recipe.getReductionOperatorAttr());
351 saveVarName(op.getAccVar(), reductionOp.getResult());
352 cloneRegionIntoAccRegion(&initRegion, &reductionOp.getRegion(),
354 Block *initBlock = &reductionOp.getRegion().
front();
355 resolveVarNamePlaceholders(initBlock, std::prev(initBlock->
end()),
362 Region &combinerRegion = recipe.getCombinerRegion();
363 setLocation(combinerRegion, loc);
367 if constexpr (std::is_same_v<AccOpTy, acc::ParallelOp>)
369 else if constexpr (std::is_same_v<AccOpTy, acc::LoopOp>)
370 b.setInsertionPointAfter(accOp);
372 llvm_unreachable(
"unexpected acc op with reduction recipe");
380 argsRemapping.append(triples);
383 auto combineRegionOp = acc::ReductionCombineRegionOp::create(
384 b, op.getLoc(), origPtr, reductionOp.getResult());
385 cloneRegionIntoAccRegion(&combinerRegion, &combineRegionOp.getRegion(),
388 auto *ctx =
b.getContext();
394 if constexpr (std::is_same_v<AccOpTy, acc::ParallelOp>) {
395 acc::GPUParallelDimsAttr parDimsAttr;
396 if (accOp.isEffectivelySerial()) {
408 auto setSeqParDimsForRecipeLoops = [&](
Region *r) {
409 r->walk([&](LoopLikeOpInterface loopLike) {
413 setSeqParDimsForRecipeLoops(&reductionOp.getRegion());
414 setSeqParDimsForRecipeLoops(&combineRegionOp.getRegion());
416 if (!recipe.getDestroyRegion().empty()) {
418 results.append(triples);
420 cloneDestroy(loc, recipe, combineRegionOp->getBlock(), ip, results);
423 llvm_unreachable(
"unexpected op type");
430template <
typename OpTy>
431LogicalResult ACCRecipeMaterialization::materializeForACCOp(
434 assert(isa<ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS>(accOp));
436 if (!accOp.getFirstprivateOperands().empty()) {
440 accOp.getFirstprivateOperandsMutable().clear();
441 for (
Value operand : operands) {
442 auto firstprivateOp = cast<acc::FirstprivateOp>(operand.getDefiningOp());
443 auto symbolRef = cast<SymbolRefAttr>(firstprivateOp.getRecipeAttr());
445 auto recipeOp = cast<acc::FirstprivateRecipeOp>(decl);
446 LLVM_DEBUG(llvm::dbgs() <<
"materializing: " << firstprivateOp <<
"\n"
447 << symbolRef <<
"\n");
448 handleFirstprivateMapping(firstprivateOp);
450 materialize(firstprivateOp, recipeOp, accOp, accSupport, policy)))
455 if (!accOp.getPrivateOperands().empty()) {
459 accOp.getPrivateOperandsMutable().clear();
460 for (
Value operand : operands) {
461 auto privateOp = cast<acc::PrivateOp>(operand.getDefiningOp());
462 auto symbolRef = cast<SymbolRefAttr>(privateOp.getRecipeAttr());
464 auto recipeOp = cast<acc::PrivateRecipeOp>(decl);
465 LLVM_DEBUG(llvm::dbgs() <<
"materializing: " << privateOp <<
"\n"
466 << symbolRef <<
"\n");
467 if (failed(materialize(privateOp, recipeOp, accOp, accSupport, policy)))
472 if (!accOp.getReductionOperands().empty()) {
476 accOp.getReductionOperandsMutable().clear();
477 for (
Value operand : operands) {
478 auto reductionOp = cast<acc::ReductionOp>(operand.getDefiningOp());
479 auto symbolRef = cast<SymbolRefAttr>(reductionOp.getRecipeAttr());
481 auto recipeOp = cast<acc::ReductionRecipeOp>(decl);
482 LLVM_DEBUG(llvm::dbgs() <<
"materializing: " << reductionOp <<
"\n"
483 << symbolRef <<
"\n");
484 if (failed(materialize(reductionOp, recipeOp, accOp, accSupport, policy)))
491void ACCRecipeMaterialization::runOnOperation() {
492 ModuleOp moduleOp = getOperation();
498 bool anyFailed =
false;
503 [&](
auto constructOp) {
504 if (failed(materializeForACCOp(constructOp, accSupport, policy)))
515 if (
auto recipe = dyn_cast<acc::ReductionRecipeOp>(op))
516 removeRecipe(recipe, moduleOp);
517 else if (
auto recipe = dyn_cast<acc::PrivateRecipeOp>(op))
518 removeRecipe(recipe, moduleOp);
519 else if (
auto recipe = dyn_cast<acc::FirstprivateRecipeOp>(op))
520 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