diff options
Diffstat (limited to 'mlir/lib/Dialect/OpenACC')
-rw-r--r-- | mlir/lib/Dialect/OpenACC/CMakeLists.txt | 1 | ||||
-rw-r--r-- | mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp | 166 | ||||
-rw-r--r-- | mlir/lib/Dialect/OpenACC/Utils/CMakeLists.txt | 20 | ||||
-rw-r--r-- | mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp | 80 |
4 files changed, 171 insertions, 96 deletions
diff --git a/mlir/lib/Dialect/OpenACC/CMakeLists.txt b/mlir/lib/Dialect/OpenACC/CMakeLists.txt index 9f57627..7117520 100644 --- a/mlir/lib/Dialect/OpenACC/CMakeLists.txt +++ b/mlir/lib/Dialect/OpenACC/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(IR) +add_subdirectory(Utils) add_subdirectory(Transforms) diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index 90cbbd8..5ca0100 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -1030,12 +1030,12 @@ struct RemoveConstantIfConditionWithRegion : public OpRewritePattern<OpTy> { //===----------------------------------------------------------------------===// /// Create and populate an init region for privatization recipes. -/// Returns the init block on success, or nullptr on failure. +/// Returns success if the region is populated, failure otherwise. /// Sets needsFree to indicate if the allocated memory requires deallocation. -static std::unique_ptr<Block> createInitRegion(OpBuilder &builder, Location loc, - Type varType, StringRef varName, - ValueRange bounds, - bool &needsFree) { +static LogicalResult createInitRegion(OpBuilder &builder, Location loc, + Region &initRegion, Type varType, + StringRef varName, ValueRange bounds, + bool &needsFree) { // Create init block with arguments: original value + bounds SmallVector<Type> argTypes{varType}; SmallVector<Location> argLocs{loc}; @@ -1044,9 +1044,9 @@ static std::unique_ptr<Block> createInitRegion(OpBuilder &builder, Location loc, argLocs.push_back(loc); } - auto initBlock = std::make_unique<Block>(); + Block *initBlock = builder.createBlock(&initRegion); initBlock->addArguments(argTypes, argLocs); - builder.setInsertionPointToStart(initBlock.get()); + builder.setInsertionPointToStart(initBlock); Value privatizedValue; @@ -1060,7 +1060,7 @@ static std::unique_ptr<Block> createInitRegion(OpBuilder &builder, Location loc, privatizedValue = mappableTy.generatePrivateInit( builder, loc, typedVar, varName, bounds, {}, needsFree); if (!privatizedValue) - return nullptr; + return failure(); } else { assert(isa<PointerLikeType>(varType) && "Expected PointerLikeType"); auto pointerLikeTy = cast<PointerLikeType>(varType); @@ -1068,21 +1068,21 @@ static std::unique_ptr<Block> createInitRegion(OpBuilder &builder, Location loc, privatizedValue = pointerLikeTy.genAllocate(builder, loc, varName, varType, blockArgVar, needsFree); if (!privatizedValue) - return nullptr; + return failure(); } // Add yield operation to init block acc::YieldOp::create(builder, loc, privatizedValue); - return initBlock; + return success(); } /// Create and populate a copy region for firstprivate recipes. -/// Returns the copy block on success, or nullptr on failure. +/// Returns success if the region is populated, failure otherwise. /// TODO: Handle MappableType - it does not yet have a copy API. -static std::unique_ptr<Block> createCopyRegion(OpBuilder &builder, Location loc, - Type varType, - ValueRange bounds) { +static LogicalResult createCopyRegion(OpBuilder &builder, Location loc, + Region ©Region, Type varType, + ValueRange bounds) { // Create copy block with arguments: original value + privatized value + // bounds SmallVector<Type> copyArgTypes{varType, varType}; @@ -1092,16 +1092,16 @@ static std::unique_ptr<Block> createCopyRegion(OpBuilder &builder, Location loc, copyArgLocs.push_back(loc); } - auto copyBlock = std::make_unique<Block>(); + Block *copyBlock = builder.createBlock(©Region); copyBlock->addArguments(copyArgTypes, copyArgLocs); - builder.setInsertionPointToStart(copyBlock.get()); + builder.setInsertionPointToStart(copyBlock); bool isMappable = isa<MappableType>(varType); bool isPointerLike = isa<PointerLikeType>(varType); // TODO: Handle MappableType - it does not yet have a copy API. // Otherwise, for now just fallback to pointer-like behavior. if (isMappable && !isPointerLike) - return nullptr; + return failure(); // Generate copy region body based on variable type if (isPointerLike) { @@ -1113,21 +1113,20 @@ static std::unique_ptr<Block> createCopyRegion(OpBuilder &builder, Location loc, if (!pointerLikeTy.genCopy( builder, loc, cast<TypedValue<PointerLikeType>>(privatizedArg), cast<TypedValue<PointerLikeType>>(originalArg), varType)) - return nullptr; + return failure(); } // Add terminator to copy block acc::TerminatorOp::create(builder, loc); - return copyBlock; + return success(); } /// Create and populate a destroy region for privatization recipes. -/// Returns the destroy block on success, or nullptr if not needed. -static std::unique_ptr<Block> createDestroyRegion(OpBuilder &builder, - Location loc, Type varType, - Value allocRes, - ValueRange bounds) { +/// Returns success if the region is populated, failure otherwise. +static LogicalResult createDestroyRegion(OpBuilder &builder, Location loc, + Region &destroyRegion, Type varType, + Value allocRes, ValueRange bounds) { // Create destroy block with arguments: original value + privatized value + // bounds SmallVector<Type> destroyArgTypes{varType, varType}; @@ -1137,28 +1136,25 @@ static std::unique_ptr<Block> createDestroyRegion(OpBuilder &builder, destroyArgLocs.push_back(loc); } - auto destroyBlock = std::make_unique<Block>(); + Block *destroyBlock = builder.createBlock(&destroyRegion); destroyBlock->addArguments(destroyArgTypes, destroyArgLocs); - builder.setInsertionPointToStart(destroyBlock.get()); + builder.setInsertionPointToStart(destroyBlock); - bool isMappable = isa<MappableType>(varType); - bool isPointerLike = isa<PointerLikeType>(varType); - // TODO: Handle MappableType - it does not yet have a deallocation API. - // Otherwise, for now just fallback to pointer-like behavior. - if (isMappable && !isPointerLike) - return nullptr; - - assert(isa<PointerLikeType>(varType) && "Expected PointerLikeType"); - auto pointerLikeTy = cast<PointerLikeType>(varType); - auto privatizedArg = + auto varToFree = cast<TypedValue<PointerLikeType>>(destroyBlock->getArgument(1)); - // Pass allocRes to help determine the allocation type - if (!pointerLikeTy.genFree(builder, loc, privatizedArg, allocRes, varType)) - return nullptr; + if (isa<MappableType>(varType)) { + auto mappableTy = cast<MappableType>(varType); + if (!mappableTy.generatePrivateDestroy(builder, loc, varToFree)) + return failure(); + } else { + assert(isa<PointerLikeType>(varType) && "Expected PointerLikeType"); + auto pointerLikeTy = cast<PointerLikeType>(varType); + if (!pointerLikeTy.genFree(builder, loc, varToFree, allocRes, varType)) + return failure(); + } acc::TerminatorOp::create(builder, loc); - - return destroyBlock; + return success(); } } // namespace @@ -1220,40 +1216,33 @@ PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc, if (!isMappable && !isPointerLike) return std::nullopt; - // Create init and destroy blocks using shared helpers OpBuilder::InsertionGuard guard(builder); - // Save the original insertion point for creating the recipe operation later - auto originalInsertionPoint = builder.saveInsertionPoint(); + // Create the recipe operation first so regions have proper parent context + auto recipe = PrivateRecipeOp::create(builder, loc, recipeName, varType); + // Populate the init region bool needsFree = false; - auto initBlock = - createInitRegion(builder, loc, varType, varName, bounds, needsFree); - if (!initBlock) + if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), varType, + varName, bounds, needsFree))) { + recipe.erase(); return std::nullopt; + } // Only create destroy region if the allocation needs deallocation - std::unique_ptr<Block> destroyBlock; if (needsFree) { // Extract the allocated value from the init block's yield operation - auto yieldOp = cast<acc::YieldOp>(initBlock->getTerminator()); + auto yieldOp = + cast<acc::YieldOp>(recipe.getInitRegion().front().getTerminator()); Value allocRes = yieldOp.getOperand(0); - destroyBlock = createDestroyRegion(builder, loc, varType, allocRes, bounds); - if (!destroyBlock) + if (failed(createDestroyRegion(builder, loc, recipe.getDestroyRegion(), + varType, allocRes, bounds))) { + recipe.erase(); return std::nullopt; + } } - // Now create the recipe operation at the original insertion point and attach - // the blocks - builder.restoreInsertionPoint(originalInsertionPoint); - auto recipe = PrivateRecipeOp::create(builder, loc, recipeName, varType); - - // Move the blocks into the recipe's regions - recipe.getInitRegion().push_back(initBlock.release()); - if (destroyBlock) - recipe.getDestroyRegion().push_back(destroyBlock.release()); - return recipe; } @@ -1299,45 +1288,40 @@ FirstprivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc, if (!isMappable && !isPointerLike) return std::nullopt; - // Create init, copy, and destroy blocks using shared helpers OpBuilder::InsertionGuard guard(builder); - // Save the original insertion point for creating the recipe operation later - auto originalInsertionPoint = builder.saveInsertionPoint(); + // Create the recipe operation first so regions have proper parent context + auto recipe = FirstprivateRecipeOp::create(builder, loc, recipeName, varType); + // Populate the init region bool needsFree = false; - auto initBlock = - createInitRegion(builder, loc, varType, varName, bounds, needsFree); - if (!initBlock) + if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), varType, + varName, bounds, needsFree))) { + recipe.erase(); return std::nullopt; + } - auto copyBlock = createCopyRegion(builder, loc, varType, bounds); - if (!copyBlock) + // Populate the copy region + if (failed(createCopyRegion(builder, loc, recipe.getCopyRegion(), varType, + bounds))) { + recipe.erase(); return std::nullopt; + } // Only create destroy region if the allocation needs deallocation - std::unique_ptr<Block> destroyBlock; if (needsFree) { // Extract the allocated value from the init block's yield operation - auto yieldOp = cast<acc::YieldOp>(initBlock->getTerminator()); + auto yieldOp = + cast<acc::YieldOp>(recipe.getInitRegion().front().getTerminator()); Value allocRes = yieldOp.getOperand(0); - destroyBlock = createDestroyRegion(builder, loc, varType, allocRes, bounds); - if (!destroyBlock) + if (failed(createDestroyRegion(builder, loc, recipe.getDestroyRegion(), + varType, allocRes, bounds))) { + recipe.erase(); return std::nullopt; + } } - // Now create the recipe operation at the original insertion point and attach - // the blocks - builder.restoreInsertionPoint(originalInsertionPoint); - auto recipe = FirstprivateRecipeOp::create(builder, loc, recipeName, varType); - - // Move the blocks into the recipe's regions - recipe.getInitRegion().push_back(initBlock.release()); - recipe.getCopyRegion().push_back(copyBlock.release()); - if (destroyBlock) - recipe.getDestroyRegion().push_back(destroyBlock.release()); - return recipe; } @@ -3858,7 +3842,8 @@ LogicalResult AtomicUpdateOp::canonicalize(AtomicUpdateOp op, } if (Value writeVal = op.getWriteOpVal()) { - rewriter.replaceOpWithNewOp<AtomicWriteOp>(op, op.getX(), writeVal); + rewriter.replaceOpWithNewOp<AtomicWriteOp>(op, op.getX(), writeVal, + op.getIfCond()); return success(); } @@ -4665,14 +4650,3 @@ mlir::acc::getMutableDataOperands(mlir::Operation *accOp) { .Default([&](mlir::Operation *) { return nullptr; })}; return dataOperands; } - -mlir::Operation *mlir::acc::getEnclosingComputeOp(mlir::Region ®ion) { - mlir::Operation *parentOp = region.getParentOp(); - while (parentOp) { - if (mlir::isa<ACC_COMPUTE_CONSTRUCT_OPS>(parentOp)) { - return parentOp; - } - parentOp = parentOp->getParentOp(); - } - return nullptr; -} diff --git a/mlir/lib/Dialect/OpenACC/Utils/CMakeLists.txt b/mlir/lib/Dialect/OpenACC/Utils/CMakeLists.txt new file mode 100644 index 0000000..68e1246 --- /dev/null +++ b/mlir/lib/Dialect/OpenACC/Utils/CMakeLists.txt @@ -0,0 +1,20 @@ +add_mlir_dialect_library(MLIROpenACCUtils + OpenACCUtils.cpp + + ADDITIONAL_HEADER_DIRS + ${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/OpenACC + + DEPENDS + MLIROpenACCPassIncGen + MLIROpenACCOpsIncGen + MLIROpenACCEnumsIncGen + MLIROpenACCAttributesIncGen + MLIROpenACCMPOpsInterfacesIncGen + MLIROpenACCOpsInterfacesIncGen + MLIROpenACCTypeInterfacesIncGen + + LINK_LIBS PUBLIC + MLIROpenACCDialect + MLIRIR + MLIRSupport +) diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp new file mode 100644 index 0000000..1223325 --- /dev/null +++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtils.cpp @@ -0,0 +1,80 @@ +//===- OpenACCUtils.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/OpenACC/OpenACCUtils.h" + +#include "mlir/Dialect/OpenACC/OpenACC.h" +#include "llvm/ADT/TypeSwitch.h" + +mlir::Operation *mlir::acc::getEnclosingComputeOp(mlir::Region ®ion) { + mlir::Operation *parentOp = region.getParentOp(); + while (parentOp) { + if (mlir::isa<ACC_COMPUTE_CONSTRUCT_OPS>(parentOp)) + return parentOp; + parentOp = parentOp->getParentOp(); + } + return nullptr; +} + +template <typename OpTy> +static bool isOnlyUsedByOpClauses(mlir::Value val, mlir::Region ®ion) { + auto checkIfUsedOnlyByOpInside = [&](mlir::Operation *user) { + // For any users which are not in the current acc region, we can ignore. + // Return true so that it can be used in a `all_of` check. + if (!region.isAncestor(user->getParentRegion())) + return true; + return mlir::isa<OpTy>(user); + }; + + return llvm::all_of(val.getUsers(), checkIfUsedOnlyByOpInside); +} + +bool mlir::acc::isOnlyUsedByPrivateClauses(mlir::Value val, + mlir::Region ®ion) { + return isOnlyUsedByOpClauses<mlir::acc::PrivateOp>(val, region); +} + +bool mlir::acc::isOnlyUsedByReductionClauses(mlir::Value val, + mlir::Region ®ion) { + return isOnlyUsedByOpClauses<mlir::acc::ReductionOp>(val, region); +} + +std::optional<mlir::acc::ClauseDefaultValue> +mlir::acc::getDefaultAttr(Operation *op) { + std::optional<mlir::acc::ClauseDefaultValue> defaultAttr; + Operation *currOp = op; + + // Iterate outwards until a default clause is found (since OpenACC + // specification notes that a visible default clause is the nearest default + // clause appearing on the compute construct or a lexically containing data + // construct. + while (!defaultAttr.has_value() && currOp) { + defaultAttr = + llvm::TypeSwitch<mlir::Operation *, + std::optional<mlir::acc::ClauseDefaultValue>>(currOp) + .Case<ACC_COMPUTE_CONSTRUCT_OPS, mlir::acc::DataOp>( + [&](auto op) { return op.getDefaultAttr(); }) + .Default([&](Operation *) { return std::nullopt; }); + currOp = currOp->getParentOp(); + } + + return defaultAttr; +} + +mlir::acc::VariableTypeCategory mlir::acc::getTypeCategory(mlir::Value var) { + mlir::acc::VariableTypeCategory typeCategory = + mlir::acc::VariableTypeCategory::uncategorized; + if (auto mappableTy = dyn_cast<mlir::acc::MappableType>(var.getType())) + typeCategory = mappableTy.getTypeCategory(var); + else if (auto pointerLikeTy = + dyn_cast<mlir::acc::PointerLikeType>(var.getType())) + typeCategory = pointerLikeTy.getPointeeTypeCategory( + cast<TypedValue<mlir::acc::PointerLikeType>>(var), + pointerLikeTy.getElementType()); + return typeCategory; +} |