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

Follow Lee on X/Twitter - Father, Husband, Serial builder creating AI, crypto, games & web tools. We are friends :) AI Will Come To Life!

Check out: eBank.nz (Art Generator) | Netwrck.com (AI Tools) | Text-Generator.io (AI API) | BitBank.nz (Crypto AI) | ReadingTime (Kids Reading) | RewordGame | BigMultiplayerChess | WebFiddle | How.nz | Helix AI Assistant