forked from OSchip/llvm-project
[OPENMP] Additional processing of 'omp atomic read' directive.
According to OpenMP standard, Section 2.12.6, atomic Construct, '#pragma omp atomic read' is allowed to be used only for expression statements of form 'v = x;', where x and v (as applicable) are both l-value expressions with scalar type. Patch adds checks for it. llvm-svn: 222231
This commit is contained in:
parent
6fdb6b8fd4
commit
62cec44ca4
|
@ -1439,7 +1439,7 @@ class OMPAtomicDirective : public OMPExecutableDirective {
|
|||
OMPAtomicDirective(SourceLocation StartLoc, SourceLocation EndLoc,
|
||||
unsigned NumClauses)
|
||||
: OMPExecutableDirective(this, OMPAtomicDirectiveClass, OMPD_atomic,
|
||||
StartLoc, EndLoc, NumClauses, 1) {}
|
||||
StartLoc, EndLoc, NumClauses, 4) {}
|
||||
|
||||
/// \brief Build an empty directive.
|
||||
///
|
||||
|
@ -1448,20 +1448,33 @@ class OMPAtomicDirective : public OMPExecutableDirective {
|
|||
explicit OMPAtomicDirective(unsigned NumClauses)
|
||||
: OMPExecutableDirective(this, OMPAtomicDirectiveClass, OMPD_atomic,
|
||||
SourceLocation(), SourceLocation(), NumClauses,
|
||||
1) {}
|
||||
4) {}
|
||||
|
||||
/// \brief Set 'x' part of the associated expression/statement.
|
||||
void setX(Expr *X) { *std::next(child_begin()) = X; }
|
||||
/// \brief Set 'v' part of the associated expression/statement.
|
||||
void setV(Expr *V) { *std::next(child_begin(), 2) = V; }
|
||||
/// \brief Set 'expr' part of the associated expression/statement.
|
||||
void setExpr(Expr *E) { *std::next(child_begin(), 3) = E; }
|
||||
|
||||
public:
|
||||
/// \brief Creates directive with a list of \a Clauses.
|
||||
/// \brief Creates directive with a list of \a Clauses and 'x', 'v' and 'expr'
|
||||
/// parts of the atomic construct (see Section 2.12.6, atomic Construct, for
|
||||
/// detailed description of 'x', 'v' and 'expr').
|
||||
///
|
||||
/// \param C AST context.
|
||||
/// \param StartLoc Starting location of the directive kind.
|
||||
/// \param EndLoc Ending Location of the directive.
|
||||
/// \param Clauses List of clauses.
|
||||
/// \param AssociatedStmt Statement, associated with the directive.
|
||||
/// \param X 'x' part of the associated expression/statement.
|
||||
/// \param V 'v' part of the associated expression/statement.
|
||||
/// \param Expr 'expr' part of the associated expression/statement.
|
||||
///
|
||||
static OMPAtomicDirective *
|
||||
Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
|
||||
ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt);
|
||||
ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt, Expr *X, Expr *V,
|
||||
Expr *E);
|
||||
|
||||
/// \brief Creates an empty directive with the place for \a NumClauses
|
||||
/// clauses.
|
||||
|
@ -1472,6 +1485,22 @@ public:
|
|||
static OMPAtomicDirective *CreateEmpty(const ASTContext &C,
|
||||
unsigned NumClauses, EmptyShell);
|
||||
|
||||
/// \brief Get 'x' part of the associated expression/statement.
|
||||
Expr *getX() { return cast_or_null<Expr>(*std::next(child_begin())); }
|
||||
const Expr *getX() const {
|
||||
return cast_or_null<Expr>(*std::next(child_begin()));
|
||||
}
|
||||
/// \brief Get 'v' part of the associated expression/statement.
|
||||
Expr *getV() { return cast_or_null<Expr>(*std::next(child_begin(), 2)); }
|
||||
const Expr *getV() const {
|
||||
return cast_or_null<Expr>(*std::next(child_begin(), 2));
|
||||
}
|
||||
/// \brief Get 'expr' part of the associated expression/statement.
|
||||
Expr *getExpr() { return cast_or_null<Expr>(*std::next(child_begin(), 3)); }
|
||||
const Expr *getExpr() const {
|
||||
return cast_or_null<Expr>(*std::next(child_begin(), 3));
|
||||
}
|
||||
|
||||
static bool classof(const Stmt *T) {
|
||||
return T->getStmtClass() == OMPAtomicDirectiveClass;
|
||||
}
|
||||
|
|
|
@ -7322,7 +7322,9 @@ def err_omp_parallel_reduction_in_task_firstprivate : Error<
|
|||
"argument of a reduction clause of a %0 construct must not appear in a firstprivate clause on a task construct">;
|
||||
def err_omp_atomic_read_not_expression_statement : Error<
|
||||
"the statement for 'atomic read' must be an expression statement of form 'v = x;',"
|
||||
" where v and x are both l-value expressions with scalar type">;
|
||||
" where v and x are both lvalue expressions with scalar type">;
|
||||
def note_omp_atomic_read: Note<
|
||||
"%select{expected an expression statement|expected built-in assignment operator|expected expression of scalar type|expected lvalue expression}0">;
|
||||
def err_omp_atomic_write_not_expression_statement : Error<
|
||||
"the statement for 'atomic write' must be an expression statement of form 'x = expr;',"
|
||||
" where x is an l-value expression with scalar type">;
|
||||
|
|
|
@ -1924,19 +1924,21 @@ OMPOrderedDirective *OMPOrderedDirective::CreateEmpty(const ASTContext &C,
|
|||
return new (Mem) OMPOrderedDirective();
|
||||
}
|
||||
|
||||
OMPAtomicDirective *OMPAtomicDirective::Create(const ASTContext &C,
|
||||
SourceLocation StartLoc,
|
||||
SourceLocation EndLoc,
|
||||
ArrayRef<OMPClause *> Clauses,
|
||||
Stmt *AssociatedStmt) {
|
||||
OMPAtomicDirective *
|
||||
OMPAtomicDirective::Create(const ASTContext &C, SourceLocation StartLoc,
|
||||
SourceLocation EndLoc, ArrayRef<OMPClause *> Clauses,
|
||||
Stmt *AssociatedStmt, Expr *X, Expr *V, Expr *E) {
|
||||
unsigned Size = llvm::RoundUpToAlignment(sizeof(OMPAtomicDirective),
|
||||
llvm::alignOf<OMPClause *>());
|
||||
void *Mem =
|
||||
C.Allocate(Size + sizeof(OMPClause *) * Clauses.size() + sizeof(Stmt *));
|
||||
void *Mem = C.Allocate(Size + sizeof(OMPClause *) * Clauses.size() +
|
||||
4 * sizeof(Stmt *));
|
||||
OMPAtomicDirective *Dir =
|
||||
new (Mem) OMPAtomicDirective(StartLoc, EndLoc, Clauses.size());
|
||||
Dir->setClauses(Clauses);
|
||||
Dir->setAssociatedStmt(AssociatedStmt);
|
||||
Dir->setX(X);
|
||||
Dir->setV(V);
|
||||
Dir->setExpr(E);
|
||||
return Dir;
|
||||
}
|
||||
|
||||
|
@ -1946,7 +1948,7 @@ OMPAtomicDirective *OMPAtomicDirective::CreateEmpty(const ASTContext &C,
|
|||
unsigned Size = llvm::RoundUpToAlignment(sizeof(OMPAtomicDirective),
|
||||
llvm::alignOf<OMPClause *>());
|
||||
void *Mem =
|
||||
C.Allocate(Size + sizeof(OMPClause *) * NumClauses + sizeof(Stmt *));
|
||||
C.Allocate(Size + sizeof(OMPClause *) * NumClauses + 4 * sizeof(Stmt *));
|
||||
return new (Mem) OMPAtomicDirective(NumClauses);
|
||||
}
|
||||
|
||||
|
|
|
@ -3223,13 +3223,94 @@ StmtResult Sema::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Body = CS->getCapturedStmt();
|
||||
Expr *X = nullptr;
|
||||
Expr *V = nullptr;
|
||||
Expr *E = nullptr;
|
||||
// OpenMP [2.12.6, atomic Construct]
|
||||
// In the next expressions:
|
||||
// * x and v (as applicable) are both l-value expressions with scalar type.
|
||||
// * During the execution of an atomic region, multiple syntactic
|
||||
// occurrences of x must designate the same storage location.
|
||||
// * Neither of v and expr (as applicable) may access the storage location
|
||||
// designated by x.
|
||||
// * Neither of x and expr (as applicable) may access the storage location
|
||||
// designated by v.
|
||||
// * expr is an expression with scalar type.
|
||||
// * binop is one of +, *, -, /, &, ^, |, <<, or >>.
|
||||
// * binop, binop=, ++, and -- are not overloaded operators.
|
||||
// * The expression x binop expr must be numerically equivalent to x binop
|
||||
// (expr). This requirement is satisfied if the operators in expr have
|
||||
// precedence greater than binop, or by using parentheses around expr or
|
||||
// subexpressions of expr.
|
||||
// * The expression expr binop x must be numerically equivalent to (expr)
|
||||
// binop x. This requirement is satisfied if the operators in expr have
|
||||
// precedence equal to or greater than binop, or by using parentheses around
|
||||
// expr or subexpressions of expr.
|
||||
// * For forms that allow multiple occurrences of x, the number of times
|
||||
// that x is evaluated is unspecified.
|
||||
if (AtomicKind == OMPC_read) {
|
||||
if (!isa<Expr>(Body)) {
|
||||
Diag(Body->getLocStart(),
|
||||
diag::err_omp_atomic_read_not_expression_statement);
|
||||
return StmtError();
|
||||
enum {
|
||||
NotAnExpression,
|
||||
NotAnAssignmentOp,
|
||||
NotAScalarType,
|
||||
NotAnLValue,
|
||||
NoError
|
||||
} ErrorFound = NoError;
|
||||
SourceLocation ErrorLoc, NoteLoc;
|
||||
SourceRange ErrorRange, NoteRange;
|
||||
// If clause is read:
|
||||
// v = x;
|
||||
if (auto AtomicBody = dyn_cast<Expr>(Body)) {
|
||||
auto AtomicBinOp =
|
||||
dyn_cast<BinaryOperator>(AtomicBody->IgnoreParenImpCasts());
|
||||
if (AtomicBinOp && AtomicBinOp->getOpcode() == BO_Assign) {
|
||||
X = AtomicBinOp->getRHS()->IgnoreParenImpCasts();
|
||||
V = AtomicBinOp->getLHS()->IgnoreParenImpCasts();
|
||||
if ((X->isInstantiationDependent() || X->getType()->isScalarType()) &&
|
||||
(V->isInstantiationDependent() || V->getType()->isScalarType())) {
|
||||
if (!X->isLValue() || !V->isLValue()) {
|
||||
auto NotLValueExpr = X->isLValue() ? V : X;
|
||||
ErrorFound = NotAnLValue;
|
||||
ErrorLoc = AtomicBinOp->getExprLoc();
|
||||
ErrorRange = AtomicBinOp->getSourceRange();
|
||||
NoteLoc = NotLValueExpr->getExprLoc();
|
||||
NoteRange = NotLValueExpr->getSourceRange();
|
||||
}
|
||||
} else if (!X->isInstantiationDependent() ||
|
||||
!V->isInstantiationDependent()) {
|
||||
auto NotScalarExpr =
|
||||
(X->isInstantiationDependent() || X->getType()->isScalarType())
|
||||
? V
|
||||
: X;
|
||||
ErrorFound = NotAScalarType;
|
||||
ErrorLoc = AtomicBinOp->getExprLoc();
|
||||
ErrorRange = AtomicBinOp->getSourceRange();
|
||||
NoteLoc = NotScalarExpr->getExprLoc();
|
||||
NoteRange = NotScalarExpr->getSourceRange();
|
||||
}
|
||||
} else {
|
||||
ErrorFound = NotAnAssignmentOp;
|
||||
ErrorLoc = AtomicBody->getExprLoc();
|
||||
ErrorRange = AtomicBody->getSourceRange();
|
||||
NoteLoc = AtomicBinOp ? AtomicBinOp->getOperatorLoc()
|
||||
: AtomicBody->getExprLoc();
|
||||
NoteRange = AtomicBinOp ? AtomicBinOp->getSourceRange()
|
||||
: AtomicBody->getSourceRange();
|
||||
}
|
||||
} else {
|
||||
ErrorFound = NotAnExpression;
|
||||
NoteLoc = ErrorLoc = Body->getLocStart();
|
||||
NoteRange = ErrorRange = SourceRange(NoteLoc, NoteLoc);
|
||||
}
|
||||
if (ErrorFound != NoError) {
|
||||
Diag(ErrorLoc, diag::err_omp_atomic_read_not_expression_statement)
|
||||
<< ErrorRange;
|
||||
Diag(NoteLoc, diag::note_omp_atomic_read) << ErrorFound << NoteRange;
|
||||
return StmtError();
|
||||
} else if (CurContext->isDependentContext())
|
||||
V = X = nullptr;
|
||||
} else if (AtomicKind == OMPC_write) {
|
||||
if (!isa<Expr>(Body)) {
|
||||
Diag(Body->getLocStart(),
|
||||
|
@ -3257,7 +3338,8 @@ StmtResult Sema::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
|
|||
|
||||
getCurFunction()->setHasBranchProtectedScope();
|
||||
|
||||
return OMPAtomicDirective::Create(Context, StartLoc, EndLoc, Clauses, AStmt);
|
||||
return OMPAtomicDirective::Create(Context, StartLoc, EndLoc, Clauses, AStmt,
|
||||
X, V, E);
|
||||
}
|
||||
|
||||
StmtResult Sema::ActOnOpenMPTargetDirective(ArrayRef<OMPClause *> Clauses,
|
||||
|
|
|
@ -2123,6 +2123,9 @@ void ASTStmtReader::VisitOMPAtomicDirective(OMPAtomicDirective *D) {
|
|||
// The NumClauses field was read in ReadStmtFromStream.
|
||||
++Idx;
|
||||
VisitOMPExecutableDirective(D);
|
||||
D->setX(Reader.ReadSubExpr());
|
||||
D->setV(Reader.ReadSubExpr());
|
||||
D->setExpr(Reader.ReadSubExpr());
|
||||
}
|
||||
|
||||
void ASTStmtReader::VisitOMPTargetDirective(OMPTargetDirective *D) {
|
||||
|
|
|
@ -1978,6 +1978,9 @@ void ASTStmtWriter::VisitOMPAtomicDirective(OMPAtomicDirective *D) {
|
|||
VisitStmt(D);
|
||||
Record.push_back(D->getNumClauses());
|
||||
VisitOMPExecutableDirective(D);
|
||||
Writer.AddStmt(D->getX());
|
||||
Writer.AddStmt(D->getV());
|
||||
Writer.AddStmt(D->getExpr());
|
||||
Code = serialization::STMT_OMP_ATOMIC_DIRECTIVE;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -ferror-limit 100 %s
|
||||
|
||||
int foo() {
|
||||
L1:
|
||||
foo();
|
||||
#pragma omp atomic
|
||||
// expected-error@+1 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an l-value expression with scalar type}}
|
||||
{
|
||||
foo();
|
||||
goto L1; // expected-error {{use of undeclared label 'L1'}}
|
||||
}
|
||||
goto L2; // expected-error {{use of undeclared label 'L2'}}
|
||||
#pragma omp atomic
|
||||
// expected-error@+1 {{the statement for 'atomic' must be an expression statement of form '++x;', '--x;', 'x++;', 'x--;', 'x binop= expr;', 'x = x binop expr' or 'x = expr binop x', where x is an l-value expression with scalar type}}
|
||||
{
|
||||
foo();
|
||||
L2:
|
||||
foo();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct S {
|
||||
int a;
|
||||
};
|
||||
|
||||
int readint() {
|
||||
int a = 0, b = 0;
|
||||
// Test for atomic read
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected an expression statement}}
|
||||
;
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected built-in assignment operator}}
|
||||
foo();
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected built-in assignment operator}}
|
||||
a += b;
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected lvalue expression}}
|
||||
a = 0;
|
||||
#pragma omp atomic read
|
||||
a = b;
|
||||
// expected-error@+1 {{directive '#pragma omp atomic' cannot contain more than one 'read' clause}}
|
||||
#pragma omp atomic read read
|
||||
a = b;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int readS() {
|
||||
struct S a, b;
|
||||
// expected-error@+1 {{directive '#pragma omp atomic' cannot contain more than one 'read' clause}}
|
||||
#pragma omp atomic read read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected expression of scalar type}}
|
||||
a = b;
|
||||
|
||||
return a.a;
|
||||
}
|
|
@ -21,31 +21,78 @@ L1:
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct S {
|
||||
int a;
|
||||
S &operator=(int v) {
|
||||
a = v;
|
||||
return *this;
|
||||
}
|
||||
S &operator+=(const S &s) {
|
||||
a += s.a;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
T read() {
|
||||
T a, b = 0;
|
||||
T a = T(), b = T();
|
||||
// Test for atomic read
|
||||
#pragma omp atomic read
|
||||
// expected-error@+1 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both l-value expressions with scalar type}}
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected an expression statement}}
|
||||
;
|
||||
// expected-error@+1 {{directive '#pragma omp atomic' cannot contain more than one 'read' clause}}
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected built-in assignment operator}}
|
||||
foo();
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected built-in assignment operator}}
|
||||
a += b;
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected lvalue expression}}
|
||||
a = 0;
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected built-in assignment operator}}
|
||||
a = b;
|
||||
// expected-error@+1 {{directive '#pragma omp atomic' cannot contain more than one 'read' clause}}
|
||||
#pragma omp atomic read read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected built-in assignment operator}}
|
||||
a = b;
|
||||
|
||||
return T();
|
||||
return a;
|
||||
}
|
||||
|
||||
int read() {
|
||||
int a, b = 0;
|
||||
int a = 0, b = 0;
|
||||
// Test for atomic read
|
||||
#pragma omp atomic read
|
||||
// expected-error@+1 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both l-value expressions with scalar type}}
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected an expression statement}}
|
||||
;
|
||||
// expected-error@+1 {{directive '#pragma omp atomic' cannot contain more than one 'read' clause}}
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected built-in assignment operator}}
|
||||
foo();
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected built-in assignment operator}}
|
||||
a += b;
|
||||
#pragma omp atomic read
|
||||
// expected-error@+2 {{the statement for 'atomic read' must be an expression statement of form 'v = x;', where v and x are both lvalue expressions with scalar type}}
|
||||
// expected-note@+1 {{expected lvalue expression}}
|
||||
a = 0;
|
||||
#pragma omp atomic read
|
||||
a = b;
|
||||
// expected-error@+1 {{directive '#pragma omp atomic' cannot contain more than one 'read' clause}}
|
||||
#pragma omp atomic read read
|
||||
a = b;
|
||||
|
||||
return read<int>();
|
||||
// expected-note@+1 {{in instantiation of function template specialization 'read<S>' requested here}}
|
||||
return read<int>() + read<S>().a;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
|
Loading…
Reference in New Issue