clang 24.0.0git
CIRGenDeclOpenACC.cpp
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// This contains code to emit Decl nodes as CIR code.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CIRGenFunction.h"
15
16#include "mlir/Dialect/OpenACC/OpenACC.h"
18#include "llvm/Support/SaveAndRestore.h"
19
20using namespace clang;
21using namespace clang::CIRGen;
22
23namespace {
24struct OpenACCDeclareCleanup final : EHScopeStack::Cleanup {
25 mlir::acc::DeclareEnterOp enterOp;
26
27 OpenACCDeclareCleanup(mlir::acc::DeclareEnterOp enterOp) : enterOp(enterOp) {}
28
29 template <typename OutTy, typename InTy>
30 void createOutOp(CIRGenFunction &cgf, InTy inOp) {
31 if constexpr (std::is_same_v<OutTy, mlir::acc::DeleteOp>) {
32 auto outOp =
33 OutTy::create(cgf.getBuilder(), inOp.getLoc(), inOp,
34 inOp.getStructured(), inOp.getImplicit(),
35 llvm::Twine(inOp.getNameAttr()), inOp.getBounds());
36 outOp.setDataClause(inOp.getDataClause());
37 outOp.setModifiers(inOp.getModifiers());
38 } else {
39 auto outOp =
40 OutTy::create(cgf.getBuilder(), inOp.getLoc(), inOp, inOp.getVarPtr(),
41 inOp.getStructured(), inOp.getImplicit(),
42 llvm::Twine(inOp.getNameAttr()), inOp.getBounds());
43 outOp.setDataClause(inOp.getDataClause());
44 outOp.setModifiers(inOp.getModifiers());
45 }
46 }
47
48 void emit(CIRGenFunction &cgf, Flags flags) override {
49 auto exitOp = mlir::acc::DeclareExitOp::create(
50 cgf.getBuilder(), enterOp.getLoc(), enterOp, {});
51
52 // Some data clauses need to be referenced in 'exit', AND need to have an
53 // operation after the exit. Copy these from the enter operation.
54 for (mlir::Value val : enterOp.getDataClauseOperands()) {
55 if (auto copyin = val.getDefiningOp<mlir::acc::CopyinOp>()) {
56 switch (copyin.getDataClause()) {
57 default:
58 llvm_unreachable(
59 "OpenACC local declare clause copyin unexpected data clause");
60 break;
61 case mlir::acc::DataClause::acc_copy:
62 createOutOp<mlir::acc::CopyoutOp>(cgf, copyin);
63 break;
64 case mlir::acc::DataClause::acc_copyin:
65 createOutOp<mlir::acc::DeleteOp>(cgf, copyin);
66 break;
67 }
68 } else if (auto create = val.getDefiningOp<mlir::acc::CreateOp>()) {
69 switch (create.getDataClause()) {
70 default:
71 llvm_unreachable(
72 "OpenACC local declare clause create unexpected data clause");
73 break;
74 case mlir::acc::DataClause::acc_copyout:
75 createOutOp<mlir::acc::CopyoutOp>(cgf, create);
76 break;
77 case mlir::acc::DataClause::acc_create:
78 createOutOp<mlir::acc::DeleteOp>(cgf, create);
79 break;
80 }
81 } else if (auto present = val.getDefiningOp<mlir::acc::PresentOp>()) {
82 createOutOp<mlir::acc::DeleteOp>(cgf, present);
83 } else if (auto dev_res =
84 val.getDefiningOp<mlir::acc::DeclareDeviceResidentOp>()) {
85 createOutOp<mlir::acc::DeleteOp>(cgf, dev_res);
86 } else if (val.getDefiningOp<mlir::acc::DeclareLinkOp>()) {
87 // Link has no exit clauses, and shouldn't be copied.
88 continue;
89 } else if (val.getDefiningOp<mlir::acc::DevicePtrOp>()) {
90 // DevicePtr has no exit clauses, and shouldn't be copied.
91 continue;
92 } else {
93 llvm_unreachable("OpenACC local declare clause unexpected defining op");
94 continue;
95 }
96 exitOp.getDataClauseOperandsMutable().append(val);
97 }
98 }
99};
100} // namespace
101
103 if (const auto *rd = dyn_cast<OpenACCRoutineDecl>(d))
105 else
107}
108
110 mlir::Location exprLoc = cgm.getLoc(d.getBeginLoc());
111 auto enterOp = mlir::acc::DeclareEnterOp::create(
112 builder, exprLoc, mlir::acc::DeclareTokenType::get(&cgm.getMLIRContext()),
113 {});
114
115 emitOpenACCClauses(enterOp, OpenACCDirectiveKind::Declare, d.clauses());
116
117 ehStack.pushCleanup<OpenACCDeclareCleanup>(CleanupKind::NormalCleanup,
118 enterOp);
119}
120
121// Helper function that gets the declaration referenced by the declare clause.
122// This is a simplified verison of the work that `getOpenACCDataOperandInfo`
123// does, as it only has to get forms that 'declare' does.
124static const Decl *getDeclareReferencedDecl(const Expr *e) {
125 const Expr *curVarExpr = e->IgnoreParenImpCasts();
126
127 // Since we allow array sections, we have to unpack the array sections here.
128 // We don't have to worry about other bounds, since only variable or array
129 // name (plus array sections as an extension) are permitted.
130 while (const auto *ase = dyn_cast<ArraySectionExpr>(curVarExpr))
131 curVarExpr = ase->getBase()->IgnoreParenImpCasts();
132
133 if (const auto *dre = dyn_cast<DeclRefExpr>(curVarExpr))
134 return dre->getFoundDecl()->getCanonicalDecl();
135
136 // MemberExpr is allowed when it is implicit 'this'.
137 return cast<MemberExpr>(curVarExpr)->getMemberDecl()->getCanonicalDecl();
138}
139
140template <typename BeforeOpTy, typename DataClauseTy>
142 const Expr *varOperand, DataClauseTy dataClause,
143 OpenACCModifierKind modifiers, bool structured, bool implicit,
144 bool requiresDtor) {
145 // This is a template argument so that we don't have to include all of
146 // mlir::acc into CIRGenModule.
147 static_assert(std::is_same_v<DataClauseTy, mlir::acc::DataClause>);
148 mlir::Location exprLoc = getLoc(varOperand->getBeginLoc());
149 const Decl *refedDecl = getDeclareReferencedDecl(varOperand);
150 StringRef varName = getMangledName(GlobalDecl{cast<VarDecl>(refedDecl)});
151
152 // We have to emit two separate functions in this case, an acc_ctor and an
153 // acc_dtor. These two sections are/should remain reasonably equal, however
154 // the order of the clauses/vs-enter&exit in them makes combining these two
155 // sections not particularly attractive, so we have a bit of repetition.
156 {
157 mlir::OpBuilder::InsertionGuard guardCase(builder);
158 auto ctorOp = mlir::acc::GlobalConstructorOp::create(
159 builder, exprLoc, (varName + "_acc_ctor").str(),
160 /*sym_visibility=*/nullptr);
161 getModule().push_back(ctorOp);
162 mlir::Block *block = builder.createBlock(&ctorOp.getRegion(),
163 ctorOp.getRegion().end(), {}, {});
164 builder.setInsertionPointToEnd(block);
165 // These things are close enough to a function handling-wise we can just
166 // create this here.
167 CIRGenFunction cgf{*this, builder, true};
168 llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
169 cgf.curFn = ctorOp;
171 varOperand->getSourceRange()};
172
173 // This gets the information we need, PLUS emits the bounds correctly, so we
174 // have to do this in both enter and exit.
176 cgf.getOpenACCDataOperandInfo(varOperand);
177 auto beforeOp =
178 BeforeOpTy::create(builder, exprLoc, inf.varValue, structured, implicit,
179 inf.name, inf.bounds);
180 beforeOp.setDataClause(dataClause);
181 beforeOp.setModifiers(convertOpenACCModifiers(modifiers));
182
183 mlir::acc::DeclareEnterOp::create(
184 builder, exprLoc, mlir::acc::DeclareTokenType::get(&getMLIRContext()),
185 beforeOp.getResult());
186
187 mlir::acc::TerminatorOp::create(builder, exprLoc);
188 }
189
190 // copyin, create, and device_resident require a destructor, link does not. In
191 // the case of the first three, they are all a 'getdeviceptr', followed by the
192 // declare_exit, followed by a delete op in the destructor region.
193 if (requiresDtor) {
194 mlir::OpBuilder::InsertionGuard guardCase(builder);
195 auto ctorOp = mlir::acc::GlobalDestructorOp::create(
196 builder, exprLoc, (varName + "_acc_dtor").str(),
197 /*sym_visibility=*/nullptr);
198 getModule().push_back(ctorOp);
199 mlir::Block *block = builder.createBlock(&ctorOp.getRegion(),
200 ctorOp.getRegion().end(), {}, {});
201 builder.setInsertionPointToEnd(block);
202
203 // These things are close enough to a function handling-wise we can just
204 // create this here.
205 CIRGenFunction cgf{*this, builder, true};
206 llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
207 cgf.curFn = ctorOp;
209 varOperand->getSourceRange()};
210
212 cgf.getOpenACCDataOperandInfo(varOperand);
213 auto getDevPtr = mlir::acc::GetDevicePtrOp::create(
214 builder, exprLoc, inf.varValue, structured, implicit, inf.name,
215 inf.bounds);
216 getDevPtr.setDataClause(dataClause);
217 getDevPtr.setModifiers(convertOpenACCModifiers(modifiers));
218
219 mlir::acc::DeclareExitOp::create(builder, exprLoc, /*token=*/mlir::Value{},
220 getDevPtr.getResult());
221 auto deleteOp = mlir::acc::DeleteOp::create(
222 builder, exprLoc, getDevPtr, structured, implicit, inf.name, {});
223 deleteOp.setDataClause(dataClause);
224 deleteOp.setModifiers(convertOpenACCModifiers(modifiers));
225 mlir::acc::TerminatorOp::create(builder, exprLoc);
226 }
227}
228namespace {
229// This class emits all of the information for a 'declare' at a global/ns/class
230// scope. Each clause results in its own acc_ctor and acc_dtor for the variable.
231// This class creates those and emits them properly.
232// This behavior is unique/special enough from the emission of statement-level
233// clauses that it doesn't really make sense to use that clause visitor.
234class OpenACCGlobalDeclareClauseEmitter final
235 : public OpenACCClauseVisitor<OpenACCGlobalDeclareClauseEmitter> {
236 CIRGenModule &cgm;
237
238public:
239 OpenACCGlobalDeclareClauseEmitter(CIRGenModule &cgm) : cgm(cgm) {}
240
241 void VisitClause(const OpenACCClause &clause) {
242 llvm_unreachable("Invalid OpenACC clause on global Declare");
243 }
244
245 void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
246 this->VisitClauseList(clauses);
247 }
248
249 void VisitCopyInClause(const OpenACCCopyInClause &clause) {
250 for (const Expr *var : clause.getVarList())
251 cgm.emitGlobalOpenACCDeclareDataOperands<mlir::acc::CopyinOp>(
252 var, mlir::acc::DataClause::acc_copyin, clause.getModifierList(),
253 /*structured=*/true,
254 /*implicit=*/false, /*requiresDtor=*/true);
255 }
256
257 void VisitCreateClause(const OpenACCCreateClause &clause) {
258 for (const Expr *var : clause.getVarList())
259 cgm.emitGlobalOpenACCDeclareDataOperands<mlir::acc::CreateOp>(
260 var, mlir::acc::DataClause::acc_create, clause.getModifierList(),
261 /*structured=*/true,
262 /*implicit=*/false, /*requiresDtor=*/true);
263 }
264
265 void VisitDeviceResidentClause(const OpenACCDeviceResidentClause &clause) {
266 for (const Expr *var : clause.getVarList())
267 cgm.emitGlobalOpenACCDeclareDataOperands<
268 mlir::acc::DeclareDeviceResidentOp>(
269 var, mlir::acc::DataClause::acc_declare_device_resident, {},
270 /*structured=*/true,
271 /*implicit=*/false, /*requiresDtor=*/true);
272 }
273
274 void VisitLinkClause(const OpenACCLinkClause &clause) {
275 for (const Expr *var : clause.getVarList())
276 cgm.emitGlobalOpenACCDeclareDataOperands<mlir::acc::DeclareLinkOp>(
277 var, mlir::acc::DataClause::acc_declare_link, {},
278 /*structured=*/true,
279 /*implicit=*/false, /*requiresDtor=*/false);
280 }
281};
282} // namespace
283
285 // Declare creates 1 'acc_ctor' and 0-1 'acc_dtor' per clause, since it needs
286 // a unique one on a per-variable basis. We can just use a clause emitter to
287 // do all the work.
288 mlir::OpBuilder::InsertionGuard guardCase(builder);
289 OpenACCGlobalDeclareClauseEmitter em{*this};
290 em.emitClauses(d->clauses());
291}
292
294 // Do nothing here. The OpenACCRoutineDeclAttr handles the implicit name
295 // cases, and the end-of-TU handling manages the named cases. This is
296 // necessary because these references aren't necessarily emitted themselves,
297 // but can be named anywhere.
298}
299
301 // Do nothing here. The OpenACCRoutineDeclAttr handles the implicit name
302 // cases, and the end-of-TU handling manages the named cases. This is
303 // necessary because these references aren't necessarily emitted themselves,
304 // but can be named anywhere.
305}
306
307namespace {
308class OpenACCRoutineClauseEmitter final
309 : public OpenACCClauseVisitor<OpenACCRoutineClauseEmitter> {
310 CIRGenModule &cgm;
312 mlir::acc::RoutineOp routineOp;
313 const clang::FunctionDecl *funcDecl;
314 llvm::SmallVector<mlir::acc::DeviceType> lastDeviceTypeValues;
315
316public:
317 OpenACCRoutineClauseEmitter(CIRGenModule &cgm,
319 mlir::acc::RoutineOp routineOp,
320 const clang::FunctionDecl *funcDecl)
321 : cgm(cgm), builder(builder), routineOp(routineOp), funcDecl(funcDecl) {}
322
323 void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
324 this->VisitClauseList(clauses);
325 }
326
327 void VisitClause(const OpenACCClause &clause) {
328 llvm_unreachable("Invalid OpenACC clause on routine");
329 }
330
331 void VisitSeqClause(const OpenACCSeqClause &clause) {
332 routineOp.addSeq(builder.getContext(), lastDeviceTypeValues);
333 }
334 void VisitWorkerClause(const OpenACCWorkerClause &clause) {
335 routineOp.addWorker(builder.getContext(), lastDeviceTypeValues);
336 }
337 void VisitVectorClause(const OpenACCVectorClause &clause) {
338 routineOp.addVector(builder.getContext(), lastDeviceTypeValues);
339 }
340
341 void VisitNoHostClause(const OpenACCNoHostClause &clause) {
342 routineOp.setNohost(/*attrValue=*/true);
343 }
344
345 void VisitGangClause(const OpenACCGangClause &clause) {
346 // Gang has an optional 'dim' value, which is a constant int of 1, 2, or 3.
347 // If we don't store any expressions in the clause, there are none, else we
348 // expect there is 1, since Sema should enforce that the single 'dim' is the
349 // only valid value.
350 if (clause.getNumExprs() == 0) {
351 routineOp.addGang(builder.getContext(), lastDeviceTypeValues);
352 } else {
353 assert(clause.getNumExprs() == 1);
354 auto [kind, expr] = clause.getExpr(0);
355 assert(kind == OpenACCGangKind::Dim);
356
357 llvm::APSInt curValue = expr->EvaluateKnownConstInt(cgm.getASTContext());
358 // The value is 1, 2, or 3, but 64 bit seems right enough.
359 curValue = curValue.sextOrTrunc(64);
360 routineOp.addGang(builder.getContext(), lastDeviceTypeValues,
361 curValue.getZExtValue());
362 }
363 }
364
365 void VisitDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {
366 lastDeviceTypeValues.clear();
367
368 for (const DeviceTypeArgument &arg : clause.getArchitectures())
369 lastDeviceTypeValues.push_back(decodeDeviceType(arg.getIdentifierInfo()));
370 }
371
372 void VisitBindClause(const OpenACCBindClause &clause) {
373 if (clause.isStringArgument()) {
374 mlir::StringAttr value =
375 builder.getStringAttr(clause.getStringArgument()->getString());
376
377 routineOp.addBindStrName(builder.getContext(), lastDeviceTypeValues,
378 value);
379 } else {
380 assert(clause.isIdentifierArgument());
381 std::string bindName = cgm.getOpenACCBindMangledName(
382 clause.getIdentifierArgument(), funcDecl);
383
384 routineOp.addBindIDName(
385 builder.getContext(), lastDeviceTypeValues,
386 mlir::SymbolRefAttr::get(builder.getContext(), bindName));
387 }
388 }
389};
390} // namespace
391
393 const clang::FunctionDecl *funcDecl, cir::FuncOp func,
395 mlir::OpBuilder::InsertionGuard guardCase(builder);
396 // These need to appear at the global module.
397 builder.setInsertionPointToEnd(&getModule().getBodyRegion().front());
398
399 mlir::Location routineLoc = getLoc(pragmaLoc);
400
401 std::stringstream routineNameSS;
402 // This follows the same naming format as Flang.
403 routineNameSS << "acc_routine_" << routineCounter++;
404 std::string routineName = routineNameSS.str();
405
406 // There isn't a good constructor for RoutineOp that just takes a location +
407 // name + function, so we use one that creates an otherwise RoutineOp and
408 // count on the visitor/emitter to fill these in.
409 auto routineOp = mlir::acc::RoutineOp::create(
410 builder, routineLoc, routineName,
411 mlir::SymbolRefAttr::get(builder.getContext(), func.getName()),
412 /*implicit=*/false);
413
414 // We have to add a pointer going the other direction via an acc.routine_info,
415 // from the func to the routine.
417 if (auto routineInfo =
418 func.getOperation()->getAttrOfType<mlir::acc::RoutineInfoAttr>(
419 mlir::acc::getRoutineInfoAttrName()))
420 funcRoutines.append(routineInfo.getAccRoutines().begin(),
421 routineInfo.getAccRoutines().end());
422
423 funcRoutines.push_back(
424 mlir::SymbolRefAttr::get(builder.getContext(), routineName));
425 func.getOperation()->setAttr(
426 mlir::acc::getRoutineInfoAttrName(),
427 mlir::acc::RoutineInfoAttr::get(func.getContext(), funcRoutines));
428
429 OpenACCRoutineClauseEmitter emitter{*this, builder, routineOp, funcDecl};
430 emitter.emitClauses(clauses);
431}
static void emit(Program &P, llvm::SmallVectorImpl< std::byte > &Code, const T &Val, bool &Success)
Helper to write bytecode and bail out if 32-bit offsets become invalid.
static const Decl * getDeclareReferencedDecl(const Expr *e)
This file defines OpenACC nodes for declarative directives.
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
void emitOpenACCRoutine(const OpenACCRoutineDecl &d)
void emitOpenACCDeclare(const OpenACCDeclareDecl &d)
OpenACCDataOperandInfo getOpenACCDataOperandInfo(const Expr *e)
mlir::Operation * curFn
The current function or global initializer that is generated code for.
EHScopeStack ehStack
Tracks function scope overall cleanup handling.
CIRGenBuilderTy & getBuilder()
This class organizes the cross-function state that is used while generating CIR code.
llvm::StringRef getMangledName(clang::GlobalDecl gd)
void emitGlobalOpenACCDeclareDataOperands(const Expr *varOperand, DataClauseTy dataClause, OpenACCModifierKind modifiers, bool structured, bool implicit, bool requiresDtor)
void emitGlobalOpenACCDeclareDecl(const clang::OpenACCDeclareDecl *cd)
void emitGlobalOpenACCRoutineDecl(const clang::OpenACCRoutineDecl *cd)
void emitGlobalOpenACCDecl(const clang::OpenACCConstructDecl *cd)
void emitOpenACCRoutineDecl(const clang::FunctionDecl *funcDecl, cir::FuncOp func, SourceLocation pragmaLoc, ArrayRef< const OpenACCClause * > clauses)
mlir::Location getLoc(clang::SourceLocation cLoc)
Helpers to convert the presumed location of Clang's SourceLocation to an MLIR Location.
mlir::ModuleOp getModule() const
mlir::MLIRContext & getMLIRContext()
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
SourceLocation getBeginLoc() const LLVM_READONLY
Definition DeclBase.h:439
This represents one expression.
Definition Expr.h:113
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3123
Represents a function declaration or definition.
Definition Decl.h:2059
GlobalDecl - represents a global declaration.
Definition GlobalDecl.h:60
const IdentifierInfo * getIdentifierArgument() const
const StringLiteral * getStringArgument() const
bool isIdentifierArgument() const
ArrayRef< Expr * > getVarList() const
This is the base type for all OpenACC Clauses.
ArrayRef< const OpenACCClause * > clauses() const
Definition DeclOpenACC.h:62
OpenACCModifierKind getModifierList() const
OpenACCModifierKind getModifierList() const
ArrayRef< DeviceTypeArgument > getArchitectures() const
unsigned getNumExprs() const
std::pair< OpenACCGangKind, const Expr * > getExpr(unsigned I) const
Encodes a location in the source.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Stmt.cpp:355
StringRef getString() const
Definition Expr.h:1887
mlir::acc::DeviceType decodeDeviceType(const IdentifierInfo *ii)
@ Decl
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
@ NormalCleanup
Denotes a cleanup that should run when a scope is exited using normal control flow (falling off the e...
mlir::acc::DataClauseModifier convertOpenACCModifiers(OpenACCModifierKind modifiers)
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
constexpr Variable var(Literal L)
Returns the variable of L.
Definition CNFFormula.h:64
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions &DiagOpts, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
Top level wrappers for InstallAPI frontend operations.
OpenACCModifierKind
IdentifierLoc DeviceTypeArgument
U cast(CodeGen::Address addr)
Definition Address.h:327
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)