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;
170 CIRGenFunction::SourceLocRAIIObject fnLoc{cgf, exprLoc};
171
172 // This gets the information we need, PLUS emits the bounds correctly, so we
173 // have to do this in both enter and exit.
175 cgf.getOpenACCDataOperandInfo(varOperand);
176 auto beforeOp =
177 BeforeOpTy::create(builder, exprLoc, inf.varValue, structured, implicit,
178 inf.name, inf.bounds);
179 beforeOp.setDataClause(dataClause);
180 beforeOp.setModifiers(convertOpenACCModifiers(modifiers));
181
182 mlir::acc::DeclareEnterOp::create(
183 builder, exprLoc, mlir::acc::DeclareTokenType::get(&getMLIRContext()),
184 beforeOp.getResult());
185
186 mlir::acc::TerminatorOp::create(builder, exprLoc);
187 }
188
189 // copyin, create, and device_resident require a destructor, link does not. In
190 // the case of the first three, they are all a 'getdeviceptr', followed by the
191 // declare_exit, followed by a delete op in the destructor region.
192 if (requiresDtor) {
193 mlir::OpBuilder::InsertionGuard guardCase(builder);
194 auto ctorOp = mlir::acc::GlobalDestructorOp::create(
195 builder, exprLoc, (varName + "_acc_dtor").str(),
196 /*sym_visibility=*/nullptr);
197 getModule().push_back(ctorOp);
198 mlir::Block *block = builder.createBlock(&ctorOp.getRegion(),
199 ctorOp.getRegion().end(), {}, {});
200 builder.setInsertionPointToEnd(block);
201
202 // These things are close enough to a function handling-wise we can just
203 // create this here.
204 CIRGenFunction cgf{*this, builder, true};
205 llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
206 cgf.curFn = ctorOp;
207 CIRGenFunction::SourceLocRAIIObject fnLoc{cgf, exprLoc};
208
210 cgf.getOpenACCDataOperandInfo(varOperand);
211 auto getDevPtr = mlir::acc::GetDevicePtrOp::create(
212 builder, exprLoc, inf.varValue, structured, implicit, inf.name,
213 inf.bounds);
214 getDevPtr.setDataClause(dataClause);
215 getDevPtr.setModifiers(convertOpenACCModifiers(modifiers));
216
217 mlir::acc::DeclareExitOp::create(builder, exprLoc, /*token=*/mlir::Value{},
218 getDevPtr.getResult());
219 auto deleteOp = mlir::acc::DeleteOp::create(
220 builder, exprLoc, getDevPtr, structured, implicit, inf.name, {});
221 deleteOp.setDataClause(dataClause);
222 deleteOp.setModifiers(convertOpenACCModifiers(modifiers));
223 mlir::acc::TerminatorOp::create(builder, exprLoc);
224 }
225}
226namespace {
227// This class emits all of the information for a 'declare' at a global/ns/class
228// scope. Each clause results in its own acc_ctor and acc_dtor for the variable.
229// This class creates those and emits them properly.
230// This behavior is unique/special enough from the emission of statement-level
231// clauses that it doesn't really make sense to use that clause visitor.
232class OpenACCGlobalDeclareClauseEmitter final
233 : public OpenACCClauseVisitor<OpenACCGlobalDeclareClauseEmitter> {
234 CIRGenModule &cgm;
235
236public:
237 OpenACCGlobalDeclareClauseEmitter(CIRGenModule &cgm) : cgm(cgm) {}
238
239 void VisitClause(const OpenACCClause &clause) {
240 llvm_unreachable("Invalid OpenACC clause on global Declare");
241 }
242
243 void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
244 this->VisitClauseList(clauses);
245 }
246
247 void VisitCopyInClause(const OpenACCCopyInClause &clause) {
248 for (const Expr *var : clause.getVarList())
249 cgm.emitGlobalOpenACCDeclareDataOperands<mlir::acc::CopyinOp>(
250 var, mlir::acc::DataClause::acc_copyin, clause.getModifierList(),
251 /*structured=*/true,
252 /*implicit=*/false, /*requiresDtor=*/true);
253 }
254
255 void VisitCreateClause(const OpenACCCreateClause &clause) {
256 for (const Expr *var : clause.getVarList())
257 cgm.emitGlobalOpenACCDeclareDataOperands<mlir::acc::CreateOp>(
258 var, mlir::acc::DataClause::acc_create, clause.getModifierList(),
259 /*structured=*/true,
260 /*implicit=*/false, /*requiresDtor=*/true);
261 }
262
263 void VisitDeviceResidentClause(const OpenACCDeviceResidentClause &clause) {
264 for (const Expr *var : clause.getVarList())
265 cgm.emitGlobalOpenACCDeclareDataOperands<
266 mlir::acc::DeclareDeviceResidentOp>(
267 var, mlir::acc::DataClause::acc_declare_device_resident, {},
268 /*structured=*/true,
269 /*implicit=*/false, /*requiresDtor=*/true);
270 }
271
272 void VisitLinkClause(const OpenACCLinkClause &clause) {
273 for (const Expr *var : clause.getVarList())
274 cgm.emitGlobalOpenACCDeclareDataOperands<mlir::acc::DeclareLinkOp>(
275 var, mlir::acc::DataClause::acc_declare_link, {},
276 /*structured=*/true,
277 /*implicit=*/false, /*requiresDtor=*/false);
278 }
279};
280} // namespace
281
283 // Declare creates 1 'acc_ctor' and 0-1 'acc_dtor' per clause, since it needs
284 // a unique one on a per-variable basis. We can just use a clause emitter to
285 // do all the work.
286 mlir::OpBuilder::InsertionGuard guardCase(builder);
287 OpenACCGlobalDeclareClauseEmitter em{*this};
288 em.emitClauses(d->clauses());
289}
290
292 // Do nothing here. The OpenACCRoutineDeclAttr handles the implicit name
293 // cases, and the end-of-TU handling manages the named cases. This is
294 // necessary because these references aren't necessarily emitted themselves,
295 // but can be named anywhere.
296}
297
299 // Do nothing here. The OpenACCRoutineDeclAttr handles the implicit name
300 // cases, and the end-of-TU handling manages the named cases. This is
301 // necessary because these references aren't necessarily emitted themselves,
302 // but can be named anywhere.
303}
304
305namespace {
306class OpenACCRoutineClauseEmitter final
307 : public OpenACCClauseVisitor<OpenACCRoutineClauseEmitter> {
308 CIRGenModule &cgm;
310 mlir::acc::RoutineOp routineOp;
311 const clang::FunctionDecl *funcDecl;
312 llvm::SmallVector<mlir::acc::DeviceType> lastDeviceTypeValues;
313
314public:
315 OpenACCRoutineClauseEmitter(CIRGenModule &cgm,
317 mlir::acc::RoutineOp routineOp,
318 const clang::FunctionDecl *funcDecl)
319 : cgm(cgm), builder(builder), routineOp(routineOp), funcDecl(funcDecl) {}
320
321 void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
322 this->VisitClauseList(clauses);
323 }
324
325 void VisitClause(const OpenACCClause &clause) {
326 llvm_unreachable("Invalid OpenACC clause on routine");
327 }
328
329 void VisitSeqClause(const OpenACCSeqClause &clause) {
330 routineOp.addSeq(builder.getContext(), lastDeviceTypeValues);
331 }
332 void VisitWorkerClause(const OpenACCWorkerClause &clause) {
333 routineOp.addWorker(builder.getContext(), lastDeviceTypeValues);
334 }
335 void VisitVectorClause(const OpenACCVectorClause &clause) {
336 routineOp.addVector(builder.getContext(), lastDeviceTypeValues);
337 }
338
339 void VisitNoHostClause(const OpenACCNoHostClause &clause) {
340 routineOp.setNohost(/*attrValue=*/true);
341 }
342
343 void VisitGangClause(const OpenACCGangClause &clause) {
344 // Gang has an optional 'dim' value, which is a constant int of 1, 2, or 3.
345 // If we don't store any expressions in the clause, there are none, else we
346 // expect there is 1, since Sema should enforce that the single 'dim' is the
347 // only valid value.
348 if (clause.getNumExprs() == 0) {
349 routineOp.addGang(builder.getContext(), lastDeviceTypeValues);
350 } else {
351 assert(clause.getNumExprs() == 1);
352 auto [kind, expr] = clause.getExpr(0);
353 assert(kind == OpenACCGangKind::Dim);
354
355 llvm::APSInt curValue = expr->EvaluateKnownConstInt(cgm.getASTContext());
356 // The value is 1, 2, or 3, but 64 bit seems right enough.
357 curValue = curValue.sextOrTrunc(64);
358 routineOp.addGang(builder.getContext(), lastDeviceTypeValues,
359 curValue.getZExtValue());
360 }
361 }
362
363 void VisitDeviceTypeClause(const OpenACCDeviceTypeClause &clause) {
364 lastDeviceTypeValues.clear();
365
366 for (const DeviceTypeArgument &arg : clause.getArchitectures())
367 lastDeviceTypeValues.push_back(decodeDeviceType(arg.getIdentifierInfo()));
368 }
369
370 void VisitBindClause(const OpenACCBindClause &clause) {
371 if (clause.isStringArgument()) {
372 mlir::StringAttr value =
373 builder.getStringAttr(clause.getStringArgument()->getString());
374
375 routineOp.addBindStrName(builder.getContext(), lastDeviceTypeValues,
376 value);
377 } else {
378 assert(clause.isIdentifierArgument());
379 std::string bindName = cgm.getOpenACCBindMangledName(
380 clause.getIdentifierArgument(), funcDecl);
381
382 routineOp.addBindIDName(
383 builder.getContext(), lastDeviceTypeValues,
384 mlir::SymbolRefAttr::get(builder.getContext(), bindName));
385 }
386 }
387};
388} // namespace
389
391 const clang::FunctionDecl *funcDecl, cir::FuncOp func,
393 mlir::OpBuilder::InsertionGuard guardCase(builder);
394 // These need to appear at the global module.
395 builder.setInsertionPointToEnd(&getModule().getBodyRegion().front());
396
397 mlir::Location routineLoc = getLoc(pragmaLoc);
398
399 std::stringstream routineNameSS;
400 // This follows the same naming format as Flang.
401 routineNameSS << "acc_routine_" << routineCounter++;
402 std::string routineName = routineNameSS.str();
403
404 // There isn't a good constructor for RoutineOp that just takes a location +
405 // name + function, so we use one that creates an otherwise RoutineOp and
406 // count on the visitor/emitter to fill these in.
407 auto routineOp = mlir::acc::RoutineOp::create(
408 builder, routineLoc, routineName,
409 mlir::SymbolRefAttr::get(builder.getContext(), func.getName()),
410 /*implicit=*/false);
411
412 // We have to add a pointer going the other direction via an acc.routine_info,
413 // from the func to the routine.
415 if (auto routineInfo =
416 func.getOperation()->getAttrOfType<mlir::acc::RoutineInfoAttr>(
417 mlir::acc::getRoutineInfoAttrName()))
418 funcRoutines.append(routineInfo.getAccRoutines().begin(),
419 routineInfo.getAccRoutines().end());
420
421 funcRoutines.push_back(
422 mlir::SymbolRefAttr::get(builder.getContext(), routineName));
423 func.getOperation()->setAttr(
424 mlir::acc::getRoutineInfoAttrName(),
425 mlir::acc::RoutineInfoAttr::get(func.getContext(), funcRoutines));
426
427 OpenACCRoutineClauseEmitter emitter{*this, builder, routineOp, funcDecl};
428 emitter.emitClauses(clauses);
429}
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.
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)