summaryrefslogtreecommitdiff
path: root/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h')
-rw-r--r--clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h323
1 files changed, 323 insertions, 0 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
new file mode 100644
index 000000000000..102fd890e557
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCRecipe.h
@@ -0,0 +1,323 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Emit OpenACC clause recipes as CIR code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/TypeBase.h"
+#include "clang/Basic/OpenACCKinds.h"
+
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+
+namespace clang::CIRGen {
+template <typename RecipeTy> class OpenACCRecipeBuilder {
+ CIRGen::CIRGenFunction &cgf;
+ CIRGen::CIRGenBuilderTy &builder;
+
+ mlir::acc::ReductionOperator convertReductionOp(OpenACCReductionOperator op) {
+ switch (op) {
+ case OpenACCReductionOperator::Addition:
+ return mlir::acc::ReductionOperator::AccAdd;
+ case OpenACCReductionOperator::Multiplication:
+ return mlir::acc::ReductionOperator::AccMul;
+ case OpenACCReductionOperator::Max:
+ return mlir::acc::ReductionOperator::AccMax;
+ case OpenACCReductionOperator::Min:
+ return mlir::acc::ReductionOperator::AccMin;
+ case OpenACCReductionOperator::BitwiseAnd:
+ return mlir::acc::ReductionOperator::AccIand;
+ case OpenACCReductionOperator::BitwiseOr:
+ return mlir::acc::ReductionOperator::AccIor;
+ case OpenACCReductionOperator::BitwiseXOr:
+ return mlir::acc::ReductionOperator::AccXor;
+ case OpenACCReductionOperator::And:
+ return mlir::acc::ReductionOperator::AccLand;
+ case OpenACCReductionOperator::Or:
+ return mlir::acc::ReductionOperator::AccLor;
+ case OpenACCReductionOperator::Invalid:
+ llvm_unreachable("invalid reduction operator");
+ }
+
+ llvm_unreachable("invalid reduction operator");
+ }
+
+ std::string getRecipeName(SourceRange loc, QualType baseType,
+ OpenACCReductionOperator reductionOp) {
+ std::string recipeName;
+ {
+ llvm::raw_string_ostream stream(recipeName);
+
+ if constexpr (std::is_same_v<RecipeTy, mlir::acc::PrivateRecipeOp>) {
+ stream << "privatization_";
+ } else if constexpr (std::is_same_v<RecipeTy,
+ mlir::acc::FirstprivateRecipeOp>) {
+ stream << "firstprivatization_";
+
+ } else if constexpr (std::is_same_v<RecipeTy,
+ mlir::acc::ReductionRecipeOp>) {
+ stream << "reduction_";
+ // Values here are a little weird (for bitwise and/or is 'i' prefix, and
+ // logical ops with 'l'), but are chosen to be the same as the MLIR
+ // dialect names as well as to match the Flang versions of these.
+ switch (reductionOp) {
+ case OpenACCReductionOperator::Addition:
+ stream << "add_";
+ break;
+ case OpenACCReductionOperator::Multiplication:
+ stream << "mul_";
+ break;
+ case OpenACCReductionOperator::Max:
+ stream << "max_";
+ break;
+ case OpenACCReductionOperator::Min:
+ stream << "min_";
+ break;
+ case OpenACCReductionOperator::BitwiseAnd:
+ stream << "iand_";
+ break;
+ case OpenACCReductionOperator::BitwiseOr:
+ stream << "ior_";
+ break;
+ case OpenACCReductionOperator::BitwiseXOr:
+ stream << "xor_";
+ break;
+ case OpenACCReductionOperator::And:
+ stream << "land_";
+ break;
+ case OpenACCReductionOperator::Or:
+ stream << "lor_";
+ break;
+ case OpenACCReductionOperator::Invalid:
+ llvm_unreachable("invalid reduction operator");
+ }
+ } else {
+ static_assert(!sizeof(RecipeTy), "Unknown Recipe op kind");
+ }
+
+ MangleContext &mc = cgf.cgm.getCXXABI().getMangleContext();
+ mc.mangleCanonicalTypeName(baseType, stream);
+ }
+ return recipeName;
+ }
+
+ void createFirstprivateRecipeCopy(
+ mlir::Location loc, mlir::Location locEnd, mlir::Value mainOp,
+ CIRGenFunction::AutoVarEmission tempDeclEmission,
+ mlir::acc::FirstprivateRecipeOp recipe, const VarDecl *varRecipe,
+ const VarDecl *temporary) {
+ mlir::Block *block = builder.createBlock(
+ &recipe.getCopyRegion(), recipe.getCopyRegion().end(),
+ {mainOp.getType(), mainOp.getType()}, {loc, loc});
+ builder.setInsertionPointToEnd(&recipe.getCopyRegion().back());
+ CIRGenFunction::LexicalScope ls(cgf, loc, block);
+
+ mlir::BlockArgument fromArg = block->getArgument(0);
+ mlir::BlockArgument toArg = block->getArgument(1);
+
+ mlir::Type elementTy =
+ mlir::cast<cir::PointerType>(mainOp.getType()).getPointee();
+
+ // Set the address of the emission to be the argument, so that we initialize
+ // that instead of the variable in the other block.
+ tempDeclEmission.setAllocatedAddress(
+ Address{toArg, elementTy, cgf.getContext().getDeclAlign(varRecipe)});
+ tempDeclEmission.EmittedAsOffload = true;
+
+ CIRGenFunction::DeclMapRevertingRAII declMapRAII{cgf, temporary};
+ cgf.setAddrOfLocalVar(
+ temporary,
+ Address{fromArg, elementTy, cgf.getContext().getDeclAlign(varRecipe)});
+
+ cgf.emitAutoVarInit(tempDeclEmission);
+ mlir::acc::YieldOp::create(builder, locEnd);
+ }
+
+ // Create the 'init' section of the recipe, including the 'copy' section for
+ // 'firstprivate'. Note that this function is not 'insertion point' clean, in
+ // that it alters the insertion point to be inside of the 'destroy' section of
+ // the recipe, but doesn't restore it aftewards.
+ void createRecipeInitCopy(mlir::Location loc, mlir::Location locEnd,
+ SourceRange exprRange, mlir::Value mainOp,
+ RecipeTy recipe, const VarDecl *varRecipe,
+ const VarDecl *temporary) {
+ assert(varRecipe && "Required recipe variable not set?");
+
+ CIRGenFunction::AutoVarEmission tempDeclEmission{
+ CIRGenFunction::AutoVarEmission::invalid()};
+ CIRGenFunction::DeclMapRevertingRAII declMapRAII{cgf, varRecipe};
+
+ // Do the 'init' section of the recipe IR, which does an alloca, then the
+ // initialization (except for firstprivate).
+ mlir::Block *block = builder.createBlock(&recipe.getInitRegion(),
+ recipe.getInitRegion().end(),
+ {mainOp.getType()}, {loc});
+ builder.setInsertionPointToEnd(&recipe.getInitRegion().back());
+ CIRGenFunction::LexicalScope ls(cgf, loc, block);
+
+ tempDeclEmission =
+ cgf.emitAutoVarAlloca(*varRecipe, builder.saveInsertionPoint());
+
+ // 'firstprivate' doesn't do its initialization in the 'init' section,
+ // instead does it in the 'copy' section. SO only do init here.
+ // 'reduction' appears to use it too (rather than a 'copy' section), so
+ // we probably have to do it here too, but we can do that when we get to
+ // reduction implementation.
+ if constexpr (std::is_same_v<RecipeTy, mlir::acc::PrivateRecipeOp>) {
+ // We are OK with no init for builtins, arrays of builtins, or pointers,
+ // else we should NYI so we know to go look for these.
+ if (cgf.getContext().getLangOpts().CPlusPlus &&
+ !varRecipe->getType()
+ ->getPointeeOrArrayElementType()
+ ->isBuiltinType() &&
+ !varRecipe->getType()->isPointerType() && !varRecipe->getInit()) {
+ // If we don't have any initialization recipe, we failed during Sema to
+ // initialize this correctly. If we disable the
+ // Sema::TentativeAnalysisScopes in SemaOpenACC::CreateInitRecipe, it'll
+ // emit an error to tell us. However, emitting those errors during
+ // production is a violation of the standard, so we cannot do them.
+ cgf.cgm.errorNYI(exprRange, "private default-init recipe");
+ }
+ cgf.emitAutoVarInit(tempDeclEmission);
+ } else if constexpr (std::is_same_v<RecipeTy,
+ mlir::acc::ReductionRecipeOp>) {
+ // Unlike Private, the recipe here is always required as it has to do
+ // init, not just 'default' init.
+ if (!varRecipe->getInit())
+ cgf.cgm.errorNYI(exprRange, "reduction init recipe");
+ cgf.emitAutoVarInit(tempDeclEmission);
+ }
+
+ mlir::acc::YieldOp::create(builder, locEnd);
+
+ if constexpr (std::is_same_v<RecipeTy, mlir::acc::FirstprivateRecipeOp>) {
+ if (!varRecipe->getInit()) {
+ // If we don't have any initialization recipe, we failed during Sema to
+ // initialize this correctly. If we disable the
+ // Sema::TentativeAnalysisScopes in SemaOpenACC::CreateInitRecipe, it'll
+ // emit an error to tell us. However, emitting those errors during
+ // production is a violation of the standard, so we cannot do them.
+ cgf.cgm.errorNYI(
+ exprRange, "firstprivate copy-init recipe not properly generated");
+ }
+
+ createFirstprivateRecipeCopy(loc, locEnd, mainOp, tempDeclEmission,
+ recipe, varRecipe, temporary);
+ }
+ }
+
+ // This function generates the 'combiner' section for a reduction recipe. Note
+ // that this function is not 'insertion point' clean, in that it alters the
+ // insertion point to be inside of the 'combiner' section of the recipe, but
+ // doesn't restore it aftewards.
+ void createReductionRecipeCombiner(mlir::Location loc, mlir::Location locEnd,
+ mlir::Value mainOp,
+ mlir::acc::ReductionRecipeOp recipe) {
+ mlir::Block *block = builder.createBlock(
+ &recipe.getCombinerRegion(), recipe.getCombinerRegion().end(),
+ {mainOp.getType(), mainOp.getType()}, {loc, loc});
+ builder.setInsertionPointToEnd(&recipe.getCombinerRegion().back());
+ CIRGenFunction::LexicalScope ls(cgf, loc, block);
+
+ mlir::BlockArgument lhsArg = block->getArgument(0);
+
+ mlir::acc::YieldOp::create(builder, locEnd, lhsArg);
+ }
+
+ // This function generates the 'destroy' section for a recipe. Note
+ // that this function is not 'insertion point' clean, in that it alters the
+ // insertion point to be inside of the 'destroy' section of the recipe, but
+ // doesn't restore it aftewards.
+ void createRecipeDestroySection(mlir::Location loc, mlir::Location locEnd,
+ mlir::Value mainOp, CharUnits alignment,
+ QualType baseType,
+ mlir::Region &destroyRegion) {
+ mlir::Block *block =
+ builder.createBlock(&destroyRegion, destroyRegion.end(),
+ {mainOp.getType(), mainOp.getType()}, {loc, loc});
+ builder.setInsertionPointToEnd(&destroyRegion.back());
+ CIRGenFunction::LexicalScope ls(cgf, loc, block);
+
+ mlir::Type elementTy =
+ mlir::cast<cir::PointerType>(mainOp.getType()).getPointee();
+ // The destroy region has a signature of "original item, privatized item".
+ // So the 2nd item is the one that needs destroying, the former is just for
+ // reference and we don't really have a need for it at the moment.
+ Address addr{block->getArgument(1), elementTy, alignment};
+ cgf.emitDestroy(addr, baseType,
+ cgf.getDestroyer(QualType::DK_cxx_destructor));
+
+ mlir::acc::YieldOp::create(builder, locEnd);
+ }
+
+public:
+ OpenACCRecipeBuilder(CIRGen::CIRGenFunction &cgf,
+ CIRGen::CIRGenBuilderTy &builder)
+ : cgf(cgf), builder(builder) {}
+ RecipeTy getOrCreateRecipe(ASTContext &astCtx, const Expr *varRef,
+ const VarDecl *varRecipe, const VarDecl *temporary,
+ OpenACCReductionOperator reductionOp,
+ DeclContext *dc, QualType baseType,
+ mlir::Value mainOp) {
+
+ if (baseType->isPointerType() ||
+ (baseType->isArrayType() && !baseType->isConstantArrayType())) {
+ // It is clear that the use of pointers/VLAs in a recipe are not properly
+ // generated/don't do what they are supposed to do. In the case where we
+ // have 'bounds', we can actually figure out what we want to
+ // initialize/copy/destroy/compare/etc, but we haven't figured out how
+ // that looks yet, both between the IR and generation code. For now, we
+ // will do an NYI error no it.
+ cgf.cgm.errorNYI(
+ varRef->getSourceRange(),
+ "OpenACC recipe generation for pointer/non-constant arrays");
+ }
+
+ mlir::ModuleOp mod = builder.getBlock()
+ ->getParent()
+ ->template getParentOfType<mlir::ModuleOp>();
+
+ std::string recipeName =
+ getRecipeName(varRef->getSourceRange(), baseType, reductionOp);
+ if (auto recipe = mod.lookupSymbol<RecipeTy>(recipeName))
+ return recipe;
+
+ mlir::Location loc = cgf.cgm.getLoc(varRef->getBeginLoc());
+ mlir::Location locEnd = cgf.cgm.getLoc(varRef->getEndLoc());
+
+ mlir::OpBuilder modBuilder(mod.getBodyRegion());
+ RecipeTy recipe;
+
+ if constexpr (std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
+ recipe = RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType(),
+ convertReductionOp(reductionOp));
+ } else {
+ recipe = RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType());
+ }
+
+ createRecipeInitCopy(loc, locEnd, varRef->getSourceRange(), mainOp, recipe,
+ varRecipe, temporary);
+
+ if constexpr (std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
+ createReductionRecipeCombiner(loc, locEnd, mainOp, recipe);
+ }
+
+ if (varRecipe && varRecipe->needsDestruction(cgf.getContext()))
+ createRecipeDestroySection(loc, locEnd, mainOp,
+ cgf.getContext().getDeclAlign(varRecipe),
+ baseType, recipe.getDestroyRegion());
+ return recipe;
+ }
+};
+} // namespace clang::CIRGen