1//===--- StmtOpenACC.cpp - Classes for OpenACC Constructs -----------------===//
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 file implements the subclasses of Stmt class declared in StmtOpenACC.h
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/AST/StmtOpenACC.h"
14#include "clang/AST/ASTContext.h"
15#include "clang/AST/ExprCXX.h"
16#include "clang/AST/StmtCXX.h"
17
18using namespace clang;
19
20OpenACCComputeConstruct *
21OpenACCComputeConstruct::CreateEmpty(const ASTContext &C, unsigned NumClauses) {
22 void *Mem = C.Allocate(
23 Size: OpenACCComputeConstruct::totalSizeToAlloc<const OpenACCClause *>(
24 Counts: NumClauses));
25 auto *Inst = new (Mem) OpenACCComputeConstruct(NumClauses);
26 return Inst;
27}
28
29OpenACCComputeConstruct *OpenACCComputeConstruct::Create(
30 const ASTContext &C, OpenACCDirectiveKind K, SourceLocation BeginLoc,
31 SourceLocation DirLoc, SourceLocation EndLoc,
32 ArrayRef<const OpenACCClause *> Clauses, Stmt *StructuredBlock) {
33 void *Mem = C.Allocate(
34 Size: OpenACCComputeConstruct::totalSizeToAlloc<const OpenACCClause *>(
35 Counts: Clauses.size()));
36 auto *Inst = new (Mem) OpenACCComputeConstruct(K, BeginLoc, DirLoc, EndLoc,
37 Clauses, StructuredBlock);
38 return Inst;
39}
40
41OpenACCLoopConstruct::OpenACCLoopConstruct(unsigned NumClauses)
42 : OpenACCAssociatedStmtConstruct(
43 OpenACCLoopConstructClass, OpenACCDirectiveKind::Loop,
44 SourceLocation{}, SourceLocation{}, SourceLocation{},
45 /*AssociatedStmt=*/nullptr) {
46 std::uninitialized_value_construct_n(first: getTrailingObjects(), count: NumClauses);
47 setClauseList(getTrailingObjects(N: NumClauses));
48}
49
50OpenACCLoopConstruct::OpenACCLoopConstruct(
51 OpenACCDirectiveKind ParentKind, SourceLocation Start,
52 SourceLocation DirLoc, SourceLocation End,
53 ArrayRef<const OpenACCClause *> Clauses, Stmt *Loop)
54 : OpenACCAssociatedStmtConstruct(OpenACCLoopConstructClass,
55 OpenACCDirectiveKind::Loop, Start, DirLoc,
56 End, Loop),
57 ParentComputeConstructKind(ParentKind) {
58 // accept 'nullptr' for the loop. This is diagnosed somewhere, but this gives
59 // us some level of AST fidelity in the error case.
60 assert((Loop == nullptr || isa<ForStmt, CXXForRangeStmt>(Loop)) &&
61 "Associated Loop not a for loop?");
62 // Initialize the trailing storage.
63 llvm::uninitialized_copy(Src&: Clauses, Dst: getTrailingObjects());
64
65 setClauseList(getTrailingObjects(N: Clauses.size()));
66}
67
68OpenACCLoopConstruct *OpenACCLoopConstruct::CreateEmpty(const ASTContext &C,
69 unsigned NumClauses) {
70 void *Mem =
71 C.Allocate(Size: OpenACCLoopConstruct::totalSizeToAlloc<const OpenACCClause *>(
72 Counts: NumClauses));
73 auto *Inst = new (Mem) OpenACCLoopConstruct(NumClauses);
74 return Inst;
75}
76
77OpenACCLoopConstruct *OpenACCLoopConstruct::Create(
78 const ASTContext &C, OpenACCDirectiveKind ParentKind,
79 SourceLocation BeginLoc, SourceLocation DirLoc, SourceLocation EndLoc,
80 ArrayRef<const OpenACCClause *> Clauses, Stmt *Loop) {
81 void *Mem =
82 C.Allocate(Size: OpenACCLoopConstruct::totalSizeToAlloc<const OpenACCClause *>(
83 Counts: Clauses.size()));
84 auto *Inst = new (Mem)
85 OpenACCLoopConstruct(ParentKind, BeginLoc, DirLoc, EndLoc, Clauses, Loop);
86 return Inst;
87}
88
89OpenACCCombinedConstruct *
90OpenACCCombinedConstruct::CreateEmpty(const ASTContext &C,
91 unsigned NumClauses) {
92 void *Mem = C.Allocate(
93 Size: OpenACCCombinedConstruct::totalSizeToAlloc<const OpenACCClause *>(
94 Counts: NumClauses));
95 auto *Inst = new (Mem) OpenACCCombinedConstruct(NumClauses);
96 return Inst;
97}
98
99OpenACCCombinedConstruct *OpenACCCombinedConstruct::Create(
100 const ASTContext &C, OpenACCDirectiveKind DK, SourceLocation BeginLoc,
101 SourceLocation DirLoc, SourceLocation EndLoc,
102 ArrayRef<const OpenACCClause *> Clauses, Stmt *Loop) {
103 void *Mem = C.Allocate(
104 Size: OpenACCCombinedConstruct::totalSizeToAlloc<const OpenACCClause *>(
105 Counts: Clauses.size()));
106 auto *Inst = new (Mem)
107 OpenACCCombinedConstruct(DK, BeginLoc, DirLoc, EndLoc, Clauses, Loop);
108 return Inst;
109}
110
111OpenACCDataConstruct *OpenACCDataConstruct::CreateEmpty(const ASTContext &C,
112 unsigned NumClauses) {
113 void *Mem =
114 C.Allocate(Size: OpenACCDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
115 Counts: NumClauses));
116 auto *Inst = new (Mem) OpenACCDataConstruct(NumClauses);
117 return Inst;
118}
119
120OpenACCDataConstruct *
121OpenACCDataConstruct::Create(const ASTContext &C, SourceLocation Start,
122 SourceLocation DirectiveLoc, SourceLocation End,
123 ArrayRef<const OpenACCClause *> Clauses,
124 Stmt *StructuredBlock) {
125 void *Mem =
126 C.Allocate(Size: OpenACCDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
127 Counts: Clauses.size()));
128 auto *Inst = new (Mem)
129 OpenACCDataConstruct(Start, DirectiveLoc, End, Clauses, StructuredBlock);
130 return Inst;
131}
132
133OpenACCEnterDataConstruct *
134OpenACCEnterDataConstruct::CreateEmpty(const ASTContext &C,
135 unsigned NumClauses) {
136 void *Mem = C.Allocate(
137 Size: OpenACCEnterDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
138 Counts: NumClauses));
139 auto *Inst = new (Mem) OpenACCEnterDataConstruct(NumClauses);
140 return Inst;
141}
142
143OpenACCEnterDataConstruct *OpenACCEnterDataConstruct::Create(
144 const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
145 SourceLocation End, ArrayRef<const OpenACCClause *> Clauses) {
146 void *Mem = C.Allocate(
147 Size: OpenACCEnterDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
148 Counts: Clauses.size()));
149 auto *Inst =
150 new (Mem) OpenACCEnterDataConstruct(Start, DirectiveLoc, End, Clauses);
151 return Inst;
152}
153
154OpenACCExitDataConstruct *
155OpenACCExitDataConstruct::CreateEmpty(const ASTContext &C,
156 unsigned NumClauses) {
157 void *Mem = C.Allocate(
158 Size: OpenACCExitDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
159 Counts: NumClauses));
160 auto *Inst = new (Mem) OpenACCExitDataConstruct(NumClauses);
161 return Inst;
162}
163
164OpenACCExitDataConstruct *OpenACCExitDataConstruct::Create(
165 const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
166 SourceLocation End, ArrayRef<const OpenACCClause *> Clauses) {
167 void *Mem = C.Allocate(
168 Size: OpenACCExitDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
169 Counts: Clauses.size()));
170 auto *Inst =
171 new (Mem) OpenACCExitDataConstruct(Start, DirectiveLoc, End, Clauses);
172 return Inst;
173}
174
175OpenACCHostDataConstruct *
176OpenACCHostDataConstruct::CreateEmpty(const ASTContext &C,
177 unsigned NumClauses) {
178 void *Mem = C.Allocate(
179 Size: OpenACCHostDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
180 Counts: NumClauses));
181 auto *Inst = new (Mem) OpenACCHostDataConstruct(NumClauses);
182 return Inst;
183}
184
185OpenACCHostDataConstruct *OpenACCHostDataConstruct::Create(
186 const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
187 SourceLocation End, ArrayRef<const OpenACCClause *> Clauses,
188 Stmt *StructuredBlock) {
189 void *Mem = C.Allocate(
190 Size: OpenACCHostDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
191 Counts: Clauses.size()));
192 auto *Inst = new (Mem) OpenACCHostDataConstruct(Start, DirectiveLoc, End,
193 Clauses, StructuredBlock);
194 return Inst;
195}
196
197OpenACCWaitConstruct *OpenACCWaitConstruct::CreateEmpty(const ASTContext &C,
198 unsigned NumExprs,
199 unsigned NumClauses) {
200 void *Mem = C.Allocate(
201 Size: OpenACCWaitConstruct::totalSizeToAlloc<Expr *, OpenACCClause *>(
202 Counts: NumExprs, Counts: NumClauses));
203
204 auto *Inst = new (Mem) OpenACCWaitConstruct(NumExprs, NumClauses);
205 return Inst;
206}
207
208OpenACCWaitConstruct *OpenACCWaitConstruct::Create(
209 const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
210 SourceLocation LParenLoc, Expr *DevNumExpr, SourceLocation QueuesLoc,
211 ArrayRef<Expr *> QueueIdExprs, SourceLocation RParenLoc, SourceLocation End,
212 ArrayRef<const OpenACCClause *> Clauses) {
213
214 assert(!llvm::is_contained(QueueIdExprs, nullptr));
215
216 void *Mem = C.Allocate(
217 Size: OpenACCWaitConstruct::totalSizeToAlloc<Expr *, OpenACCClause *>(
218 Counts: QueueIdExprs.size() + 1, Counts: Clauses.size()));
219
220 auto *Inst = new (Mem)
221 OpenACCWaitConstruct(Start, DirectiveLoc, LParenLoc, DevNumExpr,
222 QueuesLoc, QueueIdExprs, RParenLoc, End, Clauses);
223 return Inst;
224}
225OpenACCInitConstruct *OpenACCInitConstruct::CreateEmpty(const ASTContext &C,
226 unsigned NumClauses) {
227 void *Mem =
228 C.Allocate(Size: OpenACCInitConstruct::totalSizeToAlloc<const OpenACCClause *>(
229 Counts: NumClauses));
230 auto *Inst = new (Mem) OpenACCInitConstruct(NumClauses);
231 return Inst;
232}
233
234OpenACCInitConstruct *
235OpenACCInitConstruct::Create(const ASTContext &C, SourceLocation Start,
236 SourceLocation DirectiveLoc, SourceLocation End,
237 ArrayRef<const OpenACCClause *> Clauses) {
238 void *Mem =
239 C.Allocate(Size: OpenACCInitConstruct::totalSizeToAlloc<const OpenACCClause *>(
240 Counts: Clauses.size()));
241 auto *Inst =
242 new (Mem) OpenACCInitConstruct(Start, DirectiveLoc, End, Clauses);
243 return Inst;
244}
245OpenACCShutdownConstruct *
246OpenACCShutdownConstruct::CreateEmpty(const ASTContext &C,
247 unsigned NumClauses) {
248 void *Mem = C.Allocate(
249 Size: OpenACCShutdownConstruct::totalSizeToAlloc<const OpenACCClause *>(
250 Counts: NumClauses));
251 auto *Inst = new (Mem) OpenACCShutdownConstruct(NumClauses);
252 return Inst;
253}
254
255OpenACCShutdownConstruct *OpenACCShutdownConstruct::Create(
256 const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
257 SourceLocation End, ArrayRef<const OpenACCClause *> Clauses) {
258 void *Mem = C.Allocate(
259 Size: OpenACCShutdownConstruct::totalSizeToAlloc<const OpenACCClause *>(
260 Counts: Clauses.size()));
261 auto *Inst =
262 new (Mem) OpenACCShutdownConstruct(Start, DirectiveLoc, End, Clauses);
263 return Inst;
264}
265
266OpenACCSetConstruct *OpenACCSetConstruct::CreateEmpty(const ASTContext &C,
267 unsigned NumClauses) {
268 void *Mem = C.Allocate(
269 Size: OpenACCSetConstruct::totalSizeToAlloc<const OpenACCClause *>(Counts: NumClauses));
270 auto *Inst = new (Mem) OpenACCSetConstruct(NumClauses);
271 return Inst;
272}
273
274OpenACCSetConstruct *
275OpenACCSetConstruct::Create(const ASTContext &C, SourceLocation Start,
276 SourceLocation DirectiveLoc, SourceLocation End,
277 ArrayRef<const OpenACCClause *> Clauses) {
278 void *Mem =
279 C.Allocate(Size: OpenACCSetConstruct::totalSizeToAlloc<const OpenACCClause *>(
280 Counts: Clauses.size()));
281 auto *Inst = new (Mem) OpenACCSetConstruct(Start, DirectiveLoc, End, Clauses);
282 return Inst;
283}
284
285OpenACCUpdateConstruct *
286OpenACCUpdateConstruct::CreateEmpty(const ASTContext &C, unsigned NumClauses) {
287 void *Mem = C.Allocate(
288 Size: OpenACCUpdateConstruct::totalSizeToAlloc<const OpenACCClause *>(
289 Counts: NumClauses));
290 auto *Inst = new (Mem) OpenACCUpdateConstruct(NumClauses);
291 return Inst;
292}
293
294OpenACCUpdateConstruct *
295OpenACCUpdateConstruct::Create(const ASTContext &C, SourceLocation Start,
296 SourceLocation DirectiveLoc, SourceLocation End,
297 ArrayRef<const OpenACCClause *> Clauses) {
298 void *Mem = C.Allocate(
299 Size: OpenACCUpdateConstruct::totalSizeToAlloc<const OpenACCClause *>(
300 Counts: Clauses.size()));
301 auto *Inst =
302 new (Mem) OpenACCUpdateConstruct(Start, DirectiveLoc, End, Clauses);
303 return Inst;
304}
305
306OpenACCAtomicConstruct *
307OpenACCAtomicConstruct::CreateEmpty(const ASTContext &C, unsigned NumClauses) {
308 void *Mem = C.Allocate(
309 Size: OpenACCAtomicConstruct::totalSizeToAlloc<const OpenACCClause *>(
310 Counts: NumClauses));
311 auto *Inst = new (Mem) OpenACCAtomicConstruct(NumClauses);
312 return Inst;
313}
314
315OpenACCAtomicConstruct *OpenACCAtomicConstruct::Create(
316 const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
317 OpenACCAtomicKind AtKind, SourceLocation End,
318 ArrayRef<const OpenACCClause *> Clauses, Stmt *AssociatedStmt) {
319 void *Mem = C.Allocate(
320 Size: OpenACCAtomicConstruct::totalSizeToAlloc<const OpenACCClause *>(
321 Counts: Clauses.size()));
322 auto *Inst = new (Mem) OpenACCAtomicConstruct(Start, DirectiveLoc, AtKind,
323 End, Clauses, AssociatedStmt);
324 return Inst;
325}
326
327static std::optional<std::pair<const Expr *, const Expr *>>
328getBinaryAssignOpArgs(const Expr *Op, bool &IsCompoundAssign) {
329 if (const auto *BO = dyn_cast<BinaryOperator>(Val: Op)) {
330 if (!BO->isAssignmentOp())
331 return std::nullopt;
332 IsCompoundAssign = BO->isCompoundAssignmentOp();
333 return std::pair<const Expr *, const Expr *>(BO->getLHS(), BO->getRHS());
334 }
335
336 if (const auto *OO = dyn_cast<CXXOperatorCallExpr>(Val: Op)) {
337 if (!OO->isAssignmentOp())
338 return std::nullopt;
339 IsCompoundAssign = OO->getOperator() != OO_Equal;
340 return std::pair<const Expr *, const Expr *>(OO->getArg(Arg: 0), OO->getArg(Arg: 1));
341 }
342 return std::nullopt;
343}
344static std::optional<std::pair<const Expr *, const Expr *>>
345getBinaryAssignOpArgs(const Expr *Op) {
346 bool IsCompoundAssign;
347 return getBinaryAssignOpArgs(Op, IsCompoundAssign);
348}
349
350static std::optional<std::pair<const Expr *, bool>>
351getUnaryOpArgs(const Expr *Op) {
352 if (const auto *UO = dyn_cast<UnaryOperator>(Val: Op))
353 return {{UO->getSubExpr(), UO->isPostfix()}};
354
355 if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(Val: Op)) {
356 // Post-inc/dec have a second unused argument to differentiate it, so we
357 // accept -- or ++ as unary, or any operator call with only 1 arg.
358 if (OpCall->getNumArgs() == 1 || OpCall->getOperator() == OO_PlusPlus ||
359 OpCall->getOperator() == OO_MinusMinus)
360 return {{OpCall->getArg(Arg: 0), /*IsPostfix=*/OpCall->getNumArgs() == 1}};
361 }
362
363 return std::nullopt;
364}
365
366// Read is of the form `v = x;`, where both sides are scalar L-values. This is a
367// BinaryOperator or CXXOperatorCallExpr.
368static std::optional<OpenACCAtomicConstruct::SingleStmtInfo>
369getReadStmtInfo(const Expr *E, bool ForAtomicComputeSingleStmt = false) {
370 std::optional<std::pair<const Expr *, const Expr *>> BinaryArgs =
371 getBinaryAssignOpArgs(Op: E);
372
373 if (!BinaryArgs)
374 return std::nullopt;
375
376 // We want the L-value for each side, so we ignore implicit casts.
377 auto Res = OpenACCAtomicConstruct::SingleStmtInfo::createRead(
378 WholeExpr: E, V: BinaryArgs->first->IgnoreImpCasts(),
379 X: BinaryArgs->second->IgnoreImpCasts());
380
381 // The atomic compute single-stmt variant has to do a 'fixup' step for the 'X'
382 // value, since it is dependent on the RHS. So if we're in that version, we
383 // skip the checks on X.
384 if ((!ForAtomicComputeSingleStmt &&
385 (!Res.X->isLValue() || !Res.X->getType()->isScalarType())) ||
386 !Res.V->isLValue() || !Res.V->getType()->isScalarType())
387 return std::nullopt;
388
389 return Res;
390}
391
392// Write supports only the format 'x = expr', where the expression is scalar
393// type, and 'x' is a scalar l value. As above, this can come in 2 forms;
394// Binary Operator or CXXOperatorCallExpr.
395static std::optional<OpenACCAtomicConstruct::SingleStmtInfo>
396getWriteStmtInfo(const Expr *E) {
397 std::optional<std::pair<const Expr *, const Expr *>> BinaryArgs =
398 getBinaryAssignOpArgs(Op: E);
399 if (!BinaryArgs)
400 return std::nullopt;
401 // We want the L-value for ONLY the X side, so we ignore implicit casts. For
402 // the right side (the expr), we emit it as an r-value so we need to
403 // maintain implicit casts.
404 auto Res = OpenACCAtomicConstruct::SingleStmtInfo::createWrite(
405 WholeExpr: E, X: BinaryArgs->first->IgnoreImpCasts(), RefExpr: BinaryArgs->second);
406
407 if (!Res.X->isLValue() || !Res.X->getType()->isScalarType())
408 return std::nullopt;
409 return Res;
410}
411
412static std::optional<OpenACCAtomicConstruct::SingleStmtInfo>
413getUpdateStmtInfo(const Expr *E) {
414 std::optional<std::pair<const Expr *, bool>> UnaryArgs = getUnaryOpArgs(Op: E);
415 if (UnaryArgs) {
416 auto Res = OpenACCAtomicConstruct::SingleStmtInfo::createUpdate(
417 WholeExpr: E, X: UnaryArgs->first->IgnoreImpCasts(), PostfixIncDec: UnaryArgs->second);
418
419 if (!Res.X->isLValue() || !Res.X->getType()->isScalarType())
420 return std::nullopt;
421
422 return Res;
423 }
424
425 bool IsRHSCompoundAssign = false;
426 std::optional<std::pair<const Expr *, const Expr *>> BinaryArgs =
427 getBinaryAssignOpArgs(Op: E, IsCompoundAssign&: IsRHSCompoundAssign);
428 if (!BinaryArgs)
429 return std::nullopt;
430
431 auto Res = OpenACCAtomicConstruct::SingleStmtInfo::createUpdate(
432 WholeExpr: E, X: BinaryArgs->first->IgnoreImpCasts(), /*PostFixIncDec=*/PostfixIncDec: false);
433
434 if (!Res.X->isLValue() || !Res.X->getType()->isScalarType())
435 return std::nullopt;
436
437 // 'update' has to be either a compound-assignment operation, or
438 // assignment-to-a-binary-op. Return nullopt if these are not the case.
439 // If we are already compound-assign, we're done!
440 if (IsRHSCompoundAssign)
441 return Res;
442
443 // else we have to check that we have a binary operator.
444 const Expr *RHS = BinaryArgs->second->IgnoreImpCasts();
445
446 if (isa<BinaryOperator>(Val: RHS)) {
447 return Res;
448 } else if (const auto *OO = dyn_cast<CXXOperatorCallExpr>(Val: RHS)) {
449 if (OO->isInfixBinaryOp())
450 return Res;
451 }
452
453 return std::nullopt;
454}
455
456/// The statement associated with an atomic capture comes in 1 of two forms: A
457/// compound statement containing two statements, or a single statement. In
458/// either case, the compound/single statement is decomposed into 2 separate
459/// operations, eihter a read/write, read/update, or update/read. This function
460/// figures out that information in the form listed in the standard (filling in
461/// V, X, or Expr) for each of these operations.
462static OpenACCAtomicConstruct::StmtInfo
463getCaptureStmtInfo(const Stmt *AssocStmt) {
464
465 if (const auto *CmpdStmt = dyn_cast<CompoundStmt>(Val: AssocStmt)) {
466 // We checked during Sema to ensure we only have 2 statements here, and
467 // that both are expressions, we can look at these to see what the valid
468 // options are.
469 const Expr *Stmt1 = cast<Expr>(Val: *CmpdStmt->body().begin())->IgnoreImpCasts();
470 const Expr *Stmt2 =
471 cast<Expr>(Val: *(CmpdStmt->body().begin() + 1))->IgnoreImpCasts();
472
473 // The compound statement form allows read/write, read/update, or
474 // update/read. First we get the information for a 'Read' to see if this is
475 // one of the former two.
476 std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Read =
477 getReadStmtInfo(E: Stmt1);
478
479 if (Read) {
480 // READ : WRITE
481 // v = x; x = expr
482 // READ : UPDATE
483 // v = x; x binop = expr
484 // v = x; x = x binop expr
485 // v = x; x = expr binop x
486 // v = x; x++
487 // v = x; ++x
488 // v = x; x--
489 // v = x; --x
490 std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Update =
491 getUpdateStmtInfo(E: Stmt2);
492 // Since we already know the first operation is a read, the second is
493 // either an update, which we check, or a write, which we can assume next.
494 if (Update)
495 return OpenACCAtomicConstruct::StmtInfo::createReadUpdate(First: *Read,
496 Second: *Update);
497
498 std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Write =
499 getWriteStmtInfo(E: Stmt2);
500 return OpenACCAtomicConstruct::StmtInfo::createReadWrite(First: *Read, Second: *Write);
501 }
502 // UPDATE: READ
503 // x binop = expr; v = x
504 // x = x binop expr; v = x
505 // x = expr binop x ; v = x
506 // ++ x; v = x
507 // x++; v = x
508 // --x; v = x
509 // x--; v = x
510 // Otherwise, it is one of the above forms for update/read.
511 std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Update =
512 getUpdateStmtInfo(E: Stmt1);
513 Read = getReadStmtInfo(E: Stmt2);
514
515 return OpenACCAtomicConstruct::StmtInfo::createUpdateRead(First: *Update, Second: *Read);
516 } else {
517 // All of the forms that can be done in a single line fall into 2
518 // categories: update/read, or read/update. The special cases are the
519 // postfix unary operators, which we have to make sure we do the 'read'
520 // first. However, we still parse these as the RHS first, so we have a
521 // 'reversing' step. READ: UPDATE v = x++; v = x--; UPDATE: READ v = ++x; v
522 // = --x; v = x binop=expr v = x = x binop expr v = x = expr binop x
523
524 const Expr *E = cast<const Expr>(Val: AssocStmt);
525
526 std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Read =
527 getReadStmtInfo(E, /*ForAtomicComputeSingleStmt=*/true);
528 std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Update =
529 getUpdateStmtInfo(E: Read->X);
530
531 // Fixup this, since the 'X' for the read is the result after write, but is
532 // the same value as the LHS-most variable of the update(its X).
533 Read->X = Update->X;
534
535 // Postfix is a read FIRST, then an update.
536 if (Update->IsPostfixIncDec)
537 return OpenACCAtomicConstruct::StmtInfo::createReadUpdate(First: *Read, Second: *Update);
538
539 return OpenACCAtomicConstruct::StmtInfo::createUpdateRead(First: *Update, Second: *Read);
540 }
541 return {};
542}
543
544const OpenACCAtomicConstruct::StmtInfo
545OpenACCAtomicConstruct::getAssociatedStmtInfo() const {
546 // This ends up being a vastly simplified version of SemaOpenACCAtomic, since
547 // it doesn't have to worry about erroring out, but we should do a lot of
548 // asserts to ensure we don't get off into the weeds.
549 assert(getAssociatedStmt() && "invalid associated stmt?");
550
551 switch (AtomicKind) {
552 case OpenACCAtomicKind::Read:
553 return OpenACCAtomicConstruct::StmtInfo{
554 .Form: OpenACCAtomicConstruct::StmtInfo::StmtForm::Read,
555 .First: *getReadStmtInfo(E: cast<const Expr>(Val: getAssociatedStmt())),
556 .Second: OpenACCAtomicConstruct::SingleStmtInfo::Empty()};
557
558 case OpenACCAtomicKind::Write:
559 return OpenACCAtomicConstruct::StmtInfo{
560 .Form: OpenACCAtomicConstruct::StmtInfo::StmtForm::Write,
561 .First: *getWriteStmtInfo(E: cast<const Expr>(Val: getAssociatedStmt())),
562 .Second: OpenACCAtomicConstruct::SingleStmtInfo::Empty()};
563
564 case OpenACCAtomicKind::None:
565 case OpenACCAtomicKind::Update:
566 return OpenACCAtomicConstruct::StmtInfo{
567 .Form: OpenACCAtomicConstruct::StmtInfo::StmtForm::Update,
568 .First: *getUpdateStmtInfo(E: cast<const Expr>(Val: getAssociatedStmt())),
569 .Second: OpenACCAtomicConstruct::SingleStmtInfo::Empty()};
570
571 case OpenACCAtomicKind::Capture:
572 return getCaptureStmtInfo(AssocStmt: getAssociatedStmt());
573 }
574
575 llvm_unreachable("unknown OpenACC atomic kind");
576}
577
578OpenACCCacheConstruct *OpenACCCacheConstruct::CreateEmpty(const ASTContext &C,
579 unsigned NumVars) {
580 void *Mem =
581 C.Allocate(Size: OpenACCCacheConstruct::totalSizeToAlloc<Expr *>(Counts: NumVars));
582 auto *Inst = new (Mem) OpenACCCacheConstruct(NumVars);
583 return Inst;
584}
585
586OpenACCCacheConstruct *OpenACCCacheConstruct::Create(
587 const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
588 SourceLocation LParenLoc, SourceLocation ReadOnlyLoc,
589 ArrayRef<Expr *> VarList, SourceLocation RParenLoc, SourceLocation End) {
590 void *Mem = C.Allocate(
591 Size: OpenACCCacheConstruct::totalSizeToAlloc<Expr *>(Counts: VarList.size()));
592 auto *Inst = new (Mem) OpenACCCacheConstruct(
593 Start, DirectiveLoc, LParenLoc, ReadOnlyLoc, VarList, RParenLoc, End);
594 return Inst;
595}
596