A result of various cleanups, reimplementations/etc, I ended up with a return after an if/else branch where each returned in #168422. This patch removes the 'else' after a return, and removes the unreachable return.
595 lines
23 KiB
C++
595 lines
23 KiB
C++
//===--- StmtOpenACC.cpp - Classes for OpenACC Constructs -----------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the subclasses of Stmt class declared in StmtOpenACC.h
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/AST/StmtOpenACC.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "clang/AST/StmtCXX.h"
|
|
|
|
using namespace clang;
|
|
|
|
OpenACCComputeConstruct *
|
|
OpenACCComputeConstruct::CreateEmpty(const ASTContext &C, unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCComputeConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCComputeConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCComputeConstruct *OpenACCComputeConstruct::Create(
|
|
const ASTContext &C, OpenACCDirectiveKind K, SourceLocation BeginLoc,
|
|
SourceLocation DirLoc, SourceLocation EndLoc,
|
|
ArrayRef<const OpenACCClause *> Clauses, Stmt *StructuredBlock) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCComputeConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst = new (Mem) OpenACCComputeConstruct(K, BeginLoc, DirLoc, EndLoc,
|
|
Clauses, StructuredBlock);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCLoopConstruct::OpenACCLoopConstruct(unsigned NumClauses)
|
|
: OpenACCAssociatedStmtConstruct(
|
|
OpenACCLoopConstructClass, OpenACCDirectiveKind::Loop,
|
|
SourceLocation{}, SourceLocation{}, SourceLocation{},
|
|
/*AssociatedStmt=*/nullptr) {
|
|
std::uninitialized_value_construct_n(getTrailingObjects(), NumClauses);
|
|
setClauseList(getTrailingObjects(NumClauses));
|
|
}
|
|
|
|
OpenACCLoopConstruct::OpenACCLoopConstruct(
|
|
OpenACCDirectiveKind ParentKind, SourceLocation Start,
|
|
SourceLocation DirLoc, SourceLocation End,
|
|
ArrayRef<const OpenACCClause *> Clauses, Stmt *Loop)
|
|
: OpenACCAssociatedStmtConstruct(OpenACCLoopConstructClass,
|
|
OpenACCDirectiveKind::Loop, Start, DirLoc,
|
|
End, Loop),
|
|
ParentComputeConstructKind(ParentKind) {
|
|
// accept 'nullptr' for the loop. This is diagnosed somewhere, but this gives
|
|
// us some level of AST fidelity in the error case.
|
|
assert((Loop == nullptr || isa<ForStmt, CXXForRangeStmt>(Loop)) &&
|
|
"Associated Loop not a for loop?");
|
|
// Initialize the trailing storage.
|
|
llvm::uninitialized_copy(Clauses, getTrailingObjects());
|
|
|
|
setClauseList(getTrailingObjects(Clauses.size()));
|
|
}
|
|
|
|
OpenACCLoopConstruct *OpenACCLoopConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumClauses) {
|
|
void *Mem =
|
|
C.Allocate(OpenACCLoopConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCLoopConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCLoopConstruct *OpenACCLoopConstruct::Create(
|
|
const ASTContext &C, OpenACCDirectiveKind ParentKind,
|
|
SourceLocation BeginLoc, SourceLocation DirLoc, SourceLocation EndLoc,
|
|
ArrayRef<const OpenACCClause *> Clauses, Stmt *Loop) {
|
|
void *Mem =
|
|
C.Allocate(OpenACCLoopConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst = new (Mem)
|
|
OpenACCLoopConstruct(ParentKind, BeginLoc, DirLoc, EndLoc, Clauses, Loop);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCCombinedConstruct *
|
|
OpenACCCombinedConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCCombinedConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCCombinedConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCCombinedConstruct *OpenACCCombinedConstruct::Create(
|
|
const ASTContext &C, OpenACCDirectiveKind DK, SourceLocation BeginLoc,
|
|
SourceLocation DirLoc, SourceLocation EndLoc,
|
|
ArrayRef<const OpenACCClause *> Clauses, Stmt *Loop) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCCombinedConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst = new (Mem)
|
|
OpenACCCombinedConstruct(DK, BeginLoc, DirLoc, EndLoc, Clauses, Loop);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCDataConstruct *OpenACCDataConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumClauses) {
|
|
void *Mem =
|
|
C.Allocate(OpenACCDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCDataConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCDataConstruct *
|
|
OpenACCDataConstruct::Create(const ASTContext &C, SourceLocation Start,
|
|
SourceLocation DirectiveLoc, SourceLocation End,
|
|
ArrayRef<const OpenACCClause *> Clauses,
|
|
Stmt *StructuredBlock) {
|
|
void *Mem =
|
|
C.Allocate(OpenACCDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst = new (Mem)
|
|
OpenACCDataConstruct(Start, DirectiveLoc, End, Clauses, StructuredBlock);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCEnterDataConstruct *
|
|
OpenACCEnterDataConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCEnterDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCEnterDataConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCEnterDataConstruct *OpenACCEnterDataConstruct::Create(
|
|
const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
|
|
SourceLocation End, ArrayRef<const OpenACCClause *> Clauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCEnterDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst =
|
|
new (Mem) OpenACCEnterDataConstruct(Start, DirectiveLoc, End, Clauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCExitDataConstruct *
|
|
OpenACCExitDataConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCExitDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCExitDataConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCExitDataConstruct *OpenACCExitDataConstruct::Create(
|
|
const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
|
|
SourceLocation End, ArrayRef<const OpenACCClause *> Clauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCExitDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst =
|
|
new (Mem) OpenACCExitDataConstruct(Start, DirectiveLoc, End, Clauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCHostDataConstruct *
|
|
OpenACCHostDataConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCHostDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCHostDataConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCHostDataConstruct *OpenACCHostDataConstruct::Create(
|
|
const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
|
|
SourceLocation End, ArrayRef<const OpenACCClause *> Clauses,
|
|
Stmt *StructuredBlock) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCHostDataConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst = new (Mem) OpenACCHostDataConstruct(Start, DirectiveLoc, End,
|
|
Clauses, StructuredBlock);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCWaitConstruct *OpenACCWaitConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumExprs,
|
|
unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCWaitConstruct::totalSizeToAlloc<Expr *, OpenACCClause *>(
|
|
NumExprs, NumClauses));
|
|
|
|
auto *Inst = new (Mem) OpenACCWaitConstruct(NumExprs, NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCWaitConstruct *OpenACCWaitConstruct::Create(
|
|
const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
|
|
SourceLocation LParenLoc, Expr *DevNumExpr, SourceLocation QueuesLoc,
|
|
ArrayRef<Expr *> QueueIdExprs, SourceLocation RParenLoc, SourceLocation End,
|
|
ArrayRef<const OpenACCClause *> Clauses) {
|
|
|
|
assert(!llvm::is_contained(QueueIdExprs, nullptr));
|
|
|
|
void *Mem = C.Allocate(
|
|
OpenACCWaitConstruct::totalSizeToAlloc<Expr *, OpenACCClause *>(
|
|
QueueIdExprs.size() + 1, Clauses.size()));
|
|
|
|
auto *Inst = new (Mem)
|
|
OpenACCWaitConstruct(Start, DirectiveLoc, LParenLoc, DevNumExpr,
|
|
QueuesLoc, QueueIdExprs, RParenLoc, End, Clauses);
|
|
return Inst;
|
|
}
|
|
OpenACCInitConstruct *OpenACCInitConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumClauses) {
|
|
void *Mem =
|
|
C.Allocate(OpenACCInitConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCInitConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCInitConstruct *
|
|
OpenACCInitConstruct::Create(const ASTContext &C, SourceLocation Start,
|
|
SourceLocation DirectiveLoc, SourceLocation End,
|
|
ArrayRef<const OpenACCClause *> Clauses) {
|
|
void *Mem =
|
|
C.Allocate(OpenACCInitConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst =
|
|
new (Mem) OpenACCInitConstruct(Start, DirectiveLoc, End, Clauses);
|
|
return Inst;
|
|
}
|
|
OpenACCShutdownConstruct *
|
|
OpenACCShutdownConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCShutdownConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCShutdownConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCShutdownConstruct *OpenACCShutdownConstruct::Create(
|
|
const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
|
|
SourceLocation End, ArrayRef<const OpenACCClause *> Clauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCShutdownConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst =
|
|
new (Mem) OpenACCShutdownConstruct(Start, DirectiveLoc, End, Clauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCSetConstruct *OpenACCSetConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCSetConstruct::totalSizeToAlloc<const OpenACCClause *>(NumClauses));
|
|
auto *Inst = new (Mem) OpenACCSetConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCSetConstruct *
|
|
OpenACCSetConstruct::Create(const ASTContext &C, SourceLocation Start,
|
|
SourceLocation DirectiveLoc, SourceLocation End,
|
|
ArrayRef<const OpenACCClause *> Clauses) {
|
|
void *Mem =
|
|
C.Allocate(OpenACCSetConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst = new (Mem) OpenACCSetConstruct(Start, DirectiveLoc, End, Clauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCUpdateConstruct *
|
|
OpenACCUpdateConstruct::CreateEmpty(const ASTContext &C, unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCUpdateConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCUpdateConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCUpdateConstruct *
|
|
OpenACCUpdateConstruct::Create(const ASTContext &C, SourceLocation Start,
|
|
SourceLocation DirectiveLoc, SourceLocation End,
|
|
ArrayRef<const OpenACCClause *> Clauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCUpdateConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst =
|
|
new (Mem) OpenACCUpdateConstruct(Start, DirectiveLoc, End, Clauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCAtomicConstruct *
|
|
OpenACCAtomicConstruct::CreateEmpty(const ASTContext &C, unsigned NumClauses) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCAtomicConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
NumClauses));
|
|
auto *Inst = new (Mem) OpenACCAtomicConstruct(NumClauses);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCAtomicConstruct *OpenACCAtomicConstruct::Create(
|
|
const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
|
|
OpenACCAtomicKind AtKind, SourceLocation End,
|
|
ArrayRef<const OpenACCClause *> Clauses, Stmt *AssociatedStmt) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCAtomicConstruct::totalSizeToAlloc<const OpenACCClause *>(
|
|
Clauses.size()));
|
|
auto *Inst = new (Mem) OpenACCAtomicConstruct(Start, DirectiveLoc, AtKind,
|
|
End, Clauses, AssociatedStmt);
|
|
return Inst;
|
|
}
|
|
|
|
static std::optional<std::pair<const Expr *, const Expr *>>
|
|
getBinaryAssignOpArgs(const Expr *Op, bool &IsCompoundAssign) {
|
|
if (const auto *BO = dyn_cast<BinaryOperator>(Op)) {
|
|
if (!BO->isAssignmentOp())
|
|
return std::nullopt;
|
|
IsCompoundAssign = BO->isCompoundAssignmentOp();
|
|
return std::pair<const Expr *, const Expr *>(BO->getLHS(), BO->getRHS());
|
|
}
|
|
|
|
if (const auto *OO = dyn_cast<CXXOperatorCallExpr>(Op)) {
|
|
if (!OO->isAssignmentOp())
|
|
return std::nullopt;
|
|
IsCompoundAssign = OO->getOperator() != OO_Equal;
|
|
return std::pair<const Expr *, const Expr *>(OO->getArg(0), OO->getArg(1));
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
static std::optional<std::pair<const Expr *, const Expr *>>
|
|
getBinaryAssignOpArgs(const Expr *Op) {
|
|
bool IsCompoundAssign;
|
|
return getBinaryAssignOpArgs(Op, IsCompoundAssign);
|
|
}
|
|
|
|
static std::optional<std::pair<const Expr *, bool>>
|
|
getUnaryOpArgs(const Expr *Op) {
|
|
if (const auto *UO = dyn_cast<UnaryOperator>(Op))
|
|
return {{UO->getSubExpr(), UO->isPostfix()}};
|
|
|
|
if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(Op)) {
|
|
// Post-inc/dec have a second unused argument to differentiate it, so we
|
|
// accept -- or ++ as unary, or any operator call with only 1 arg.
|
|
if (OpCall->getNumArgs() == 1 || OpCall->getOperator() == OO_PlusPlus ||
|
|
OpCall->getOperator() == OO_MinusMinus)
|
|
return {{OpCall->getArg(0), /*IsPostfix=*/OpCall->getNumArgs() == 1}};
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Read is of the form `v = x;`, where both sides are scalar L-values. This is a
|
|
// BinaryOperator or CXXOperatorCallExpr.
|
|
static std::optional<OpenACCAtomicConstruct::SingleStmtInfo>
|
|
getReadStmtInfo(const Expr *E, bool ForAtomicComputeSingleStmt = false) {
|
|
std::optional<std::pair<const Expr *, const Expr *>> BinaryArgs =
|
|
getBinaryAssignOpArgs(E);
|
|
|
|
if (!BinaryArgs)
|
|
return std::nullopt;
|
|
|
|
// We want the L-value for each side, so we ignore implicit casts.
|
|
auto Res = OpenACCAtomicConstruct::SingleStmtInfo::createRead(
|
|
E, BinaryArgs->first->IgnoreImpCasts(),
|
|
BinaryArgs->second->IgnoreImpCasts());
|
|
|
|
// The atomic compute single-stmt variant has to do a 'fixup' step for the 'X'
|
|
// value, since it is dependent on the RHS. So if we're in that version, we
|
|
// skip the checks on X.
|
|
if ((!ForAtomicComputeSingleStmt &&
|
|
(!Res.X->isLValue() || !Res.X->getType()->isScalarType())) ||
|
|
!Res.V->isLValue() || !Res.V->getType()->isScalarType())
|
|
return std::nullopt;
|
|
|
|
return Res;
|
|
}
|
|
|
|
// Write supports only the format 'x = expr', where the expression is scalar
|
|
// type, and 'x' is a scalar l value. As above, this can come in 2 forms;
|
|
// Binary Operator or CXXOperatorCallExpr.
|
|
static std::optional<OpenACCAtomicConstruct::SingleStmtInfo>
|
|
getWriteStmtInfo(const Expr *E) {
|
|
std::optional<std::pair<const Expr *, const Expr *>> BinaryArgs =
|
|
getBinaryAssignOpArgs(E);
|
|
if (!BinaryArgs)
|
|
return std::nullopt;
|
|
// We want the L-value for ONLY the X side, so we ignore implicit casts. For
|
|
// the right side (the expr), we emit it as an r-value so we need to
|
|
// maintain implicit casts.
|
|
auto Res = OpenACCAtomicConstruct::SingleStmtInfo::createWrite(
|
|
E, BinaryArgs->first->IgnoreImpCasts(), BinaryArgs->second);
|
|
|
|
if (!Res.X->isLValue() || !Res.X->getType()->isScalarType())
|
|
return std::nullopt;
|
|
return Res;
|
|
}
|
|
|
|
static std::optional<OpenACCAtomicConstruct::SingleStmtInfo>
|
|
getUpdateStmtInfo(const Expr *E) {
|
|
std::optional<std::pair<const Expr *, bool>> UnaryArgs = getUnaryOpArgs(E);
|
|
if (UnaryArgs) {
|
|
auto Res = OpenACCAtomicConstruct::SingleStmtInfo::createUpdate(
|
|
E, UnaryArgs->first->IgnoreImpCasts(), UnaryArgs->second);
|
|
|
|
if (!Res.X->isLValue() || !Res.X->getType()->isScalarType())
|
|
return std::nullopt;
|
|
|
|
return Res;
|
|
}
|
|
|
|
bool IsRHSCompoundAssign = false;
|
|
std::optional<std::pair<const Expr *, const Expr *>> BinaryArgs =
|
|
getBinaryAssignOpArgs(E, IsRHSCompoundAssign);
|
|
if (!BinaryArgs)
|
|
return std::nullopt;
|
|
|
|
auto Res = OpenACCAtomicConstruct::SingleStmtInfo::createUpdate(
|
|
E, BinaryArgs->first->IgnoreImpCasts(), /*PostFixIncDec=*/false);
|
|
|
|
if (!Res.X->isLValue() || !Res.X->getType()->isScalarType())
|
|
return std::nullopt;
|
|
|
|
// 'update' has to be either a compound-assignment operation, or
|
|
// assignment-to-a-binary-op. Return nullopt if these are not the case.
|
|
// If we are already compound-assign, we're done!
|
|
if (IsRHSCompoundAssign)
|
|
return Res;
|
|
|
|
// else we have to check that we have a binary operator.
|
|
const Expr *RHS = BinaryArgs->second->IgnoreImpCasts();
|
|
|
|
if (isa<BinaryOperator>(RHS)) {
|
|
return Res;
|
|
} else if (const auto *OO = dyn_cast<CXXOperatorCallExpr>(RHS)) {
|
|
if (OO->isInfixBinaryOp())
|
|
return Res;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
/// The statement associated with an atomic capture comes in 1 of two forms: A
|
|
/// compound statement containing two statements, or a single statement. In
|
|
/// either case, the compound/single statement is decomposed into 2 separate
|
|
/// operations, eihter a read/write, read/update, or update/read. This function
|
|
/// figures out that information in the form listed in the standard (filling in
|
|
/// V, X, or Expr) for each of these operations.
|
|
static OpenACCAtomicConstruct::StmtInfo
|
|
getCaptureStmtInfo(const Stmt *AssocStmt) {
|
|
|
|
if (const auto *CmpdStmt = dyn_cast<CompoundStmt>(AssocStmt)) {
|
|
// We checked during Sema to ensure we only have 2 statements here, and
|
|
// that both are expressions, we can look at these to see what the valid
|
|
// options are.
|
|
const Expr *Stmt1 = cast<Expr>(*CmpdStmt->body().begin())->IgnoreImpCasts();
|
|
const Expr *Stmt2 =
|
|
cast<Expr>(*(CmpdStmt->body().begin() + 1))->IgnoreImpCasts();
|
|
|
|
// The compound statement form allows read/write, read/update, or
|
|
// update/read. First we get the information for a 'Read' to see if this is
|
|
// one of the former two.
|
|
std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Read =
|
|
getReadStmtInfo(Stmt1);
|
|
|
|
if (Read) {
|
|
// READ : WRITE
|
|
// v = x; x = expr
|
|
// READ : UPDATE
|
|
// v = x; x binop = expr
|
|
// v = x; x = x binop expr
|
|
// v = x; x = expr binop x
|
|
// v = x; x++
|
|
// v = x; ++x
|
|
// v = x; x--
|
|
// v = x; --x
|
|
std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Update =
|
|
getUpdateStmtInfo(Stmt2);
|
|
// Since we already know the first operation is a read, the second is
|
|
// either an update, which we check, or a write, which we can assume next.
|
|
if (Update)
|
|
return OpenACCAtomicConstruct::StmtInfo::createReadUpdate(*Read,
|
|
*Update);
|
|
|
|
std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Write =
|
|
getWriteStmtInfo(Stmt2);
|
|
return OpenACCAtomicConstruct::StmtInfo::createReadWrite(*Read, *Write);
|
|
}
|
|
// UPDATE: READ
|
|
// x binop = expr; v = x
|
|
// x = x binop expr; v = x
|
|
// x = expr binop x ; v = x
|
|
// ++ x; v = x
|
|
// x++; v = x
|
|
// --x; v = x
|
|
// x--; v = x
|
|
// Otherwise, it is one of the above forms for update/read.
|
|
std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Update =
|
|
getUpdateStmtInfo(Stmt1);
|
|
Read = getReadStmtInfo(Stmt2);
|
|
|
|
return OpenACCAtomicConstruct::StmtInfo::createUpdateRead(*Update, *Read);
|
|
}
|
|
|
|
// All of the forms that can be done in a single line fall into 2
|
|
// categories: update/read, or read/update. The special cases are the
|
|
// postfix unary operators, which we have to make sure we do the 'read'
|
|
// first. However, we still parse these as the RHS first, so we have a
|
|
// 'reversing' step. READ: UPDATE v = x++; v = x--; UPDATE: READ v = ++x; v
|
|
// = --x; v = x binop=expr v = x = x binop expr v = x = expr binop x
|
|
|
|
const Expr *E = cast<const Expr>(AssocStmt);
|
|
|
|
std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Read =
|
|
getReadStmtInfo(E, /*ForAtomicComputeSingleStmt=*/true);
|
|
std::optional<OpenACCAtomicConstruct::SingleStmtInfo> Update =
|
|
getUpdateStmtInfo(Read->X);
|
|
|
|
// Fixup this, since the 'X' for the read is the result after write, but is
|
|
// the same value as the LHS-most variable of the update(its X).
|
|
Read->X = Update->X;
|
|
|
|
// Postfix is a read FIRST, then an update.
|
|
if (Update->IsPostfixIncDec)
|
|
return OpenACCAtomicConstruct::StmtInfo::createReadUpdate(*Read, *Update);
|
|
|
|
return OpenACCAtomicConstruct::StmtInfo::createUpdateRead(*Update, *Read);
|
|
}
|
|
|
|
const OpenACCAtomicConstruct::StmtInfo
|
|
OpenACCAtomicConstruct::getAssociatedStmtInfo() const {
|
|
// This ends up being a vastly simplified version of SemaOpenACCAtomic, since
|
|
// it doesn't have to worry about erroring out, but we should do a lot of
|
|
// asserts to ensure we don't get off into the weeds.
|
|
assert(getAssociatedStmt() && "invalid associated stmt?");
|
|
|
|
switch (AtomicKind) {
|
|
case OpenACCAtomicKind::Read:
|
|
return OpenACCAtomicConstruct::StmtInfo{
|
|
OpenACCAtomicConstruct::StmtInfo::StmtForm::Read,
|
|
*getReadStmtInfo(cast<const Expr>(getAssociatedStmt())),
|
|
OpenACCAtomicConstruct::SingleStmtInfo::Empty()};
|
|
|
|
case OpenACCAtomicKind::Write:
|
|
return OpenACCAtomicConstruct::StmtInfo{
|
|
OpenACCAtomicConstruct::StmtInfo::StmtForm::Write,
|
|
*getWriteStmtInfo(cast<const Expr>(getAssociatedStmt())),
|
|
OpenACCAtomicConstruct::SingleStmtInfo::Empty()};
|
|
|
|
case OpenACCAtomicKind::None:
|
|
case OpenACCAtomicKind::Update:
|
|
return OpenACCAtomicConstruct::StmtInfo{
|
|
OpenACCAtomicConstruct::StmtInfo::StmtForm::Update,
|
|
*getUpdateStmtInfo(cast<const Expr>(getAssociatedStmt())),
|
|
OpenACCAtomicConstruct::SingleStmtInfo::Empty()};
|
|
|
|
case OpenACCAtomicKind::Capture:
|
|
return getCaptureStmtInfo(getAssociatedStmt());
|
|
}
|
|
|
|
llvm_unreachable("unknown OpenACC atomic kind");
|
|
}
|
|
|
|
OpenACCCacheConstruct *OpenACCCacheConstruct::CreateEmpty(const ASTContext &C,
|
|
unsigned NumVars) {
|
|
void *Mem =
|
|
C.Allocate(OpenACCCacheConstruct::totalSizeToAlloc<Expr *>(NumVars));
|
|
auto *Inst = new (Mem) OpenACCCacheConstruct(NumVars);
|
|
return Inst;
|
|
}
|
|
|
|
OpenACCCacheConstruct *OpenACCCacheConstruct::Create(
|
|
const ASTContext &C, SourceLocation Start, SourceLocation DirectiveLoc,
|
|
SourceLocation LParenLoc, SourceLocation ReadOnlyLoc,
|
|
ArrayRef<Expr *> VarList, SourceLocation RParenLoc, SourceLocation End) {
|
|
void *Mem = C.Allocate(
|
|
OpenACCCacheConstruct::totalSizeToAlloc<Expr *>(VarList.size()));
|
|
auto *Inst = new (Mem) OpenACCCacheConstruct(
|
|
Start, DirectiveLoc, LParenLoc, ReadOnlyLoc, VarList, RParenLoc, End);
|
|
return Inst;
|
|
}
|