clang 22.0.0git
CIRGenOpenACCRecipe.h
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Emit OpenACC clause recipes as CIR code.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CIRGenFunction.h"
14
16#include "clang/AST/DeclBase.h"
17#include "clang/AST/Expr.h"
18#include "clang/AST/ExprCXX.h"
19#include "clang/AST/TypeBase.h"
21
22#include "mlir/Dialect/OpenACC/OpenACC.h"
23
24namespace clang::CIRGen {
25template <typename RecipeTy> class OpenACCRecipeBuilder {
28
29 mlir::acc::ReductionOperator convertReductionOp(OpenACCReductionOperator op) {
30 switch (op) {
32 return mlir::acc::ReductionOperator::AccAdd;
34 return mlir::acc::ReductionOperator::AccMul;
36 return mlir::acc::ReductionOperator::AccMax;
38 return mlir::acc::ReductionOperator::AccMin;
40 return mlir::acc::ReductionOperator::AccIand;
42 return mlir::acc::ReductionOperator::AccIor;
44 return mlir::acc::ReductionOperator::AccXor;
46 return mlir::acc::ReductionOperator::AccLand;
48 return mlir::acc::ReductionOperator::AccLor;
50 llvm_unreachable("invalid reduction operator");
51 }
52
53 llvm_unreachable("invalid reduction operator");
54 }
55
56 std::string getRecipeName(SourceRange loc, QualType baseType,
57 OpenACCReductionOperator reductionOp) {
58 std::string recipeName;
59 {
60 llvm::raw_string_ostream stream(recipeName);
61
62 if constexpr (std::is_same_v<RecipeTy, mlir::acc::PrivateRecipeOp>) {
63 stream << "privatization_";
64 } else if constexpr (std::is_same_v<RecipeTy,
65 mlir::acc::FirstprivateRecipeOp>) {
66 stream << "firstprivatization_";
67
68 } else if constexpr (std::is_same_v<RecipeTy,
69 mlir::acc::ReductionRecipeOp>) {
70 stream << "reduction_";
71 // Values here are a little weird (for bitwise and/or is 'i' prefix, and
72 // logical ops with 'l'), but are chosen to be the same as the MLIR
73 // dialect names as well as to match the Flang versions of these.
74 switch (reductionOp) {
76 stream << "add_";
77 break;
79 stream << "mul_";
80 break;
82 stream << "max_";
83 break;
85 stream << "min_";
86 break;
88 stream << "iand_";
89 break;
91 stream << "ior_";
92 break;
94 stream << "xor_";
95 break;
97 stream << "land_";
98 break;
100 stream << "lor_";
101 break;
103 llvm_unreachable("invalid reduction operator");
104 }
105 } else {
106 static_assert(!sizeof(RecipeTy), "Unknown Recipe op kind");
107 }
108
109 MangleContext &mc = cgf.cgm.getCXXABI().getMangleContext();
110 mc.mangleCanonicalTypeName(baseType, stream);
111 }
112 return recipeName;
113 }
114
115 void createFirstprivateRecipeCopy(
116 mlir::Location loc, mlir::Location locEnd, mlir::Value mainOp,
117 CIRGenFunction::AutoVarEmission tempDeclEmission,
118 mlir::acc::FirstprivateRecipeOp recipe, const VarDecl *varRecipe,
119 const VarDecl *temporary) {
120 mlir::Block *block = builder.createBlock(
121 &recipe.getCopyRegion(), recipe.getCopyRegion().end(),
122 {mainOp.getType(), mainOp.getType()}, {loc, loc});
123 builder.setInsertionPointToEnd(&recipe.getCopyRegion().back());
124 CIRGenFunction::LexicalScope ls(cgf, loc, block);
125
126 mlir::BlockArgument fromArg = block->getArgument(0);
127 mlir::BlockArgument toArg = block->getArgument(1);
128
129 mlir::Type elementTy =
130 mlir::cast<cir::PointerType>(mainOp.getType()).getPointee();
131
132 // Set the address of the emission to be the argument, so that we initialize
133 // that instead of the variable in the other block.
134 tempDeclEmission.setAllocatedAddress(
135 Address{toArg, elementTy, cgf.getContext().getDeclAlign(varRecipe)});
136 tempDeclEmission.EmittedAsOffload = true;
137
138 CIRGenFunction::DeclMapRevertingRAII declMapRAII{cgf, temporary};
139 cgf.setAddrOfLocalVar(
140 temporary,
141 Address{fromArg, elementTy, cgf.getContext().getDeclAlign(varRecipe)});
142
143 cgf.emitAutoVarInit(tempDeclEmission);
144 mlir::acc::YieldOp::create(builder, locEnd);
145 }
146
147 // Create the 'init' section of the recipe, including the 'copy' section for
148 // 'firstprivate'. Note that this function is not 'insertion point' clean, in
149 // that it alters the insertion point to be inside of the 'destroy' section of
150 // the recipe, but doesn't restore it aftewards.
151 void createRecipeInitCopy(mlir::Location loc, mlir::Location locEnd,
152 SourceRange exprRange, mlir::Value mainOp,
153 RecipeTy recipe, const VarDecl *varRecipe,
154 const VarDecl *temporary) {
155 assert(varRecipe && "Required recipe variable not set?");
156
157 CIRGenFunction::AutoVarEmission tempDeclEmission{
159 CIRGenFunction::DeclMapRevertingRAII declMapRAII{cgf, varRecipe};
160
161 // Do the 'init' section of the recipe IR, which does an alloca, then the
162 // initialization (except for firstprivate).
163 mlir::Block *block = builder.createBlock(&recipe.getInitRegion(),
164 recipe.getInitRegion().end(),
165 {mainOp.getType()}, {loc});
166 builder.setInsertionPointToEnd(&recipe.getInitRegion().back());
167 CIRGenFunction::LexicalScope ls(cgf, loc, block);
168
169 tempDeclEmission =
170 cgf.emitAutoVarAlloca(*varRecipe, builder.saveInsertionPoint());
171
172 // 'firstprivate' doesn't do its initialization in the 'init' section,
173 // instead does it in the 'copy' section. SO only do init here.
174 // 'reduction' appears to use it too (rather than a 'copy' section), so
175 // we probably have to do it here too, but we can do that when we get to
176 // reduction implementation.
177 if constexpr (std::is_same_v<RecipeTy, mlir::acc::PrivateRecipeOp>) {
178 // We are OK with no init for builtins, arrays of builtins, or pointers,
179 // else we should NYI so we know to go look for these.
180 if (cgf.getContext().getLangOpts().CPlusPlus &&
181 !varRecipe->getType()
183 ->isBuiltinType() &&
184 !varRecipe->getType()->isPointerType() && !varRecipe->getInit()) {
185 // If we don't have any initialization recipe, we failed during Sema to
186 // initialize this correctly. If we disable the
187 // Sema::TentativeAnalysisScopes in SemaOpenACC::CreateInitRecipe, it'll
188 // emit an error to tell us. However, emitting those errors during
189 // production is a violation of the standard, so we cannot do them.
190 cgf.cgm.errorNYI(exprRange, "private default-init recipe");
191 }
192 cgf.emitAutoVarInit(tempDeclEmission);
193 } else if constexpr (std::is_same_v<RecipeTy,
194 mlir::acc::ReductionRecipeOp>) {
195 // Unlike Private, the recipe here is always required as it has to do
196 // init, not just 'default' init.
197 if (!varRecipe->getInit())
198 cgf.cgm.errorNYI(exprRange, "reduction init recipe");
199 cgf.emitAutoVarInit(tempDeclEmission);
200 }
201
202 mlir::acc::YieldOp::create(builder, locEnd);
203
204 if constexpr (std::is_same_v<RecipeTy, mlir::acc::FirstprivateRecipeOp>) {
205 if (!varRecipe->getInit()) {
206 // If we don't have any initialization recipe, we failed during Sema to
207 // initialize this correctly. If we disable the
208 // Sema::TentativeAnalysisScopes in SemaOpenACC::CreateInitRecipe, it'll
209 // emit an error to tell us. However, emitting those errors during
210 // production is a violation of the standard, so we cannot do them.
211 cgf.cgm.errorNYI(
212 exprRange, "firstprivate copy-init recipe not properly generated");
213 }
214
215 createFirstprivateRecipeCopy(loc, locEnd, mainOp, tempDeclEmission,
216 recipe, varRecipe, temporary);
217 }
218 }
219
220 // This function generates the 'combiner' section for a reduction recipe. Note
221 // that this function is not 'insertion point' clean, in that it alters the
222 // insertion point to be inside of the 'combiner' section of the recipe, but
223 // doesn't restore it aftewards.
224 void createReductionRecipeCombiner(mlir::Location loc, mlir::Location locEnd,
225 mlir::Value mainOp,
226 mlir::acc::ReductionRecipeOp recipe) {
227 mlir::Block *block = builder.createBlock(
228 &recipe.getCombinerRegion(), recipe.getCombinerRegion().end(),
229 {mainOp.getType(), mainOp.getType()}, {loc, loc});
230 builder.setInsertionPointToEnd(&recipe.getCombinerRegion().back());
231 CIRGenFunction::LexicalScope ls(cgf, loc, block);
232
233 mlir::BlockArgument lhsArg = block->getArgument(0);
234
235 mlir::acc::YieldOp::create(builder, locEnd, lhsArg);
236 }
237
238 // This function generates the 'destroy' section for a recipe. Note
239 // that this function is not 'insertion point' clean, in that it alters the
240 // insertion point to be inside of the 'destroy' section of the recipe, but
241 // doesn't restore it aftewards.
242 void createRecipeDestroySection(mlir::Location loc, mlir::Location locEnd,
243 mlir::Value mainOp, CharUnits alignment,
244 QualType baseType,
245 mlir::Region &destroyRegion) {
246 mlir::Block *block =
247 builder.createBlock(&destroyRegion, destroyRegion.end(),
248 {mainOp.getType(), mainOp.getType()}, {loc, loc});
249 builder.setInsertionPointToEnd(&destroyRegion.back());
250 CIRGenFunction::LexicalScope ls(cgf, loc, block);
251
252 mlir::Type elementTy =
253 mlir::cast<cir::PointerType>(mainOp.getType()).getPointee();
254 // The destroy region has a signature of "original item, privatized item".
255 // So the 2nd item is the one that needs destroying, the former is just for
256 // reference and we don't really have a need for it at the moment.
257 Address addr{block->getArgument(1), elementTy, alignment};
258 cgf.emitDestroy(addr, baseType,
259 cgf.getDestroyer(QualType::DK_cxx_destructor));
260
261 mlir::acc::YieldOp::create(builder, locEnd);
262 }
263
264public:
267 : cgf(cgf), builder(builder) {}
268 RecipeTy getOrCreateRecipe(ASTContext &astCtx, const Expr *varRef,
269 const VarDecl *varRecipe, const VarDecl *temporary,
270 OpenACCReductionOperator reductionOp,
271 DeclContext *dc, QualType baseType,
272 mlir::Value mainOp) {
273
274 if (baseType->isPointerType() ||
275 (baseType->isArrayType() && !baseType->isConstantArrayType())) {
276 // It is clear that the use of pointers/VLAs in a recipe are not properly
277 // generated/don't do what they are supposed to do. In the case where we
278 // have 'bounds', we can actually figure out what we want to
279 // initialize/copy/destroy/compare/etc, but we haven't figured out how
280 // that looks yet, both between the IR and generation code. For now, we
281 // will do an NYI error no it.
282 cgf.cgm.errorNYI(
283 varRef->getSourceRange(),
284 "OpenACC recipe generation for pointer/non-constant arrays");
285 }
286
287 mlir::ModuleOp mod = builder.getBlock()
288 ->getParent()
289 ->template getParentOfType<mlir::ModuleOp>();
290
291 std::string recipeName =
292 getRecipeName(varRef->getSourceRange(), baseType, reductionOp);
293 if (auto recipe = mod.lookupSymbol<RecipeTy>(recipeName))
294 return recipe;
295
296 mlir::Location loc = cgf.cgm.getLoc(varRef->getBeginLoc());
297 mlir::Location locEnd = cgf.cgm.getLoc(varRef->getEndLoc());
298
299 mlir::OpBuilder modBuilder(mod.getBodyRegion());
300 RecipeTy recipe;
301
302 if constexpr (std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
303 recipe = RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType(),
304 convertReductionOp(reductionOp));
305 } else {
306 recipe = RecipeTy::create(modBuilder, loc, recipeName, mainOp.getType());
307 }
308
309 createRecipeInitCopy(loc, locEnd, varRef->getSourceRange(), mainOp, recipe,
310 varRecipe, temporary);
311
312 if constexpr (std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
313 createReductionRecipeCombiner(loc, locEnd, mainOp, recipe);
314 }
315
316 if (varRecipe && varRecipe->needsDestruction(cgf.getContext()))
317 createRecipeDestroySection(loc, locEnd, mainOp,
318 cgf.getContext().getDeclAlign(varRecipe),
319 baseType, recipe.getDestroyRegion());
320 return recipe;
321 }
322};
323} // namespace clang::CIRGen
Defines the clang::ASTContext interface.
Defines the clang::Expr interface and subclasses for C++ expressions.
Defines some OpenACC-specific enums and functions.
C Language Family Type Representation.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:188
OpenACCRecipeBuilder(CIRGen::CIRGenFunction &cgf, CIRGen::CIRGenBuilderTy &builder)
RecipeTy getOrCreateRecipe(ASTContext &astCtx, const Expr *varRef, const VarDecl *varRecipe, const VarDecl *temporary, OpenACCReductionOperator reductionOp, DeclContext *dc, QualType baseType, mlir::Value mainOp)
CharUnits - This is an opaque type for sizes expressed in character units.
Definition CharUnits.h:38
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition DeclBase.h:1449
This represents one expression.
Definition Expr.h:112
MangleContext - Context for tracking state which persists across multiple calls to the C++ name mangl...
Definition Mangle.h:52
virtual void mangleCanonicalTypeName(QualType T, raw_ostream &, bool NormalizeIntegers=false)=0
Generates a unique string for an externally visible type for use with TBAA or type uniquing.
A (possibly-)qualified type.
Definition TypeBase.h:937
A trivial tuple used to represent a source range.
SourceLocation getEndLoc() const LLVM_READONLY
Definition Stmt.cpp:358
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:334
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:346
const Type * getPointeeOrArrayElementType() const
If this is a pointer type, return the pointee type.
Definition TypeBase.h:9058
bool isConstantArrayType() const
Definition TypeBase.h:8625
bool isArrayType() const
Definition TypeBase.h:8621
bool isPointerType() const
Definition TypeBase.h:8522
bool isBuiltinType() const
Helper methods to distinguish type categories.
Definition TypeBase.h:8645
QualType getType() const
Definition Decl.h:722
Represents a variable declaration or definition.
Definition Decl.h:925
QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const
Would the destruction of this variable have any effect, and if so, what kind?
Definition Decl.cpp:2851
const Expr * getInit() const
Definition Decl.h:1367
OpenACCReductionOperator
@ Invalid
Invalid Reduction Clause Kind.
bool EmittedAsOffload
True if the variable was emitted as an offload recipe, and thus doesn't have the same sort of alloca ...
Represents a scope, including function bodies, compound statements, and the substatements of if/while...