//===----------------------------------------------------------------------===// // // 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 "CIRGenCXXABI.h" #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 { class OpenACCRecipeBuilderBase { // makes the copy of the addresses of an alloca to the previous allocation. void makeAllocaCopy(mlir::Location loc, mlir::Type copyType, mlir::Value numEltsToCopy, mlir::Value offsetPerSubarray, mlir::Value destAlloca, mlir::Value srcAlloca); // This function generates the required alloca, similar to // 'emitAutoVarAlloca', except for the OpenACC array/pointer types. mlir::Value makeBoundsAlloca(mlir::Block *block, SourceRange exprRange, mlir::Location loc, std::string_view allocaName, size_t numBounds, llvm::ArrayRef boundTypes); void makeBoundsInit(mlir::Value alloca, mlir::Location loc, mlir::Block *block, const VarDecl *allocaDecl, QualType origType, bool isInitSection); protected: CIRGen::CIRGenFunction &cgf; CIRGen::CIRGenBuilderTy &builder; mlir::Block *createRecipeBlock(mlir::Region ®ion, mlir::Type opTy, mlir::Location loc, size_t numBounds, bool isInit); // Creates a loop through an 'acc.bounds', leaving the 'insertion' point to be // the inside of the loop body. Traverses LB->UB UNLESS `inverse` is set. // Returns the 'subscriptedValue' changed with the new bounds subscript. std::pair createBoundsLoop(mlir::Value subscriptedValue, mlir::Value subscriptedValue2, mlir::Value bound, mlir::Location loc, bool inverse); mlir::Value createBoundsLoop(mlir::Value subscriptedValue, mlir::Value bound, mlir::Location loc, bool inverse) { return createBoundsLoop(subscriptedValue, {}, bound, loc, inverse).first; } mlir::acc::ReductionOperator convertReductionOp(OpenACCReductionOperator op); // 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, size_t numBounds, QualType origType, llvm::ArrayRef combinerRecipes); void createInitRecipe(mlir::Location loc, mlir::Location locEnd, SourceRange exprRange, mlir::Value mainOp, mlir::Region &recipeInitRegion, size_t numBounds, llvm::ArrayRef boundTypes, const VarDecl *allocaDecl, QualType origType, bool emitInitExpr); void createFirstprivateRecipeCopy(mlir::Location loc, mlir::Location locEnd, mlir::Value mainOp, const VarDecl *allocaDecl, const VarDecl *temporary, mlir::Region ©Region, size_t numBounds); void createRecipeDestroySection(mlir::Location loc, mlir::Location locEnd, mlir::Value mainOp, CharUnits alignment, QualType origType, size_t numBounds, QualType baseType, mlir::Region &destroyRegion); OpenACCRecipeBuilderBase(CIRGen::CIRGenFunction &cgf, CIRGen::CIRGenBuilderTy &builder) : cgf(cgf), builder(builder) {} }; template class OpenACCRecipeBuilder : OpenACCRecipeBuilderBase { std::string getRecipeName(SourceRange loc, QualType baseType, unsigned numBounds, OpenACCReductionOperator reductionOp) { std::string recipeName; { llvm::raw_string_ostream stream(recipeName); if constexpr (std::is_same_v) { stream << "privatization_"; } else if constexpr (std::is_same_v) { stream << "firstprivatization_"; } else if constexpr (std::is_same_v) { 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"); } // The naming convention from Flang with bounds doesn't map to C++ types // very well, so we're just going to choose our own here. if (numBounds) stream << "_Bcnt" << numBounds << '_'; MangleContext &mc = cgf.cgm.getCXXABI().getMangleContext(); mc.mangleCanonicalTypeName(baseType, stream); } return recipeName; } public: OpenACCRecipeBuilder(CIRGen::CIRGenFunction &cgf, CIRGen::CIRGenBuilderTy &builder) : OpenACCRecipeBuilderBase(cgf, builder) {} RecipeTy getOrCreateRecipe( ASTContext &astCtx, mlir::OpBuilder::InsertPoint &insertLocation, const Expr *varRef, const VarDecl *varRecipe, const VarDecl *temporary, OpenACCReductionOperator reductionOp, DeclContext *dc, QualType origType, size_t numBounds, llvm::ArrayRef boundTypes, QualType baseType, mlir::Value mainOp, llvm::ArrayRef reductionCombinerRecipes) { assert(!varRecipe->getType()->isSpecificBuiltinType( BuiltinType::ArraySection) && "array section shouldn't make it to recipe creation"); mlir::ModuleOp mod = builder.getBlock() ->getParent() ->template getParentOfType(); std::string recipeName = getRecipeName(varRef->getSourceRange(), baseType, numBounds, reductionOp); if (auto recipe = mod.lookupSymbol(recipeName)) return recipe; mlir::Location loc = cgf.cgm.getLoc(varRef->getBeginLoc()); mlir::Location locEnd = cgf.cgm.getLoc(varRef->getEndLoc()); mlir::OpBuilder modBuilder(mod.getBodyRegion()); if (insertLocation.isSet()) modBuilder.restoreInsertionPoint(insertLocation); RecipeTy recipe; if constexpr (std::is_same_v) { recipe = RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType(), convertReductionOp(reductionOp)); } else { recipe = RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType()); } insertLocation = modBuilder.saveInsertionPoint(); if constexpr (std::is_same_v) { createInitRecipe(loc, locEnd, varRef->getSourceRange(), mainOp, recipe.getInitRegion(), numBounds, boundTypes, varRecipe, origType, /*emitInitExpr=*/true); } else if constexpr (std::is_same_v) { createInitRecipe(loc, locEnd, varRef->getSourceRange(), mainOp, recipe.getInitRegion(), numBounds, boundTypes, varRecipe, origType, /*emitInitExpr=*/true); createReductionRecipeCombiner(loc, locEnd, mainOp, recipe, numBounds, origType, reductionCombinerRecipes); } else { static_assert(std::is_same_v); createInitRecipe(loc, locEnd, varRef->getSourceRange(), mainOp, recipe.getInitRegion(), numBounds, boundTypes, varRecipe, origType, /*emitInitExpr=*/false); createFirstprivateRecipeCopy(loc, locEnd, mainOp, varRecipe, temporary, recipe.getCopyRegion(), numBounds); } if (origType.isDestructedType()) createRecipeDestroySection( loc, locEnd, mainOp, cgf.getContext().getDeclAlign(varRecipe), origType, numBounds, baseType, recipe.getDestroyRegion()); return recipe; } }; } // namespace clang::CIRGen