| 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 | |
| 18 | using namespace clang; |
| 19 | |
| 20 | OpenACCComputeConstruct * |
| 21 | OpenACCComputeConstruct::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 | |
| 29 | OpenACCComputeConstruct *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 | |
| 41 | OpenACCLoopConstruct::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 | |
| 50 | OpenACCLoopConstruct::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 | |
| 68 | OpenACCLoopConstruct *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 | |
| 77 | OpenACCLoopConstruct *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 | |
| 89 | OpenACCCombinedConstruct * |
| 90 | OpenACCCombinedConstruct::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 | |
| 99 | OpenACCCombinedConstruct *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 | |
| 111 | OpenACCDataConstruct *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 | |
| 120 | OpenACCDataConstruct * |
| 121 | OpenACCDataConstruct::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 | |
| 133 | OpenACCEnterDataConstruct * |
| 134 | OpenACCEnterDataConstruct::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 | |
| 143 | OpenACCEnterDataConstruct *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 | |
| 154 | OpenACCExitDataConstruct * |
| 155 | OpenACCExitDataConstruct::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 | |
| 164 | OpenACCExitDataConstruct *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 | |
| 175 | OpenACCHostDataConstruct * |
| 176 | OpenACCHostDataConstruct::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 | |
| 185 | OpenACCHostDataConstruct *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 | |
| 197 | OpenACCWaitConstruct *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 | |
| 208 | OpenACCWaitConstruct *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 | } |
| 225 | OpenACCInitConstruct *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 | |
| 234 | OpenACCInitConstruct * |
| 235 | OpenACCInitConstruct::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 | } |
| 245 | OpenACCShutdownConstruct * |
| 246 | OpenACCShutdownConstruct::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 | |
| 255 | OpenACCShutdownConstruct *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 | |
| 266 | OpenACCSetConstruct *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 | |
| 274 | OpenACCSetConstruct * |
| 275 | OpenACCSetConstruct::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 | |
| 285 | OpenACCUpdateConstruct * |
| 286 | OpenACCUpdateConstruct::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 | |
| 294 | OpenACCUpdateConstruct * |
| 295 | OpenACCUpdateConstruct::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 | |
| 306 | OpenACCAtomicConstruct * |
| 307 | OpenACCAtomicConstruct::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 | |
| 315 | OpenACCAtomicConstruct *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 | |
| 327 | static std::optional<std::pair<const Expr *, const Expr *>> |
| 328 | getBinaryAssignOpArgs(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 | } |
| 344 | static std::optional<std::pair<const Expr *, const Expr *>> |
| 345 | getBinaryAssignOpArgs(const Expr *Op) { |
| 346 | bool IsCompoundAssign; |
| 347 | return getBinaryAssignOpArgs(Op, IsCompoundAssign); |
| 348 | } |
| 349 | |
| 350 | static std::optional<std::pair<const Expr *, bool>> |
| 351 | getUnaryOpArgs(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. |
| 368 | static std::optional<OpenACCAtomicConstruct::SingleStmtInfo> |
| 369 | getReadStmtInfo(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. |
| 395 | static std::optional<OpenACCAtomicConstruct::SingleStmtInfo> |
| 396 | getWriteStmtInfo(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 | |
| 412 | static std::optional<OpenACCAtomicConstruct::SingleStmtInfo> |
| 413 | getUpdateStmtInfo(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. |
| 462 | static OpenACCAtomicConstruct::StmtInfo |
| 463 | getCaptureStmtInfo(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 | |
| 544 | const OpenACCAtomicConstruct::StmtInfo |
| 545 | OpenACCAtomicConstruct::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 | |
| 578 | OpenACCCacheConstruct *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 | |
| 586 | OpenACCCacheConstruct *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 | |