llvm-project/clang/lib/Sema/SemaOpenACC.cpp
erichkeane 1ab81f8e7f [OpenACC] Implement 'delete' AST/Sema for 'exit data' construct
'delete' is another clause that has very little compile-time
implication, but needs a full AST that takes a var list.  This patch
ipmlements it fully, plus adds sufficient test coverage.
2024-12-16 06:44:53 -08:00

3665 lines
141 KiB
C++

//===--- SemaOpenACC.cpp - Semantic Analysis 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
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements semantic analysis for OpenACC constructs and
/// clauses.
///
//===----------------------------------------------------------------------===//
#include "clang/Sema/SemaOpenACC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/OpenACCKinds.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
using namespace clang;
namespace {
bool diagnoseConstructAppertainment(SemaOpenACC &S, OpenACCDirectiveKind K,
SourceLocation StartLoc, bool IsStmt) {
switch (K) {
default:
case OpenACCDirectiveKind::Invalid:
// Nothing to do here, both invalid and unimplemented don't really need to
// do anything.
break;
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ExitData:
case OpenACCDirectiveKind::HostData:
if (!IsStmt)
return S.Diag(StartLoc, diag::err_acc_construct_appertainment) << K;
break;
}
return false;
}
bool doesClauseApplyToDirective(OpenACCDirectiveKind DirectiveKind,
OpenACCClauseKind ClauseKind) {
switch (ClauseKind) {
// FIXME: For each clause as we implement them, we can add the
// 'legalization' list here.
case OpenACCClauseKind::Default:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::Data:
return true;
default:
return false;
}
case OpenACCClauseKind::If:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ExitData:
case OpenACCDirectiveKind::HostData:
case OpenACCDirectiveKind::Init:
case OpenACCDirectiveKind::Shutdown:
case OpenACCDirectiveKind::Set:
case OpenACCDirectiveKind::Update:
case OpenACCDirectiveKind::Wait:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Self:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Update:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::NumGangs:
case OpenACCClauseKind::NumWorkers:
case OpenACCClauseKind::VectorLength:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::FirstPrivate:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Private:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::NoCreate:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Present:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::Declare:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Copy:
case OpenACCClauseKind::PCopy:
case OpenACCClauseKind::PresentOrCopy:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::Declare:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::CopyIn:
case OpenACCClauseKind::PCopyIn:
case OpenACCClauseKind::PresentOrCopyIn:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::Declare:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::CopyOut:
case OpenACCClauseKind::PCopyOut:
case OpenACCClauseKind::PresentOrCopyOut:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::ExitData:
case OpenACCDirectiveKind::Declare:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Create:
case OpenACCClauseKind::PCreate:
case OpenACCClauseKind::PresentOrCreate:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Attach:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::DevicePtr:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::Declare:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Async:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ExitData:
case OpenACCDirectiveKind::Set:
case OpenACCDirectiveKind::Update:
case OpenACCDirectiveKind::Wait:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Wait:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ExitData:
case OpenACCDirectiveKind::Update:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Seq:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::Routine:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Independent:
case OpenACCClauseKind::Auto:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Reduction:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::DeviceType:
case OpenACCClauseKind::DType:
switch (DirectiveKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::Init:
case OpenACCDirectiveKind::Shutdown:
case OpenACCDirectiveKind::Set:
case OpenACCDirectiveKind::Update:
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::Routine:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
case OpenACCClauseKind::Collapse: {
switch (DirectiveKind) {
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
}
case OpenACCClauseKind::Tile: {
switch (DirectiveKind) {
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
return true;
default:
return false;
}
}
case OpenACCClauseKind::Gang: {
switch (DirectiveKind) {
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::Routine:
return true;
default:
return false;
}
case OpenACCClauseKind::Worker: {
switch (DirectiveKind) {
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::Routine:
return true;
default:
return false;
}
}
case OpenACCClauseKind::Vector: {
switch (DirectiveKind) {
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::Routine:
return true;
default:
return false;
}
}
case OpenACCClauseKind::Finalize: {
switch (DirectiveKind) {
case OpenACCDirectiveKind::ExitData:
return true;
default:
return false;
}
}
case OpenACCClauseKind::IfPresent: {
switch (DirectiveKind) {
case OpenACCDirectiveKind::HostData:
case OpenACCDirectiveKind::Update:
return true;
default:
return false;
}
}
case OpenACCClauseKind::Delete: {
switch (DirectiveKind) {
case OpenACCDirectiveKind::ExitData:
return true;
default:
return false;
}
}
case OpenACCClauseKind::Detach: {
switch (DirectiveKind) {
case OpenACCDirectiveKind::ExitData:
return true;
default:
return false;
}
}
}
default:
// Do nothing so we can go to the 'unimplemented' diagnostic instead.
return true;
}
llvm_unreachable("Invalid clause kind");
}
bool checkAlreadyHasClauseOfKind(
SemaOpenACC &S, ArrayRef<const OpenACCClause *> ExistingClauses,
SemaOpenACC::OpenACCParsedClause &Clause) {
const auto *Itr = llvm::find_if(ExistingClauses, [&](const OpenACCClause *C) {
return C->getClauseKind() == Clause.getClauseKind();
});
if (Itr != ExistingClauses.end()) {
S.Diag(Clause.getBeginLoc(), diag::err_acc_duplicate_clause_disallowed)
<< Clause.getDirectiveKind() << Clause.getClauseKind();
S.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
return true;
}
return false;
}
bool checkValidAfterDeviceType(
SemaOpenACC &S, const OpenACCDeviceTypeClause &DeviceTypeClause,
const SemaOpenACC::OpenACCParsedClause &NewClause) {
// This is only a requirement on compute, combined, data and loop constructs
// so far, so this is fine otherwise.
if (!isOpenACCComputeDirectiveKind(NewClause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(NewClause.getDirectiveKind()) &&
NewClause.getDirectiveKind() != OpenACCDirectiveKind::Loop &&
NewClause.getDirectiveKind() != OpenACCDirectiveKind::Data)
return false;
// OpenACC3.3: Section 2.4: Clauses that precede any device_type clause are
// default clauses. Clauses that follow a device_type clause up to the end of
// the directive or up to the next device_type clause are device-specific
// clauses for the device types specified in the device_type argument.
//
// The above implies that despite what the individual text says, these are
// valid.
if (NewClause.getClauseKind() == OpenACCClauseKind::DType ||
NewClause.getClauseKind() == OpenACCClauseKind::DeviceType)
return false;
// Implement check from OpenACC3.3: section 2.5.4:
// Only the async, wait, num_gangs, num_workers, and vector_length clauses may
// follow a device_type clause.
if (isOpenACCComputeDirectiveKind(NewClause.getDirectiveKind())) {
switch (NewClause.getClauseKind()) {
case OpenACCClauseKind::Async:
case OpenACCClauseKind::Wait:
case OpenACCClauseKind::NumGangs:
case OpenACCClauseKind::NumWorkers:
case OpenACCClauseKind::VectorLength:
return false;
default:
break;
}
} else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Loop) {
// Implement check from OpenACC3.3: section 2.9:
// Only the collapse, gang, worker, vector, seq, independent, auto, and tile
// clauses may follow a device_type clause.
switch (NewClause.getClauseKind()) {
case OpenACCClauseKind::Collapse:
case OpenACCClauseKind::Gang:
case OpenACCClauseKind::Worker:
case OpenACCClauseKind::Vector:
case OpenACCClauseKind::Seq:
case OpenACCClauseKind::Independent:
case OpenACCClauseKind::Auto:
case OpenACCClauseKind::Tile:
return false;
default:
break;
}
} else if (isOpenACCCombinedDirectiveKind(NewClause.getDirectiveKind())) {
// This seems like it should be the union of 2.9 and 2.5.4 from above.
switch (NewClause.getClauseKind()) {
case OpenACCClauseKind::Async:
case OpenACCClauseKind::Wait:
case OpenACCClauseKind::NumGangs:
case OpenACCClauseKind::NumWorkers:
case OpenACCClauseKind::VectorLength:
case OpenACCClauseKind::Collapse:
case OpenACCClauseKind::Gang:
case OpenACCClauseKind::Worker:
case OpenACCClauseKind::Vector:
case OpenACCClauseKind::Seq:
case OpenACCClauseKind::Independent:
case OpenACCClauseKind::Auto:
case OpenACCClauseKind::Tile:
return false;
default:
break;
}
} else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Data) {
// OpenACC3.3 section 2.6.5: Only the async and wait clauses may follow a
// device_type clause.
switch (NewClause.getClauseKind()) {
case OpenACCClauseKind::Async:
case OpenACCClauseKind::Wait:
return false;
default:
break;
}
}
S.Diag(NewClause.getBeginLoc(), diag::err_acc_clause_after_device_type)
<< NewClause.getClauseKind() << DeviceTypeClause.getClauseKind()
<< NewClause.getDirectiveKind();
S.Diag(DeviceTypeClause.getBeginLoc(), diag::note_acc_previous_clause_here);
return true;
}
class SemaOpenACCClauseVisitor {
SemaOpenACC &SemaRef;
ASTContext &Ctx;
ArrayRef<const OpenACCClause *> ExistingClauses;
bool NotImplemented = false;
OpenACCClause *isNotImplemented() {
NotImplemented = true;
return nullptr;
}
// OpenACC 3.3 2.9:
// A 'gang', 'worker', or 'vector' clause may not appear if a 'seq' clause
// appears.
bool DiagIfSeqClause(SemaOpenACC::OpenACCParsedClause &Clause) {
const auto *Itr =
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCSeqClause>);
if (Itr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_cannot_combine)
<< Clause.getClauseKind() << (*Itr)->getClauseKind()
<< Clause.getDirectiveKind();
SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
return true;
}
return false;
}
public:
SemaOpenACCClauseVisitor(SemaOpenACC &S,
ArrayRef<const OpenACCClause *> ExistingClauses)
: SemaRef(S), Ctx(S.getASTContext()), ExistingClauses(ExistingClauses) {}
// Once we've implemented everything, we shouldn't need this infrastructure.
// But in the meantime, we use this to help decide whether the clause was
// handled for this directive.
bool diagNotImplemented() { return NotImplemented; }
OpenACCClause *Visit(SemaOpenACC::OpenACCParsedClause &Clause) {
switch (Clause.getClauseKind()) {
#define VISIT_CLAUSE(CLAUSE_NAME) \
case OpenACCClauseKind::CLAUSE_NAME: \
return Visit##CLAUSE_NAME##Clause(Clause);
#define CLAUSE_ALIAS(ALIAS, CLAUSE_NAME, DEPRECATED) \
case OpenACCClauseKind::ALIAS: \
if (DEPRECATED) \
SemaRef.Diag(Clause.getBeginLoc(), diag::warn_acc_deprecated_alias_name) \
<< Clause.getClauseKind() << OpenACCClauseKind::CLAUSE_NAME; \
return Visit##CLAUSE_NAME##Clause(Clause);
#include "clang/Basic/OpenACCClauses.def"
default:
return isNotImplemented();
}
llvm_unreachable("Invalid clause kind");
}
#define VISIT_CLAUSE(CLAUSE_NAME) \
OpenACCClause *Visit##CLAUSE_NAME##Clause( \
SemaOpenACC::OpenACCParsedClause &Clause);
#include "clang/Basic/OpenACCClauses.def"
};
OpenACCClause *SemaOpenACCClauseVisitor::VisitDefaultClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Don't add an invalid clause to the AST.
if (Clause.getDefaultClauseKind() == OpenACCDefaultClauseKind::Invalid)
return nullptr;
// OpenACC 3.3, Section 2.5.4:
// At most one 'default' clause may appear, and it must have a value of
// either 'none' or 'present'.
// Second half of the sentence is diagnosed during parsing.
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
return nullptr;
return OpenACCDefaultClause::Create(
Ctx, Clause.getDefaultClauseKind(), Clause.getBeginLoc(),
Clause.getLParenLoc(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitTileClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Duplicates here are not really sensible. We could possible permit
// multiples if they all had the same value, but there isn't really a good
// reason to do so. Also, this simplifies the suppression of duplicates, in
// that we know if we 'find' one after instantiation, that it is the same
// clause, which simplifies instantiation/checking/etc.
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
return nullptr;
llvm::SmallVector<Expr *> NewSizeExprs;
// Make sure these are all positive constant expressions or *.
for (Expr *E : Clause.getIntExprs()) {
ExprResult Res = SemaRef.CheckTileSizeExpr(E);
if (!Res.isUsable())
return nullptr;
NewSizeExprs.push_back(Res.get());
}
return OpenACCTileClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(), NewSizeExprs,
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitIfClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute'/'combined'/'data'
// constructs, and 'compute'/'combined'/'data' constructs are the only
// constructs that can do anything with this yet, so skip/treat as
// unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCDataDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// There is no prose in the standard that says duplicates aren't allowed,
// but this diagnostic is present in other compilers, as well as makes
// sense. Prose DOES exist for 'data' and 'host_data', 'enter data' and 'exit
// data' both don't, but other implmementations do this. OpenACC issue 519
// filed for the latter two.
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
return nullptr;
// The parser has ensured that we have a proper condition expr, so there
// isn't really much to do here.
// If the 'if' clause is true, it makes the 'self' clause have no effect,
// diagnose that here.
// TODO OpenACC: When we add these two to other constructs, we might not
// want to warn on this (for example, 'update').
const auto *Itr =
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCSelfClause>);
if (Itr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict);
SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
}
return OpenACCIfClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(),
Clause.getConditionExpr(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitSelfClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute' constructs, and
// 'compute' constructs are the only construct that can do anything with
// this yet, so skip/treat as unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// TODO OpenACC: When we implement this for 'update', this takes a
// 'var-list' instead of a condition expression, so semantics/handling has
// to happen differently here.
// There is no prose in the standard that says duplicates aren't allowed,
// but this diagnostic is present in other compilers, as well as makes
// sense.
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
return nullptr;
// If the 'if' clause is true, it makes the 'self' clause have no effect,
// diagnose that here.
// TODO OpenACC: When we add these two to other constructs, we might not
// want to warn on this (for example, 'update').
const auto *Itr =
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCIfClause>);
if (Itr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict);
SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
}
return OpenACCSelfClause::Create(
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(),
Clause.getConditionExpr(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitNumGangsClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// There is no prose in the standard that says duplicates aren't allowed,
// but this diagnostic is present in other compilers, as well as makes
// sense.
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
return nullptr;
// num_gangs requires at least 1 int expr in all forms. Diagnose here, but
// allow us to continue, an empty clause might be useful for future
// diagnostics.
if (Clause.getIntExprs().empty())
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_num_gangs_num_args)
<< /*NoArgs=*/0;
unsigned MaxArgs =
(Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel ||
Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop)
? 3
: 1;
// The max number of args differs between parallel and other constructs.
// Again, allow us to continue for the purposes of future diagnostics.
if (Clause.getIntExprs().size() > MaxArgs)
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_num_gangs_num_args)
<< /*NoArgs=*/1 << Clause.getDirectiveKind() << MaxArgs
<< Clause.getIntExprs().size();
// OpenACC 3.3 Section 2.9.11: A reduction clause may not appear on a loop
// directive that has a gang clause and is within a compute construct that has
// a num_gangs clause with more than one explicit argument.
if (Clause.getIntExprs().size() > 1 &&
isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
auto *GangClauseItr =
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
auto *ReductionClauseItr =
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
if (GangClauseItr != ExistingClauses.end() &&
ReductionClauseItr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_gang_reduction_numgangs_conflict)
<< OpenACCClauseKind::Reduction << OpenACCClauseKind::Gang
<< Clause.getDirectiveKind() << /*is on combined directive=*/1;
SemaRef.Diag((*ReductionClauseItr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
SemaRef.Diag((*GangClauseItr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
return nullptr;
}
}
// OpenACC 3.3 Section 2.5.4:
// A reduction clause may not appear on a parallel construct with a
// num_gangs clause that has more than one argument.
if ((Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel ||
Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop) &&
Clause.getIntExprs().size() > 1) {
auto *Parallel =
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
if (Parallel != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_reduction_num_gangs_conflict)
<< /*>1 arg in first loc=*/1 << Clause.getClauseKind()
<< Clause.getDirectiveKind() << OpenACCClauseKind::Reduction;
SemaRef.Diag((*Parallel)->getBeginLoc(),
diag::note_acc_previous_clause_here);
return nullptr;
}
}
// OpenACC 3.3 Section 2.9.2:
// An argument with no keyword or with the 'num' keyword is allowed only when
// the 'num_gangs' does not appear on the 'kernel' construct.
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop) {
auto GangClauses = llvm::make_filter_range(
ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
for (auto *GC : GangClauses) {
if (cast<OpenACCGangClause>(GC)->hasExprOfKind(OpenACCGangKind::Num)) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_num_arg_conflict_reverse)
<< OpenACCClauseKind::NumGangs << OpenACCClauseKind::Gang
<< /*Num argument*/ 1;
SemaRef.Diag(GC->getBeginLoc(), diag::note_acc_previous_clause_here);
return nullptr;
}
}
}
return OpenACCNumGangsClause::Create(
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitNumWorkersClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// There is no prose in the standard that says duplicates aren't allowed,
// but this diagnostic is present in other compilers, as well as makes
// sense.
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
return nullptr;
// OpenACC 3.3 Section 2.9.2:
// An argument is allowed only when the 'num_workers' does not appear on the
// kernels construct.
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop) {
auto WorkerClauses = llvm::make_filter_range(
ExistingClauses, llvm::IsaPred<OpenACCWorkerClause>);
for (auto *WC : WorkerClauses) {
if (cast<OpenACCWorkerClause>(WC)->hasIntExpr()) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_num_arg_conflict_reverse)
<< OpenACCClauseKind::NumWorkers << OpenACCClauseKind::Worker
<< /*num argument*/ 0;
SemaRef.Diag(WC->getBeginLoc(), diag::note_acc_previous_clause_here);
return nullptr;
}
}
}
assert(Clause.getIntExprs().size() == 1 &&
"Invalid number of expressions for NumWorkers");
return OpenACCNumWorkersClause::Create(
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitVectorLengthClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// There is no prose in the standard that says duplicates aren't allowed,
// but this diagnostic is present in other compilers, as well as makes
// sense.
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
return nullptr;
// OpenACC 3.3 Section 2.9.4:
// An argument is allowed only when the 'vector_length' does not appear on the
// 'kernels' construct.
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop) {
auto VectorClauses = llvm::make_filter_range(
ExistingClauses, llvm::IsaPred<OpenACCVectorClause>);
for (auto *VC : VectorClauses) {
if (cast<OpenACCVectorClause>(VC)->hasIntExpr()) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_num_arg_conflict_reverse)
<< OpenACCClauseKind::VectorLength << OpenACCClauseKind::Vector
<< /*num argument*/ 0;
SemaRef.Diag(VC->getBeginLoc(), diag::note_acc_previous_clause_here);
return nullptr;
}
}
}
assert(Clause.getIntExprs().size() == 1 &&
"Invalid number of expressions for NumWorkers");
return OpenACCVectorLengthClause::Create(
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitAsyncClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute'/'combined'/'data'
// constructs, and 'compute'/'combined'/'data' constructs are the only
// construct that can do anything with this yet, so skip/treat as
// unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCDataDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// There is no prose in the standard that says duplicates aren't allowed,
// but this diagnostic is present in other compilers, as well as makes
// sense.
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
return nullptr;
assert(Clause.getNumIntExprs() < 2 &&
"Invalid number of expressions for Async");
return OpenACCAsyncClause::Create(
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(),
Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr,
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitPrivateClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
return OpenACCPrivateClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(),
Clause.getVarList(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitFirstPrivateClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
return OpenACCFirstPrivateClause::Create(
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitNoCreateClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
return OpenACCNoCreateClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(),
Clause.getVarList(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitPresentClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute'/'combined'/'data'
// constructs, and 'compute'/'combined'/'data' constructs are the only
// construct that can do anything with this yet, so skip/treat as
// unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCDataDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
return OpenACCPresentClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(),
Clause.getVarList(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute'/'combined'/'data'
// constructs, and 'compute'/'combined'/'data' constructs are the only
// construct that can do anything with this yet, so skip/treat as
// unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCDataDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
return OpenACCCopyClause::Create(
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
Clause.getVarList(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyInClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute'/'combined'/'data'
// constructs, and 'compute'/'combined'/'data' constructs are the only
// construct that can do anything with this yet, so skip/treat as
// unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCDataDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
return OpenACCCopyInClause::Create(
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
Clause.isReadOnly(), Clause.getVarList(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyOutClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute'/'combined'/'data'
// constructs, and 'compute'/'combined'/'data' constructs are the only
// construct that can do anything with this yet, so skip/treat as
// unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCDataDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
return OpenACCCopyOutClause::Create(
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
Clause.isZero(), Clause.getVarList(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitCreateClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
return OpenACCCreateClause::Create(
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
Clause.isZero(), Clause.getVarList(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitAttachClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// ActOnVar ensured that everything is a valid variable reference, but we
// still have to make sure it is a pointer type.
llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
llvm::erase_if(VarList, [&](Expr *E) {
return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Attach, E);
});
Clause.setVarListDetails(VarList,
/*IsReadOnly=*/false, /*IsZero=*/false);
return OpenACCAttachClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(), Clause.getVarList(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitDetachClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// ActOnVar ensured that everything is a valid variable reference, but we
// still have to make sure it is a pointer type.
llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
llvm::erase_if(VarList, [&](Expr *E) {
return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Detach, E);
});
Clause.setVarListDetails(VarList,
/*IsReadOnly=*/false, /*IsZero=*/false);
return OpenACCDetachClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(), Clause.getVarList(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitDeleteClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// ActOnVar ensured that everything is a valid variable reference, so there
// really isn't anything to do here. GCC does some duplicate-finding, though
// it isn't apparent in the standard where this is justified.
return OpenACCDeleteClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(), Clause.getVarList(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitDevicePtrClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute'/'combined'/'data'
// constructs, and 'compute'/'combined'/'data' constructs are the only
// construct that can do anything with this yet, so skip/treat as
// unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCDataDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// ActOnVar ensured that everything is a valid variable reference, but we
// still have to make sure it is a pointer type.
llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
llvm::erase_if(VarList, [&](Expr *E) {
return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::DevicePtr, E);
});
Clause.setVarListDetails(VarList,
/*IsReadOnly=*/false, /*IsZero=*/false);
return OpenACCDevicePtrClause::Create(
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitWaitClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute'/'combined'/'data'
// constructs, and 'compute'/'combined'/'data' constructs are the only
// construct that can do anything with this yet, so skip/treat as
// unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()) &&
!isOpenACCDataDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
return OpenACCWaitClause::Create(
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getDevNumExpr(),
Clause.getQueuesLoc(), Clause.getQueueIdExprs(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceTypeClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'compute', 'combined', 'data' and
// 'loop' constructs, and 'compute'/'combined'/'data'/'loop' constructs are
// the only construct that can do anything with this yet, so skip/treat as
// unimplemented in this case.
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()) &&
Clause.getDirectiveKind() != OpenACCDirectiveKind::Loop &&
Clause.getDirectiveKind() != OpenACCDirectiveKind::Data &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// TODO OpenACC: Once we get enough of the CodeGen implemented that we have
// a source for the list of valid architectures, we need to warn on unknown
// identifiers here.
return OpenACCDeviceTypeClause::Create(
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
Clause.getDeviceTypeArchitectures(), Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitAutoClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// OpenACC 3.3 2.9:
// Only one of the seq, independent, and auto clauses may appear.
const auto *Itr =
llvm::find_if(ExistingClauses,
llvm::IsaPred<OpenACCIndependentClause, OpenACCSeqClause>);
if (Itr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_loop_spec_conflict)
<< Clause.getClauseKind() << Clause.getDirectiveKind();
SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
return nullptr;
}
return OpenACCAutoClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitIndependentClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// OpenACC 3.3 2.9:
// Only one of the seq, independent, and auto clauses may appear.
const auto *Itr = llvm::find_if(
ExistingClauses, llvm::IsaPred<OpenACCAutoClause, OpenACCSeqClause>);
if (Itr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_loop_spec_conflict)
<< Clause.getClauseKind() << Clause.getDirectiveKind();
SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
return nullptr;
}
return OpenACCIndependentClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getEndLoc());
}
ExprResult CheckGangStaticExpr(SemaOpenACC &S, Expr *E) {
if (isa<OpenACCAsteriskSizeExpr>(E))
return E;
return S.ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Gang,
E->getBeginLoc(), E);
}
bool IsOrphanLoop(OpenACCDirectiveKind DK, OpenACCDirectiveKind AssocKind) {
return DK == OpenACCDirectiveKind::Loop &&
AssocKind == OpenACCDirectiveKind::Invalid;
}
bool HasAssocKind(OpenACCDirectiveKind DK, OpenACCDirectiveKind AssocKind) {
return DK == OpenACCDirectiveKind::Loop &&
AssocKind != OpenACCDirectiveKind::Invalid;
}
ExprResult DiagIntArgInvalid(SemaOpenACC &S, Expr *E, OpenACCGangKind GK,
OpenACCClauseKind CK, OpenACCDirectiveKind DK,
OpenACCDirectiveKind AssocKind) {
S.Diag(E->getBeginLoc(), diag::err_acc_int_arg_invalid)
<< GK << CK << IsOrphanLoop(DK, AssocKind) << DK
<< HasAssocKind(DK, AssocKind) << AssocKind;
return ExprError();
}
ExprResult DiagIntArgInvalid(SemaOpenACC &S, Expr *E, StringRef TagKind,
OpenACCClauseKind CK, OpenACCDirectiveKind DK,
OpenACCDirectiveKind AssocKind) {
S.Diag(E->getBeginLoc(), diag::err_acc_int_arg_invalid)
<< TagKind << CK << IsOrphanLoop(DK, AssocKind) << DK
<< HasAssocKind(DK, AssocKind) << AssocKind;
return ExprError();
}
ExprResult CheckGangParallelExpr(SemaOpenACC &S, OpenACCDirectiveKind DK,
OpenACCDirectiveKind AssocKind,
OpenACCGangKind GK, Expr *E) {
switch (GK) {
case OpenACCGangKind::Static:
return CheckGangStaticExpr(S, E);
case OpenACCGangKind::Num:
// OpenACC 3.3 2.9.2: When the parent compute construct is a parallel
// construct, or an orphaned loop construct, the gang clause behaves as
// follows. ... The num argument is not allowed.
return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
case OpenACCGangKind::Dim: {
// OpenACC 3.3 2.9.2: When the parent compute construct is a parallel
// construct, or an orphaned loop construct, the gang clause behaves as
// follows. ... The dim argument must be a constant positive integer value
// 1, 2, or 3.
if (!E)
return ExprError();
ExprResult Res =
S.ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Gang,
E->getBeginLoc(), E);
if (!Res.isUsable())
return Res;
if (Res.get()->isInstantiationDependent())
return Res;
std::optional<llvm::APSInt> ICE =
Res.get()->getIntegerConstantExpr(S.getASTContext());
if (!ICE || *ICE <= 0 || ICE > 3) {
S.Diag(Res.get()->getBeginLoc(), diag::err_acc_gang_dim_value)
<< ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue();
return ExprError();
}
return ExprResult{
ConstantExpr::Create(S.getASTContext(), Res.get(), APValue{*ICE})};
}
}
llvm_unreachable("Unknown gang kind in gang parallel check");
}
ExprResult CheckGangKernelsExpr(SemaOpenACC &S,
ArrayRef<const OpenACCClause *> ExistingClauses,
OpenACCDirectiveKind DK,
OpenACCDirectiveKind AssocKind,
OpenACCGangKind GK, Expr *E) {
switch (GK) {
// OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
// construct, the gang clause behaves as follows. ... The dim argument is
// not allowed.
case OpenACCGangKind::Dim:
return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
case OpenACCGangKind::Num: {
// OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
// construct, the gang clause behaves as follows. ... An argument with no
// keyword or with num keyword is only allowed when num_gangs does not
// appear on the kernels construct. ... The region of a loop with the gang
// clause may not contain another loop with a gang clause unless within a
// nested compute region.
// If this is a 'combined' construct, search the list of existing clauses.
// Else we need to search the containing 'kernel'.
auto Collection = isOpenACCCombinedDirectiveKind(DK)
? ExistingClauses
: S.getActiveComputeConstructInfo().Clauses;
const auto *Itr =
llvm::find_if(Collection, llvm::IsaPred<OpenACCNumGangsClause>);
if (Itr != Collection.end()) {
S.Diag(E->getBeginLoc(), diag::err_acc_num_arg_conflict)
<< "num" << OpenACCClauseKind::Gang << DK
<< HasAssocKind(DK, AssocKind) << AssocKind
<< OpenACCClauseKind::NumGangs;
S.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
return ExprError();
}
return ExprResult{E};
}
case OpenACCGangKind::Static:
return CheckGangStaticExpr(S, E);
return ExprError();
}
llvm_unreachable("Unknown gang kind in gang kernels check");
}
ExprResult CheckGangSerialExpr(SemaOpenACC &S, OpenACCDirectiveKind DK,
OpenACCDirectiveKind AssocKind,
OpenACCGangKind GK, Expr *E) {
switch (GK) {
// 'dim' and 'num' don't really make sense on serial, and GCC rejects them
// too, so we disallow them too.
case OpenACCGangKind::Dim:
case OpenACCGangKind::Num:
return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
case OpenACCGangKind::Static:
return CheckGangStaticExpr(S, E);
}
llvm_unreachable("Unknown gang kind in gang serial check");
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitVectorClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
if (DiagIfSeqClause(Clause))
return nullptr;
// Restrictions only properly implemented on 'loop'/'combined' constructs, and
// it is the only construct that can do anything with this, so skip/treat as
// unimplemented for the routine constructs.
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Loop &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
Expr *IntExpr =
Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr;
if (IntExpr) {
if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
switch (SemaRef.getActiveComputeConstructInfo().Kind) {
case OpenACCDirectiveKind::Invalid:
case OpenACCDirectiveKind::Parallel:
// No restriction on when 'parallel' can contain an argument.
break;
case OpenACCDirectiveKind::Serial:
// GCC disallows this, and there is no real good reason for us to permit
// it, so disallow until we come up with a use case that makes sense.
DiagIntArgInvalid(SemaRef, IntExpr, "length", OpenACCClauseKind::Vector,
Clause.getDirectiveKind(),
SemaRef.getActiveComputeConstructInfo().Kind);
IntExpr = nullptr;
break;
case OpenACCDirectiveKind::Kernels: {
const auto *Itr =
llvm::find_if(SemaRef.getActiveComputeConstructInfo().Clauses,
llvm::IsaPred<OpenACCVectorLengthClause>);
if (Itr != SemaRef.getActiveComputeConstructInfo().Clauses.end()) {
SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
<< "length" << OpenACCClauseKind::Vector
<< Clause.getDirectiveKind()
<< HasAssocKind(Clause.getDirectiveKind(),
SemaRef.getActiveComputeConstructInfo().Kind)
<< SemaRef.getActiveComputeConstructInfo().Kind
<< OpenACCClauseKind::VectorLength;
SemaRef.Diag((*Itr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
IntExpr = nullptr;
}
break;
}
default:
llvm_unreachable("Non compute construct in active compute construct");
}
} else {
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::SerialLoop) {
DiagIntArgInvalid(SemaRef, IntExpr, "length", OpenACCClauseKind::Vector,
Clause.getDirectiveKind(),
SemaRef.getActiveComputeConstructInfo().Kind);
IntExpr = nullptr;
} else if (Clause.getDirectiveKind() ==
OpenACCDirectiveKind::KernelsLoop) {
const auto *Itr = llvm::find_if(
ExistingClauses, llvm::IsaPred<OpenACCVectorLengthClause>);
if (Itr != ExistingClauses.end()) {
SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
<< "length" << OpenACCClauseKind::Vector
<< Clause.getDirectiveKind()
<< HasAssocKind(Clause.getDirectiveKind(),
SemaRef.getActiveComputeConstructInfo().Kind)
<< SemaRef.getActiveComputeConstructInfo().Kind
<< OpenACCClauseKind::VectorLength;
SemaRef.Diag((*Itr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
IntExpr = nullptr;
}
}
}
}
if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
// OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
// contain a loop with a gang, worker, or vector clause unless within a
// nested compute region.
if (SemaRef.LoopVectorClauseLoc.isValid()) {
// This handles the 'inner loop' diagnostic, but we cannot set that we're
// on one of these until we get to the end of the construct.
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
<< OpenACCClauseKind::Vector << OpenACCClauseKind::Vector
<< /*skip kernels construct info*/ 0;
SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
diag::note_acc_previous_clause_here);
return nullptr;
}
}
return OpenACCVectorClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(), IntExpr,
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitWorkerClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
if (DiagIfSeqClause(Clause))
return nullptr;
// Restrictions only properly implemented on 'loop'/'combined' constructs, and
// it is the only construct that can do anything with this, so skip/treat as
// unimplemented for the routine constructs.
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Loop &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
Expr *IntExpr =
Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr;
if (IntExpr) {
if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
switch (SemaRef.getActiveComputeConstructInfo().Kind) {
case OpenACCDirectiveKind::Invalid:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
DiagIntArgInvalid(SemaRef, IntExpr, OpenACCGangKind::Num,
OpenACCClauseKind::Worker, Clause.getDirectiveKind(),
SemaRef.getActiveComputeConstructInfo().Kind);
IntExpr = nullptr;
break;
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::Kernels: {
const auto *Itr =
llvm::find_if(SemaRef.getActiveComputeConstructInfo().Clauses,
llvm::IsaPred<OpenACCNumWorkersClause>);
if (Itr != SemaRef.getActiveComputeConstructInfo().Clauses.end()) {
SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
<< "num" << OpenACCClauseKind::Worker << Clause.getDirectiveKind()
<< HasAssocKind(Clause.getDirectiveKind(),
SemaRef.getActiveComputeConstructInfo().Kind)
<< SemaRef.getActiveComputeConstructInfo().Kind
<< OpenACCClauseKind::NumWorkers;
SemaRef.Diag((*Itr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
IntExpr = nullptr;
}
break;
}
default:
llvm_unreachable("Non compute construct in active compute construct");
}
} else {
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop ||
Clause.getDirectiveKind() == OpenACCDirectiveKind::SerialLoop) {
DiagIntArgInvalid(SemaRef, IntExpr, OpenACCGangKind::Num,
OpenACCClauseKind::Worker, Clause.getDirectiveKind(),
SemaRef.getActiveComputeConstructInfo().Kind);
IntExpr = nullptr;
} else {
assert(Clause.getDirectiveKind() == OpenACCDirectiveKind::KernelsLoop &&
"Unknown combined directive kind?");
const auto *Itr = llvm::find_if(ExistingClauses,
llvm::IsaPred<OpenACCNumWorkersClause>);
if (Itr != ExistingClauses.end()) {
SemaRef.Diag(IntExpr->getBeginLoc(), diag::err_acc_num_arg_conflict)
<< "num" << OpenACCClauseKind::Worker << Clause.getDirectiveKind()
<< HasAssocKind(Clause.getDirectiveKind(),
SemaRef.getActiveComputeConstructInfo().Kind)
<< SemaRef.getActiveComputeConstructInfo().Kind
<< OpenACCClauseKind::NumWorkers;
SemaRef.Diag((*Itr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
IntExpr = nullptr;
}
}
}
}
if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
// OpenACC 3.3 2.9.3: The region of a loop with a 'worker' clause may not
// contain a loop with a gang or worker clause unless within a nested
// compute region.
if (SemaRef.LoopWorkerClauseLoc.isValid()) {
// This handles the 'inner loop' diagnostic, but we cannot set that we're
// on one of these until we get to the end of the construct.
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
<< OpenACCClauseKind::Worker << OpenACCClauseKind::Worker
<< /*skip kernels construct info*/ 0;
SemaRef.Diag(SemaRef.LoopWorkerClauseLoc,
diag::note_acc_previous_clause_here);
return nullptr;
}
// OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
// contain a loop with a gang, worker, or vector clause unless within a
// nested compute region.
if (SemaRef.LoopVectorClauseLoc.isValid()) {
// This handles the 'inner loop' diagnostic, but we cannot set that we're
// on one of these until we get to the end of the construct.
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
<< OpenACCClauseKind::Worker << OpenACCClauseKind::Vector
<< /*skip kernels construct info*/ 0;
SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
diag::note_acc_previous_clause_here);
return nullptr;
}
}
return OpenACCWorkerClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(), IntExpr,
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitGangClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
if (DiagIfSeqClause(Clause))
return nullptr;
// Restrictions only properly implemented on 'loop' constructs, and it is
// the only construct that can do anything with this, so skip/treat as
// unimplemented for the combined constructs.
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Loop &&
!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind()))
return isNotImplemented();
// OpenACC 3.3 Section 2.9.11: A reduction clause may not appear on a loop
// directive that has a gang clause and is within a compute construct that has
// a num_gangs clause with more than one explicit argument.
if ((Clause.getDirectiveKind() == OpenACCDirectiveKind::Loop &&
SemaRef.getActiveComputeConstructInfo().Kind !=
OpenACCDirectiveKind::Invalid) ||
isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
// num_gangs clause on the active compute construct.
auto ActiveComputeConstructContainer =
isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())
? ExistingClauses
: SemaRef.getActiveComputeConstructInfo().Clauses;
auto *NumGangsClauseItr = llvm::find_if(
ActiveComputeConstructContainer, llvm::IsaPred<OpenACCNumGangsClause>);
if (NumGangsClauseItr != ActiveComputeConstructContainer.end() &&
cast<OpenACCNumGangsClause>(*NumGangsClauseItr)->getIntExprs().size() >
1) {
auto *ReductionClauseItr =
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
if (ReductionClauseItr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_gang_reduction_numgangs_conflict)
<< OpenACCClauseKind::Gang << OpenACCClauseKind::Reduction
<< Clause.getDirectiveKind()
<< isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind());
SemaRef.Diag((*ReductionClauseItr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
SemaRef.Diag((*NumGangsClauseItr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
return nullptr;
}
}
}
llvm::SmallVector<OpenACCGangKind> GangKinds;
llvm::SmallVector<Expr *> IntExprs;
// Store the existing locations, so we can do duplicate checking. Index is
// the int-value of the OpenACCGangKind enum.
SourceLocation ExistingElemLoc[3];
for (unsigned I = 0; I < Clause.getIntExprs().size(); ++I) {
OpenACCGangKind GK = Clause.getGangKinds()[I];
ExprResult ER =
SemaRef.CheckGangExpr(ExistingClauses, Clause.getDirectiveKind(), GK,
Clause.getIntExprs()[I]);
if (!ER.isUsable())
continue;
// OpenACC 3.3 2.9: 'gang-arg-list' may have at most one num, one dim, and
// one static argument.
if (ExistingElemLoc[static_cast<unsigned>(GK)].isValid()) {
SemaRef.Diag(ER.get()->getBeginLoc(), diag::err_acc_gang_multiple_elt)
<< static_cast<unsigned>(GK);
SemaRef.Diag(ExistingElemLoc[static_cast<unsigned>(GK)],
diag::note_acc_previous_expr_here);
continue;
}
ExistingElemLoc[static_cast<unsigned>(GK)] = ER.get()->getBeginLoc();
GangKinds.push_back(GK);
IntExprs.push_back(ER.get());
}
if (!isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
// OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
// construct, the gang clause behaves as follows. ... The region of a loop
// with a gang clause may not contain another loop with a gang clause unless
// within a nested compute region.
if (SemaRef.LoopGangClauseOnKernel.Loc.isValid()) {
// This handles the 'inner loop' diagnostic, but we cannot set that we're
// on one of these until we get to the end of the construct.
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
<< OpenACCClauseKind::Gang << OpenACCClauseKind::Gang
<< /*kernels construct info*/ 1
<< SemaRef.LoopGangClauseOnKernel.DirKind;
SemaRef.Diag(SemaRef.LoopGangClauseOnKernel.Loc,
diag::note_acc_previous_clause_here);
return nullptr;
}
// OpenACC 3.3 2.9.3: The region of a loop with a 'worker' clause may not
// contain a loop with a gang or worker clause unless within a nested
// compute region.
if (SemaRef.LoopWorkerClauseLoc.isValid()) {
// This handles the 'inner loop' diagnostic, but we cannot set that we're
// on one of these until we get to the end of the construct.
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
<< OpenACCClauseKind::Gang << OpenACCClauseKind::Worker
<< /*!kernels construct info*/ 0;
SemaRef.Diag(SemaRef.LoopWorkerClauseLoc,
diag::note_acc_previous_clause_here);
return nullptr;
}
// OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
// contain a loop with a gang, worker, or vector clause unless within a
// nested compute region.
if (SemaRef.LoopVectorClauseLoc.isValid()) {
// This handles the 'inner loop' diagnostic, but we cannot set that we're
// on one of these until we get to the end of the construct.
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
<< OpenACCClauseKind::Gang << OpenACCClauseKind::Vector
<< /*!kernels construct info*/ 0;
SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
diag::note_acc_previous_clause_here);
return nullptr;
}
}
return SemaRef.CheckGangClause(Clause.getDirectiveKind(), ExistingClauses,
Clause.getBeginLoc(), Clause.getLParenLoc(),
GangKinds, IntExprs, Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitFinalizeClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// There isn't anything to do here, this is only valid on one construct, and
// has no associated rules.
return OpenACCFinalizeClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitIfPresentClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::HostData)
return isNotImplemented();
// There isn't anything to do here, this is only valid on one construct, and
// has no associated rules.
return OpenACCIfPresentClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitSeqClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Restrictions only properly implemented on 'loop' constructs and combined ,
// and it is the only construct that can do anything with this, so skip/treat
// as unimplemented for the routine constructs.
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Routine)
return isNotImplemented();
// OpenACC 3.3 2.9:
// Only one of the seq, independent, and auto clauses may appear.
const auto *Itr =
llvm::find_if(ExistingClauses,
llvm::IsaPred<OpenACCAutoClause, OpenACCIndependentClause>);
if (Itr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_loop_spec_conflict)
<< Clause.getClauseKind() << Clause.getDirectiveKind();
SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
return nullptr;
}
// OpenACC 3.3 2.9:
// A 'gang', 'worker', or 'vector' clause may not appear if a 'seq' clause
// appears.
Itr = llvm::find_if(ExistingClauses,
llvm::IsaPred<OpenACCGangClause, OpenACCWorkerClause,
OpenACCVectorClause>);
if (Itr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_cannot_combine)
<< Clause.getClauseKind() << (*Itr)->getClauseKind()
<< Clause.getDirectiveKind();
SemaRef.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
return nullptr;
}
return OpenACCSeqClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitReductionClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// OpenACC 3.3 Section 2.9.11: A reduction clause may not appear on a loop
// directive that has a gang clause and is within a compute construct that has
// a num_gangs clause with more than one explicit argument.
if ((Clause.getDirectiveKind() == OpenACCDirectiveKind::Loop &&
SemaRef.getActiveComputeConstructInfo().Kind !=
OpenACCDirectiveKind::Invalid) ||
isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())) {
// num_gangs clause on the active compute construct.
auto ActiveComputeConstructContainer =
isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind())
? ExistingClauses
: SemaRef.getActiveComputeConstructInfo().Clauses;
auto *NumGangsClauseItr = llvm::find_if(
ActiveComputeConstructContainer, llvm::IsaPred<OpenACCNumGangsClause>);
if (NumGangsClauseItr != ActiveComputeConstructContainer.end() &&
cast<OpenACCNumGangsClause>(*NumGangsClauseItr)->getIntExprs().size() >
1) {
auto *GangClauseItr =
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
if (GangClauseItr != ExistingClauses.end()) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_gang_reduction_numgangs_conflict)
<< OpenACCClauseKind::Reduction << OpenACCClauseKind::Gang
<< Clause.getDirectiveKind()
<< isOpenACCCombinedDirectiveKind(Clause.getDirectiveKind());
SemaRef.Diag((*GangClauseItr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
SemaRef.Diag((*NumGangsClauseItr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
return nullptr;
}
}
}
// OpenACC3.3 Section 2.9.11: If a variable is involved in a reduction that
// spans multiple nested loops where two or more of those loops have
// associated loop directives, a reduction clause containing that variable
// must appear on each of those loop directives.
//
// This can't really be implemented in the CFE, as this requires a level of
// rechability/useage analysis that we're not really wanting to get into.
// Additionally, I'm alerted that this restriction is one that the middle-end
// can just 'figure out' as an extension and isn't really necessary.
//
// OpenACC3.3 Section 2.9.11: Every 'var' in a reduction clause appearing on
// an orphaned loop construct must be private.
//
// This again is something we cannot really diagnose, as it requires we see
// all the uses/scopes of all variables referenced. The middle end/MLIR might
// be able to diagnose this.
// OpenACC 3.3 Section 2.5.4:
// A reduction clause may not appear on a parallel construct with a
// num_gangs clause that has more than one argument.
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel ||
Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop) {
auto NumGangsClauses = llvm::make_filter_range(
ExistingClauses, llvm::IsaPred<OpenACCNumGangsClause>);
for (auto *NGC : NumGangsClauses) {
unsigned NumExprs =
cast<OpenACCNumGangsClause>(NGC)->getIntExprs().size();
if (NumExprs > 1) {
SemaRef.Diag(Clause.getBeginLoc(),
diag::err_acc_reduction_num_gangs_conflict)
<< /*>1 arg in first loc=*/0 << Clause.getClauseKind()
<< Clause.getDirectiveKind() << OpenACCClauseKind::NumGangs;
SemaRef.Diag(NGC->getBeginLoc(), diag::note_acc_previous_clause_here);
return nullptr;
}
}
}
SmallVector<Expr *> ValidVars;
for (Expr *Var : Clause.getVarList()) {
ExprResult Res = SemaRef.CheckReductionVar(Clause.getDirectiveKind(),
Clause.getReductionOp(), Var);
if (Res.isUsable())
ValidVars.push_back(Res.get());
}
return SemaRef.CheckReductionClause(
ExistingClauses, Clause.getDirectiveKind(), Clause.getBeginLoc(),
Clause.getLParenLoc(), Clause.getReductionOp(), ValidVars,
Clause.getEndLoc());
}
OpenACCClause *SemaOpenACCClauseVisitor::VisitCollapseClause(
SemaOpenACC::OpenACCParsedClause &Clause) {
// Duplicates here are not really sensible. We could possible permit
// multiples if they all had the same value, but there isn't really a good
// reason to do so. Also, this simplifies the suppression of duplicates, in
// that we know if we 'find' one after instantiation, that it is the same
// clause, which simplifies instantiation/checking/etc.
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
return nullptr;
ExprResult LoopCount = SemaRef.CheckCollapseLoopCount(Clause.getLoopCount());
if (!LoopCount.isUsable())
return nullptr;
return OpenACCCollapseClause::Create(Ctx, Clause.getBeginLoc(),
Clause.getLParenLoc(), Clause.isForce(),
LoopCount.get(), Clause.getEndLoc());
}
void CollectActiveReductionClauses(
llvm::SmallVector<OpenACCReductionClause *> &ActiveClauses,
ArrayRef<OpenACCClause *> CurClauses) {
for (auto *CurClause : CurClauses) {
if (auto *RedClause = dyn_cast<OpenACCReductionClause>(CurClause);
RedClause && !RedClause->getVarList().empty())
ActiveClauses.push_back(RedClause);
}
}
// Depth needs to be preserved for all associated statements that aren't
// supposed to modify the compute/combined/loop construct information.
bool PreserveLoopRAIIDepthInAssociatedStmtRAII(OpenACCDirectiveKind DK) {
switch (DK) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::Loop:
return false;
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::HostData:
return true;
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ExitData:
llvm_unreachable("Doesn't have an associated stmt");
default:
case OpenACCDirectiveKind::Invalid:
llvm_unreachable("Unhandled directive kind?");
}
llvm_unreachable("Unhandled directive kind?");
}
} // namespace
SemaOpenACC::SemaOpenACC(Sema &S) : SemaBase(S) {}
SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII(
SemaOpenACC &S, OpenACCDirectiveKind DK, SourceLocation DirLoc,
ArrayRef<const OpenACCClause *> UnInstClauses,
ArrayRef<OpenACCClause *> Clauses)
: SemaRef(S), OldActiveComputeConstructInfo(S.ActiveComputeConstructInfo),
DirKind(DK), OldLoopGangClauseOnKernel(S.LoopGangClauseOnKernel),
OldLoopWorkerClauseLoc(S.LoopWorkerClauseLoc),
OldLoopVectorClauseLoc(S.LoopVectorClauseLoc),
OldLoopWithoutSeqInfo(S.LoopWithoutSeqInfo),
ActiveReductionClauses(S.ActiveReductionClauses),
LoopRAII(SemaRef, PreserveLoopRAIIDepthInAssociatedStmtRAII(DirKind)) {
// Compute constructs end up taking their 'loop'.
if (DirKind == OpenACCDirectiveKind::Parallel ||
DirKind == OpenACCDirectiveKind::Serial ||
DirKind == OpenACCDirectiveKind::Kernels) {
CollectActiveReductionClauses(S.ActiveReductionClauses, Clauses);
SemaRef.ActiveComputeConstructInfo.Kind = DirKind;
SemaRef.ActiveComputeConstructInfo.Clauses = Clauses;
// OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
// construct, the gang clause behaves as follows. ... The region of a loop
// with a gang clause may not contain another loop with a gang clause unless
// within a nested compute region.
//
// Implement the 'unless within a nested compute region' part.
SemaRef.LoopGangClauseOnKernel = {};
SemaRef.LoopWorkerClauseLoc = {};
SemaRef.LoopVectorClauseLoc = {};
SemaRef.LoopWithoutSeqInfo = {};
} else if (DirKind == OpenACCDirectiveKind::ParallelLoop ||
DirKind == OpenACCDirectiveKind::SerialLoop ||
DirKind == OpenACCDirectiveKind::KernelsLoop) {
SemaRef.ActiveComputeConstructInfo.Kind = DirKind;
SemaRef.ActiveComputeConstructInfo.Clauses = Clauses;
CollectActiveReductionClauses(S.ActiveReductionClauses, Clauses);
SetCollapseInfoBeforeAssociatedStmt(UnInstClauses, Clauses);
SetTileInfoBeforeAssociatedStmt(UnInstClauses, Clauses);
SemaRef.LoopGangClauseOnKernel = {};
SemaRef.LoopWorkerClauseLoc = {};
SemaRef.LoopVectorClauseLoc = {};
// Set the active 'loop' location if there isn't a 'seq' on it, so we can
// diagnose the for loops.
SemaRef.LoopWithoutSeqInfo = {};
if (Clauses.end() ==
llvm::find_if(Clauses, llvm::IsaPred<OpenACCSeqClause>))
SemaRef.LoopWithoutSeqInfo = {DirKind, DirLoc};
// OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
// construct, the gang clause behaves as follows. ... The region of a loop
// with a gang clause may not contain another loop with a gang clause unless
// within a nested compute region.
//
// We don't bother doing this when this is a template instantiation, as
// there is no reason to do these checks: the existance of a
// gang/kernels/etc cannot be dependent.
if (DirKind == OpenACCDirectiveKind::KernelsLoop && UnInstClauses.empty()) {
// This handles the 'outer loop' part of this.
auto *Itr = llvm::find_if(Clauses, llvm::IsaPred<OpenACCGangClause>);
if (Itr != Clauses.end())
SemaRef.LoopGangClauseOnKernel = {(*Itr)->getBeginLoc(), DirKind};
}
if (UnInstClauses.empty()) {
auto *Itr = llvm::find_if(Clauses, llvm::IsaPred<OpenACCWorkerClause>);
if (Itr != Clauses.end())
SemaRef.LoopWorkerClauseLoc = (*Itr)->getBeginLoc();
auto *Itr2 = llvm::find_if(Clauses, llvm::IsaPred<OpenACCVectorClause>);
if (Itr2 != Clauses.end())
SemaRef.LoopVectorClauseLoc = (*Itr2)->getBeginLoc();
}
} else if (DirKind == OpenACCDirectiveKind::Loop) {
CollectActiveReductionClauses(S.ActiveReductionClauses, Clauses);
SetCollapseInfoBeforeAssociatedStmt(UnInstClauses, Clauses);
SetTileInfoBeforeAssociatedStmt(UnInstClauses, Clauses);
// Set the active 'loop' location if there isn't a 'seq' on it, so we can
// diagnose the for loops.
SemaRef.LoopWithoutSeqInfo = {};
if (Clauses.end() ==
llvm::find_if(Clauses, llvm::IsaPred<OpenACCSeqClause>))
SemaRef.LoopWithoutSeqInfo = {DirKind, DirLoc};
// OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
// construct, the gang clause behaves as follows. ... The region of a loop
// with a gang clause may not contain another loop with a gang clause unless
// within a nested compute region.
//
// We don't bother doing this when this is a template instantiation, as
// there is no reason to do these checks: the existance of a
// gang/kernels/etc cannot be dependent.
if (SemaRef.getActiveComputeConstructInfo().Kind ==
OpenACCDirectiveKind::Kernels &&
UnInstClauses.empty()) {
// This handles the 'outer loop' part of this.
auto *Itr = llvm::find_if(Clauses, llvm::IsaPred<OpenACCGangClause>);
if (Itr != Clauses.end())
SemaRef.LoopGangClauseOnKernel = {(*Itr)->getBeginLoc(),
OpenACCDirectiveKind::Kernels};
}
if (UnInstClauses.empty()) {
auto *Itr = llvm::find_if(Clauses, llvm::IsaPred<OpenACCWorkerClause>);
if (Itr != Clauses.end())
SemaRef.LoopWorkerClauseLoc = (*Itr)->getBeginLoc();
auto *Itr2 = llvm::find_if(Clauses, llvm::IsaPred<OpenACCVectorClause>);
if (Itr2 != Clauses.end())
SemaRef.LoopVectorClauseLoc = (*Itr2)->getBeginLoc();
}
}
}
void SemaOpenACC::AssociatedStmtRAII::SetCollapseInfoBeforeAssociatedStmt(
ArrayRef<const OpenACCClause *> UnInstClauses,
ArrayRef<OpenACCClause *> Clauses) {
// Reset this checking for loops that aren't covered in a RAII object.
SemaRef.LoopInfo.CurLevelHasLoopAlready = false;
SemaRef.CollapseInfo.CollapseDepthSatisfied = true;
SemaRef.TileInfo.TileDepthSatisfied = true;
// We make sure to take an optional list of uninstantiated clauses, so that
// we can check to make sure we don't 'double diagnose' in the event that
// the value of 'N' was not dependent in a template. We also ensure during
// Sema that there is only 1 collapse on each construct, so we can count on
// the fact that if both find a 'collapse', that they are the same one.
auto *CollapseClauseItr =
llvm::find_if(Clauses, llvm::IsaPred<OpenACCCollapseClause>);
auto *UnInstCollapseClauseItr =
llvm::find_if(UnInstClauses, llvm::IsaPred<OpenACCCollapseClause>);
if (Clauses.end() == CollapseClauseItr)
return;
OpenACCCollapseClause *CollapseClause =
cast<OpenACCCollapseClause>(*CollapseClauseItr);
SemaRef.CollapseInfo.ActiveCollapse = CollapseClause;
Expr *LoopCount = CollapseClause->getLoopCount();
// If the loop count is still instantiation dependent, setting the depth
// counter isn't necessary, so return here.
if (!LoopCount || LoopCount->isInstantiationDependent())
return;
// Suppress diagnostics if we've done a 'transform' where the previous version
// wasn't dependent, meaning we already diagnosed it.
if (UnInstCollapseClauseItr != UnInstClauses.end() &&
!cast<OpenACCCollapseClause>(*UnInstCollapseClauseItr)
->getLoopCount()
->isInstantiationDependent())
return;
SemaRef.CollapseInfo.CollapseDepthSatisfied = false;
SemaRef.CollapseInfo.CurCollapseCount =
cast<ConstantExpr>(LoopCount)->getResultAsAPSInt();
SemaRef.CollapseInfo.DirectiveKind = DirKind;
}
void SemaOpenACC::AssociatedStmtRAII::SetTileInfoBeforeAssociatedStmt(
ArrayRef<const OpenACCClause *> UnInstClauses,
ArrayRef<OpenACCClause *> Clauses) {
// We don't diagnose if this is during instantiation, since the only thing we
// care about is the number of arguments, which we can figure out without
// instantiation, so we don't want to double-diagnose.
if (UnInstClauses.size() > 0)
return;
auto *TileClauseItr =
llvm::find_if(Clauses, llvm::IsaPred<OpenACCTileClause>);
if (Clauses.end() == TileClauseItr)
return;
OpenACCTileClause *TileClause = cast<OpenACCTileClause>(*TileClauseItr);
SemaRef.TileInfo.ActiveTile = TileClause;
SemaRef.TileInfo.TileDepthSatisfied = false;
SemaRef.TileInfo.CurTileCount = TileClause->getSizeExprs().size();
SemaRef.TileInfo.DirectiveKind = DirKind;
}
SemaOpenACC::AssociatedStmtRAII::~AssociatedStmtRAII() {
if (DirKind == OpenACCDirectiveKind::Parallel ||
DirKind == OpenACCDirectiveKind::Serial ||
DirKind == OpenACCDirectiveKind::Kernels ||
DirKind == OpenACCDirectiveKind::Loop ||
DirKind == OpenACCDirectiveKind::ParallelLoop ||
DirKind == OpenACCDirectiveKind::SerialLoop ||
DirKind == OpenACCDirectiveKind::KernelsLoop) {
SemaRef.ActiveComputeConstructInfo = OldActiveComputeConstructInfo;
SemaRef.LoopGangClauseOnKernel = OldLoopGangClauseOnKernel;
SemaRef.LoopWorkerClauseLoc = OldLoopWorkerClauseLoc;
SemaRef.LoopVectorClauseLoc = OldLoopVectorClauseLoc;
SemaRef.LoopWithoutSeqInfo = OldLoopWithoutSeqInfo;
SemaRef.ActiveReductionClauses.swap(ActiveReductionClauses);
} else if (DirKind == OpenACCDirectiveKind::Data ||
DirKind == OpenACCDirectiveKind::HostData) {
// Intentionally doesn't reset the Loop, Compute Construct, or reduction
// effects.
}
}
OpenACCClause *
SemaOpenACC::ActOnClause(ArrayRef<const OpenACCClause *> ExistingClauses,
OpenACCParsedClause &Clause) {
if (Clause.getClauseKind() == OpenACCClauseKind::Invalid)
return nullptr;
// Diagnose that we don't support this clause on this directive.
if (!doesClauseApplyToDirective(Clause.getDirectiveKind(),
Clause.getClauseKind())) {
Diag(Clause.getBeginLoc(), diag::err_acc_clause_appertainment)
<< Clause.getDirectiveKind() << Clause.getClauseKind();
return nullptr;
}
if (const auto *DevTypeClause =
llvm::find_if(ExistingClauses,
[&](const OpenACCClause *C) {
return isa<OpenACCDeviceTypeClause>(C);
});
DevTypeClause != ExistingClauses.end()) {
if (checkValidAfterDeviceType(
*this, *cast<OpenACCDeviceTypeClause>(*DevTypeClause), Clause))
return nullptr;
}
SemaOpenACCClauseVisitor Visitor{*this, ExistingClauses};
OpenACCClause *Result = Visitor.Visit(Clause);
assert((!Result || Result->getClauseKind() == Clause.getClauseKind()) &&
"Created wrong clause?");
if (Visitor.diagNotImplemented())
Diag(Clause.getBeginLoc(), diag::warn_acc_clause_unimplemented)
<< Clause.getClauseKind();
return Result;
}
namespace {
// Return true if the two vars refer to the same variable, for the purposes of
// equality checking.
bool areVarsEqual(Expr *VarExpr1, Expr *VarExpr2) {
if (VarExpr1->isInstantiationDependent() ||
VarExpr2->isInstantiationDependent())
return false;
VarExpr1 = VarExpr1->IgnoreParenCasts();
VarExpr2 = VarExpr2->IgnoreParenCasts();
// Legal expressions can be: Scalar variable reference, sub-array, array
// element, or composite variable member.
// Sub-array.
if (isa<ArraySectionExpr>(VarExpr1)) {
auto *Expr2AS = dyn_cast<ArraySectionExpr>(VarExpr2);
if (!Expr2AS)
return false;
auto *Expr1AS = cast<ArraySectionExpr>(VarExpr1);
if (!areVarsEqual(Expr1AS->getBase(), Expr2AS->getBase()))
return false;
// We could possibly check to see if the ranges aren't overlapping, but it
// isn't clear that the rules allow this.
return true;
}
// Array-element.
if (isa<ArraySubscriptExpr>(VarExpr1)) {
auto *Expr2AS = dyn_cast<ArraySubscriptExpr>(VarExpr2);
if (!Expr2AS)
return false;
auto *Expr1AS = cast<ArraySubscriptExpr>(VarExpr1);
if (!areVarsEqual(Expr1AS->getBase(), Expr2AS->getBase()))
return false;
// We could possibly check to see if the elements referenced aren't the
// same, but it isn't clear by reading of the standard that this is allowed
// (and that the 'var' refered to isn't the array).
return true;
}
// Scalar variable reference, or composite variable.
if (isa<DeclRefExpr>(VarExpr1)) {
auto *Expr2DRE = dyn_cast<DeclRefExpr>(VarExpr2);
if (!Expr2DRE)
return false;
auto *Expr1DRE = cast<DeclRefExpr>(VarExpr1);
return Expr1DRE->getDecl()->getMostRecentDecl() ==
Expr2DRE->getDecl()->getMostRecentDecl();
}
llvm_unreachable("Unknown variable type encountered");
}
} // namespace
/// OpenACC 3.3 section 2.5.15:
/// At a mininmum, the supported data types include ... the numerical data types
/// in C, C++, and Fortran.
///
/// If the reduction var is a composite variable, each
/// member of the composite variable must be a supported datatype for the
/// reduction operation.
ExprResult SemaOpenACC::CheckReductionVar(OpenACCDirectiveKind DirectiveKind,
OpenACCReductionOperator ReductionOp,
Expr *VarExpr) {
VarExpr = VarExpr->IgnoreParenCasts();
auto TypeIsValid = [](QualType Ty) {
return Ty->isDependentType() || Ty->isScalarType();
};
if (isa<ArraySectionExpr>(VarExpr)) {
Expr *ASExpr = VarExpr;
QualType BaseTy = ArraySectionExpr::getBaseOriginalType(ASExpr);
QualType EltTy = getASTContext().getBaseElementType(BaseTy);
if (!TypeIsValid(EltTy)) {
Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_type)
<< EltTy << /*Sub array base type*/ 1;
return ExprError();
}
} else if (auto *RD = VarExpr->getType()->getAsRecordDecl()) {
if (!RD->isStruct() && !RD->isClass()) {
Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type)
<< /*not class or struct*/ 0 << VarExpr->getType();
return ExprError();
}
if (!RD->isCompleteDefinition()) {
Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type)
<< /*incomplete*/ 1 << VarExpr->getType();
return ExprError();
}
if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
CXXRD && !CXXRD->isAggregate()) {
Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type)
<< /*aggregate*/ 2 << VarExpr->getType();
return ExprError();
}
for (FieldDecl *FD : RD->fields()) {
if (!TypeIsValid(FD->getType())) {
Diag(VarExpr->getExprLoc(),
diag::err_acc_reduction_composite_member_type);
Diag(FD->getLocation(), diag::note_acc_reduction_composite_member_loc);
return ExprError();
}
}
} else if (!TypeIsValid(VarExpr->getType())) {
Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_type)
<< VarExpr->getType() << /*Sub array base type*/ 0;
return ExprError();
}
// OpenACC3.3: 2.9.11: Reduction clauses on nested constructs for the same
// reduction 'var' must have the same reduction operator.
if (!VarExpr->isInstantiationDependent()) {
for (const OpenACCReductionClause *RClause : ActiveReductionClauses) {
if (RClause->getReductionOp() == ReductionOp)
break;
for (Expr *OldVarExpr : RClause->getVarList()) {
if (OldVarExpr->isInstantiationDependent())
continue;
if (areVarsEqual(VarExpr, OldVarExpr)) {
Diag(VarExpr->getExprLoc(), diag::err_reduction_op_mismatch)
<< ReductionOp << RClause->getReductionOp();
Diag(OldVarExpr->getExprLoc(), diag::note_acc_previous_clause_here);
return ExprError();
}
}
}
}
return VarExpr;
}
void SemaOpenACC::ActOnConstruct(OpenACCDirectiveKind K,
SourceLocation DirLoc) {
// Start an evaluation context to parse the clause arguments on.
SemaRef.PushExpressionEvaluationContext(
Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
switch (K) {
case OpenACCDirectiveKind::Invalid:
// Nothing to do here, an invalid kind has nothing we can check here. We
// want to continue parsing clauses as far as we can, so we will just
// ensure that we can still work and don't check any construct-specific
// rules anywhere.
break;
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ExitData:
case OpenACCDirectiveKind::HostData:
// Nothing to do here, there is no real legalization that needs to happen
// here as these constructs do not take any arguments.
break;
default:
Diag(DirLoc, diag::warn_acc_construct_unimplemented) << K;
break;
}
}
ExprResult SemaOpenACC::ActOnIntExpr(OpenACCDirectiveKind DK,
OpenACCClauseKind CK, SourceLocation Loc,
Expr *IntExpr) {
assert(((DK != OpenACCDirectiveKind::Invalid &&
CK == OpenACCClauseKind::Invalid) ||
(DK == OpenACCDirectiveKind::Invalid &&
CK != OpenACCClauseKind::Invalid) ||
(DK == OpenACCDirectiveKind::Invalid &&
CK == OpenACCClauseKind::Invalid)) &&
"Only one of directive or clause kind should be provided");
class IntExprConverter : public Sema::ICEConvertDiagnoser {
OpenACCDirectiveKind DirectiveKind;
OpenACCClauseKind ClauseKind;
Expr *IntExpr;
// gets the index into the diagnostics so we can use this for clauses,
// directives, and sub array.s
unsigned getDiagKind() const {
if (ClauseKind != OpenACCClauseKind::Invalid)
return 0;
if (DirectiveKind != OpenACCDirectiveKind::Invalid)
return 1;
return 2;
}
public:
IntExprConverter(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
Expr *IntExpr)
: ICEConvertDiagnoser(/*AllowScopedEnumerations=*/false,
/*Suppress=*/false,
/*SuppressConversion=*/true),
DirectiveKind(DK), ClauseKind(CK), IntExpr(IntExpr) {}
bool match(QualType T) override {
// OpenACC spec just calls this 'integer expression' as having an
// 'integer type', so fall back on C99's 'integer type'.
return T->isIntegerType();
}
SemaBase::SemaDiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc,
QualType T) override {
return S.Diag(Loc, diag::err_acc_int_expr_requires_integer)
<< getDiagKind() << ClauseKind << DirectiveKind << T;
}
SemaBase::SemaDiagnosticBuilder
diagnoseIncomplete(Sema &S, SourceLocation Loc, QualType T) override {
return S.Diag(Loc, diag::err_acc_int_expr_incomplete_class_type)
<< T << IntExpr->getSourceRange();
}
SemaBase::SemaDiagnosticBuilder
diagnoseExplicitConv(Sema &S, SourceLocation Loc, QualType T,
QualType ConvTy) override {
return S.Diag(Loc, diag::err_acc_int_expr_explicit_conversion)
<< T << ConvTy;
}
SemaBase::SemaDiagnosticBuilder noteExplicitConv(Sema &S,
CXXConversionDecl *Conv,
QualType ConvTy) override {
return S.Diag(Conv->getLocation(), diag::note_acc_int_expr_conversion)
<< ConvTy->isEnumeralType() << ConvTy;
}
SemaBase::SemaDiagnosticBuilder
diagnoseAmbiguous(Sema &S, SourceLocation Loc, QualType T) override {
return S.Diag(Loc, diag::err_acc_int_expr_multiple_conversions) << T;
}
SemaBase::SemaDiagnosticBuilder
noteAmbiguous(Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override {
return S.Diag(Conv->getLocation(), diag::note_acc_int_expr_conversion)
<< ConvTy->isEnumeralType() << ConvTy;
}
SemaBase::SemaDiagnosticBuilder
diagnoseConversion(Sema &S, SourceLocation Loc, QualType T,
QualType ConvTy) override {
llvm_unreachable("conversion functions are permitted");
}
} IntExprDiagnoser(DK, CK, IntExpr);
if (!IntExpr)
return ExprError();
ExprResult IntExprResult = SemaRef.PerformContextualImplicitConversion(
Loc, IntExpr, IntExprDiagnoser);
if (IntExprResult.isInvalid())
return ExprError();
IntExpr = IntExprResult.get();
if (!IntExpr->isTypeDependent() && !IntExpr->getType()->isIntegerType())
return ExprError();
// TODO OpenACC: Do we want to perform usual unary conversions here? When
// doing codegen we might find that is necessary, but skip it for now.
return IntExpr;
}
bool SemaOpenACC::CheckVarIsPointerType(OpenACCClauseKind ClauseKind,
Expr *VarExpr) {
// We already know that VarExpr is a proper reference to a variable, so we
// should be able to just take the type of the expression to get the type of
// the referenced variable.
// We've already seen an error, don't diagnose anything else.
if (!VarExpr || VarExpr->containsErrors())
return false;
if (isa<ArraySectionExpr>(VarExpr->IgnoreParenImpCasts()) ||
VarExpr->hasPlaceholderType(BuiltinType::ArraySection)) {
Diag(VarExpr->getExprLoc(), diag::err_array_section_use) << /*OpenACC=*/0;
Diag(VarExpr->getExprLoc(), diag::note_acc_expected_pointer_var);
return true;
}
QualType Ty = VarExpr->getType();
Ty = Ty.getNonReferenceType().getUnqualifiedType();
// Nothing we can do if this is a dependent type.
if (Ty->isDependentType())
return false;
if (!Ty->isPointerType())
return Diag(VarExpr->getExprLoc(), diag::err_acc_var_not_pointer_type)
<< ClauseKind << Ty;
return false;
}
ExprResult SemaOpenACC::ActOnVar(OpenACCClauseKind CK, Expr *VarExpr) {
Expr *CurVarExpr = VarExpr->IgnoreParenImpCasts();
// Sub-arrays/subscript-exprs are fine as long as the base is a
// VarExpr/MemberExpr. So strip all of those off.
while (isa<ArraySectionExpr, ArraySubscriptExpr>(CurVarExpr)) {
if (auto *SubScrpt = dyn_cast<ArraySubscriptExpr>(CurVarExpr))
CurVarExpr = SubScrpt->getBase()->IgnoreParenImpCasts();
else
CurVarExpr =
cast<ArraySectionExpr>(CurVarExpr)->getBase()->IgnoreParenImpCasts();
}
// References to a VarDecl are fine.
if (const auto *DRE = dyn_cast<DeclRefExpr>(CurVarExpr)) {
if (isa<VarDecl, NonTypeTemplateParmDecl>(
DRE->getFoundDecl()->getCanonicalDecl()))
return VarExpr;
}
// If CK is a Reduction, this special cases for OpenACC3.3 2.5.15: "A var in a
// reduction clause must be a scalar variable name, an aggregate variable
// name, an array element, or a subarray.
// A MemberExpr that references a Field is valid.
if (CK != OpenACCClauseKind::Reduction) {
if (const auto *ME = dyn_cast<MemberExpr>(CurVarExpr)) {
if (isa<FieldDecl>(ME->getMemberDecl()->getCanonicalDecl()))
return VarExpr;
}
}
// Referring to 'this' is always OK.
if (isa<CXXThisExpr>(CurVarExpr))
return VarExpr;
// Nothing really we can do here, as these are dependent. So just return they
// are valid.
if (isa<DependentScopeDeclRefExpr>(CurVarExpr) ||
(CK != OpenACCClauseKind::Reduction &&
isa<CXXDependentScopeMemberExpr>(CurVarExpr)))
return VarExpr;
// There isn't really anything we can do in the case of a recovery expr, so
// skip the diagnostic rather than produce a confusing diagnostic.
if (isa<RecoveryExpr>(CurVarExpr))
return ExprError();
Diag(VarExpr->getExprLoc(), diag::err_acc_not_a_var_ref)
<< (CK != OpenACCClauseKind::Reduction);
return ExprError();
}
ExprResult SemaOpenACC::ActOnArraySectionExpr(Expr *Base, SourceLocation LBLoc,
Expr *LowerBound,
SourceLocation ColonLoc,
Expr *Length,
SourceLocation RBLoc) {
ASTContext &Context = getASTContext();
// Handle placeholders.
if (Base->hasPlaceholderType() &&
!Base->hasPlaceholderType(BuiltinType::ArraySection)) {
ExprResult Result = SemaRef.CheckPlaceholderExpr(Base);
if (Result.isInvalid())
return ExprError();
Base = Result.get();
}
if (LowerBound && LowerBound->getType()->isNonOverloadPlaceholderType()) {
ExprResult Result = SemaRef.CheckPlaceholderExpr(LowerBound);
if (Result.isInvalid())
return ExprError();
Result = SemaRef.DefaultLvalueConversion(Result.get());
if (Result.isInvalid())
return ExprError();
LowerBound = Result.get();
}
if (Length && Length->getType()->isNonOverloadPlaceholderType()) {
ExprResult Result = SemaRef.CheckPlaceholderExpr(Length);
if (Result.isInvalid())
return ExprError();
Result = SemaRef.DefaultLvalueConversion(Result.get());
if (Result.isInvalid())
return ExprError();
Length = Result.get();
}
// Check the 'base' value, it must be an array or pointer type, and not to/of
// a function type.
QualType OriginalBaseTy = ArraySectionExpr::getBaseOriginalType(Base);
QualType ResultTy;
if (!Base->isTypeDependent()) {
if (OriginalBaseTy->isAnyPointerType()) {
ResultTy = OriginalBaseTy->getPointeeType();
} else if (OriginalBaseTy->isArrayType()) {
ResultTy = OriginalBaseTy->getAsArrayTypeUnsafe()->getElementType();
} else {
return ExprError(
Diag(Base->getExprLoc(), diag::err_acc_typecheck_subarray_value)
<< Base->getSourceRange());
}
if (ResultTy->isFunctionType()) {
Diag(Base->getExprLoc(), diag::err_acc_subarray_function_type)
<< ResultTy << Base->getSourceRange();
return ExprError();
}
if (SemaRef.RequireCompleteType(Base->getExprLoc(), ResultTy,
diag::err_acc_subarray_incomplete_type,
Base))
return ExprError();
if (!Base->hasPlaceholderType(BuiltinType::ArraySection)) {
ExprResult Result = SemaRef.DefaultFunctionArrayLvalueConversion(Base);
if (Result.isInvalid())
return ExprError();
Base = Result.get();
}
}
auto GetRecovery = [&](Expr *E, QualType Ty) {
ExprResult Recovery =
SemaRef.CreateRecoveryExpr(E->getBeginLoc(), E->getEndLoc(), E, Ty);
return Recovery.isUsable() ? Recovery.get() : nullptr;
};
// Ensure both of the expressions are int-exprs.
if (LowerBound && !LowerBound->isTypeDependent()) {
ExprResult LBRes =
ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Invalid,
LowerBound->getExprLoc(), LowerBound);
if (LBRes.isUsable())
LBRes = SemaRef.DefaultLvalueConversion(LBRes.get());
LowerBound =
LBRes.isUsable() ? LBRes.get() : GetRecovery(LowerBound, Context.IntTy);
}
if (Length && !Length->isTypeDependent()) {
ExprResult LenRes =
ActOnIntExpr(OpenACCDirectiveKind::Invalid, OpenACCClauseKind::Invalid,
Length->getExprLoc(), Length);
if (LenRes.isUsable())
LenRes = SemaRef.DefaultLvalueConversion(LenRes.get());
Length =
LenRes.isUsable() ? LenRes.get() : GetRecovery(Length, Context.IntTy);
}
// Length is required if the base type is not an array of known bounds.
if (!Length && (OriginalBaseTy.isNull() ||
(!OriginalBaseTy->isDependentType() &&
!OriginalBaseTy->isConstantArrayType() &&
!OriginalBaseTy->isDependentSizedArrayType()))) {
bool IsArray = !OriginalBaseTy.isNull() && OriginalBaseTy->isArrayType();
Diag(ColonLoc, diag::err_acc_subarray_no_length) << IsArray;
// Fill in a dummy 'length' so that when we instantiate this we don't
// double-diagnose here.
ExprResult Recovery = SemaRef.CreateRecoveryExpr(
ColonLoc, SourceLocation(), ArrayRef<Expr *>(), Context.IntTy);
Length = Recovery.isUsable() ? Recovery.get() : nullptr;
}
// Check the values of each of the arguments, they cannot be negative(we
// assume), and if the array bound is known, must be within range. As we do
// so, do our best to continue with evaluation, we can set the
// value/expression to nullptr/nullopt if they are invalid, and treat them as
// not present for the rest of evaluation.
// We don't have to check for dependence, because the dependent size is
// represented as a different AST node.
std::optional<llvm::APSInt> BaseSize;
if (!OriginalBaseTy.isNull() && OriginalBaseTy->isConstantArrayType()) {
const auto *ArrayTy = Context.getAsConstantArrayType(OriginalBaseTy);
BaseSize = ArrayTy->getSize();
}
auto GetBoundValue = [&](Expr *E) -> std::optional<llvm::APSInt> {
if (!E || E->isInstantiationDependent())
return std::nullopt;
Expr::EvalResult Res;
if (!E->EvaluateAsInt(Res, Context))
return std::nullopt;
return Res.Val.getInt();
};
std::optional<llvm::APSInt> LowerBoundValue = GetBoundValue(LowerBound);
std::optional<llvm::APSInt> LengthValue = GetBoundValue(Length);
// Check lower bound for negative or out of range.
if (LowerBoundValue.has_value()) {
if (LowerBoundValue->isNegative()) {
Diag(LowerBound->getExprLoc(), diag::err_acc_subarray_negative)
<< /*LowerBound=*/0 << toString(*LowerBoundValue, /*Radix=*/10);
LowerBoundValue.reset();
LowerBound = GetRecovery(LowerBound, LowerBound->getType());
} else if (BaseSize.has_value() &&
llvm::APSInt::compareValues(*LowerBoundValue, *BaseSize) >= 0) {
// Lower bound (start index) must be less than the size of the array.
Diag(LowerBound->getExprLoc(), diag::err_acc_subarray_out_of_range)
<< /*LowerBound=*/0 << toString(*LowerBoundValue, /*Radix=*/10)
<< toString(*BaseSize, /*Radix=*/10);
LowerBoundValue.reset();
LowerBound = GetRecovery(LowerBound, LowerBound->getType());
}
}
// Check length for negative or out of range.
if (LengthValue.has_value()) {
if (LengthValue->isNegative()) {
Diag(Length->getExprLoc(), diag::err_acc_subarray_negative)
<< /*Length=*/1 << toString(*LengthValue, /*Radix=*/10);
LengthValue.reset();
Length = GetRecovery(Length, Length->getType());
} else if (BaseSize.has_value() &&
llvm::APSInt::compareValues(*LengthValue, *BaseSize) > 0) {
// Length must be lessthan or EQUAL to the size of the array.
Diag(Length->getExprLoc(), diag::err_acc_subarray_out_of_range)
<< /*Length=*/1 << toString(*LengthValue, /*Radix=*/10)
<< toString(*BaseSize, /*Radix=*/10);
LengthValue.reset();
Length = GetRecovery(Length, Length->getType());
}
}
// Adding two APSInts requires matching sign, so extract that here.
auto AddAPSInt = [](llvm::APSInt LHS, llvm::APSInt RHS) -> llvm::APSInt {
if (LHS.isSigned() == RHS.isSigned())
return LHS + RHS;
unsigned Width = std::max(LHS.getBitWidth(), RHS.getBitWidth()) + 1;
return llvm::APSInt(LHS.sext(Width) + RHS.sext(Width), /*Signed=*/true);
};
// If we know all 3 values, we can diagnose that the total value would be out
// of range.
if (BaseSize.has_value() && LowerBoundValue.has_value() &&
LengthValue.has_value() &&
llvm::APSInt::compareValues(AddAPSInt(*LowerBoundValue, *LengthValue),
*BaseSize) > 0) {
Diag(Base->getExprLoc(),
diag::err_acc_subarray_base_plus_length_out_of_range)
<< toString(*LowerBoundValue, /*Radix=*/10)
<< toString(*LengthValue, /*Radix=*/10)
<< toString(*BaseSize, /*Radix=*/10);
LowerBoundValue.reset();
LowerBound = GetRecovery(LowerBound, LowerBound->getType());
LengthValue.reset();
Length = GetRecovery(Length, Length->getType());
}
// If any part of the expression is dependent, return a dependent sub-array.
QualType ArrayExprTy = Context.ArraySectionTy;
if (Base->isTypeDependent() ||
(LowerBound && LowerBound->isInstantiationDependent()) ||
(Length && Length->isInstantiationDependent()))
ArrayExprTy = Context.DependentTy;
return new (Context)
ArraySectionExpr(Base, LowerBound, Length, ArrayExprTy, VK_LValue,
OK_Ordinary, ColonLoc, RBLoc);
}
ExprResult SemaOpenACC::CheckCollapseLoopCount(Expr *LoopCount) {
if (!LoopCount)
return ExprError();
assert((LoopCount->isInstantiationDependent() ||
LoopCount->getType()->isIntegerType()) &&
"Loop argument non integer?");
// If this is dependent, there really isn't anything we can check.
if (LoopCount->isInstantiationDependent())
return ExprResult{LoopCount};
std::optional<llvm::APSInt> ICE =
LoopCount->getIntegerConstantExpr(getASTContext());
// OpenACC 3.3: 2.9.1
// The argument to the collapse clause must be a constant positive integer
// expression.
if (!ICE || *ICE <= 0) {
Diag(LoopCount->getBeginLoc(), diag::err_acc_collapse_loop_count)
<< ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue();
return ExprError();
}
return ExprResult{
ConstantExpr::Create(getASTContext(), LoopCount, APValue{*ICE})};
}
ExprResult
SemaOpenACC::CheckGangExpr(ArrayRef<const OpenACCClause *> ExistingClauses,
OpenACCDirectiveKind DK, OpenACCGangKind GK,
Expr *E) {
// There are two cases for the enforcement here: the 'current' directive is a
// 'loop', where we need to check the active compute construct kind, or the
// current directive is a 'combined' construct, where we have to check the
// current one.
switch (DK) {
case OpenACCDirectiveKind::ParallelLoop:
return CheckGangParallelExpr(*this, DK, ActiveComputeConstructInfo.Kind, GK,
E);
case OpenACCDirectiveKind::SerialLoop:
return CheckGangSerialExpr(*this, DK, ActiveComputeConstructInfo.Kind, GK,
E);
case OpenACCDirectiveKind::KernelsLoop:
return CheckGangKernelsExpr(*this, ExistingClauses, DK,
ActiveComputeConstructInfo.Kind, GK, E);
case OpenACCDirectiveKind::Loop:
switch (ActiveComputeConstructInfo.Kind) {
case OpenACCDirectiveKind::Invalid:
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::ParallelLoop:
return CheckGangParallelExpr(*this, DK, ActiveComputeConstructInfo.Kind,
GK, E);
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::Serial:
return CheckGangSerialExpr(*this, DK, ActiveComputeConstructInfo.Kind, GK,
E);
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::Kernels:
return CheckGangKernelsExpr(*this, ExistingClauses, DK,
ActiveComputeConstructInfo.Kind, GK, E);
default:
llvm_unreachable("Non compute construct in active compute construct?");
}
default:
// TODO: OpenACC: when we implement this on 'routine', we'll have to
// implement its checking here.
llvm_unreachable("Invalid directive kind for a Gang clause");
}
llvm_unreachable("Compute construct directive not handled?");
}
OpenACCClause *
SemaOpenACC::CheckGangClause(OpenACCDirectiveKind DirKind,
ArrayRef<const OpenACCClause *> ExistingClauses,
SourceLocation BeginLoc, SourceLocation LParenLoc,
ArrayRef<OpenACCGangKind> GangKinds,
ArrayRef<Expr *> IntExprs, SourceLocation EndLoc) {
// OpenACC 3.3 2.9.11: A reduction clause may not appear on a loop directive
// that has a gang clause with a dim: argument whose value is greater than 1.
const auto *ReductionItr =
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCReductionClause>);
if (ReductionItr != ExistingClauses.end()) {
const auto GangZip = llvm::zip_equal(GangKinds, IntExprs);
const auto GangItr = llvm::find_if(GangZip, [](const auto &Tuple) {
return std::get<0>(Tuple) == OpenACCGangKind::Dim;
});
if (GangItr != GangZip.end()) {
const Expr *DimExpr = std::get<1>(*GangItr);
assert(
(DimExpr->isInstantiationDependent() || isa<ConstantExpr>(DimExpr)) &&
"Improperly formed gang argument");
if (const auto *DimVal = dyn_cast<ConstantExpr>(DimExpr);
DimVal && DimVal->getResultAsAPSInt() > 1) {
Diag(DimVal->getBeginLoc(), diag::err_acc_gang_reduction_conflict)
<< /*gang/reduction=*/0 << DirKind;
Diag((*ReductionItr)->getBeginLoc(),
diag::note_acc_previous_clause_here);
return nullptr;
}
}
}
return OpenACCGangClause::Create(getASTContext(), BeginLoc, LParenLoc,
GangKinds, IntExprs, EndLoc);
}
OpenACCClause *SemaOpenACC::CheckReductionClause(
ArrayRef<const OpenACCClause *> ExistingClauses,
OpenACCDirectiveKind DirectiveKind, SourceLocation BeginLoc,
SourceLocation LParenLoc, OpenACCReductionOperator ReductionOp,
ArrayRef<Expr *> Vars, SourceLocation EndLoc) {
if (DirectiveKind == OpenACCDirectiveKind::Loop ||
isOpenACCCombinedDirectiveKind(DirectiveKind)) {
// OpenACC 3.3 2.9.11: A reduction clause may not appear on a loop directive
// that has a gang clause with a dim: argument whose value is greater
// than 1.
const auto GangClauses = llvm::make_filter_range(
ExistingClauses, llvm::IsaPred<OpenACCGangClause>);
for (auto *GC : GangClauses) {
const auto *GangClause = cast<OpenACCGangClause>(GC);
for (unsigned I = 0; I < GangClause->getNumExprs(); ++I) {
std::pair<OpenACCGangKind, const Expr *> EPair = GangClause->getExpr(I);
if (EPair.first != OpenACCGangKind::Dim)
continue;
if (const auto *DimVal = dyn_cast<ConstantExpr>(EPair.second);
DimVal && DimVal->getResultAsAPSInt() > 1) {
Diag(BeginLoc, diag::err_acc_gang_reduction_conflict)
<< /*reduction/gang=*/1 << DirectiveKind;
Diag(GangClause->getBeginLoc(), diag::note_acc_previous_clause_here);
return nullptr;
}
}
}
}
auto *Ret = OpenACCReductionClause::Create(
getASTContext(), BeginLoc, LParenLoc, ReductionOp, Vars, EndLoc);
return Ret;
}
ExprResult SemaOpenACC::CheckTileSizeExpr(Expr *SizeExpr) {
if (!SizeExpr)
return ExprError();
assert((SizeExpr->isInstantiationDependent() ||
SizeExpr->getType()->isIntegerType()) &&
"size argument non integer?");
// If dependent, or an asterisk, the expression is fine.
if (SizeExpr->isInstantiationDependent() ||
isa<OpenACCAsteriskSizeExpr>(SizeExpr))
return ExprResult{SizeExpr};
std::optional<llvm::APSInt> ICE =
SizeExpr->getIntegerConstantExpr(getASTContext());
// OpenACC 3.3 2.9.8
// where each tile size is a constant positive integer expression or asterisk.
if (!ICE || *ICE <= 0) {
Diag(SizeExpr->getBeginLoc(), diag::err_acc_size_expr_value)
<< ICE.has_value() << ICE.value_or(llvm::APSInt{}).getExtValue();
return ExprError();
}
return ExprResult{
ConstantExpr::Create(getASTContext(), SizeExpr, APValue{*ICE})};
}
void SemaOpenACC::ActOnWhileStmt(SourceLocation WhileLoc) {
if (!getLangOpts().OpenACC)
return;
if (!LoopInfo.TopLevelLoopSeen)
return;
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
Diag(WhileLoc, diag::err_acc_invalid_in_loop)
<< /*while loop*/ 1 << CollapseInfo.DirectiveKind
<< OpenACCClauseKind::Collapse;
assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Collapse;
// Remove the value so that we don't get cascading errors in the body. The
// caller RAII object will restore this.
CollapseInfo.CurCollapseCount = std::nullopt;
}
if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) {
Diag(WhileLoc, diag::err_acc_invalid_in_loop)
<< /*while loop*/ 1 << TileInfo.DirectiveKind
<< OpenACCClauseKind::Tile;
assert(TileInfo.ActiveTile && "tile count without object?");
Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Tile;
// Remove the value so that we don't get cascading errors in the body. The
// caller RAII object will restore this.
TileInfo.CurTileCount = std::nullopt;
}
}
void SemaOpenACC::ActOnDoStmt(SourceLocation DoLoc) {
if (!getLangOpts().OpenACC)
return;
if (!LoopInfo.TopLevelLoopSeen)
return;
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
Diag(DoLoc, diag::err_acc_invalid_in_loop)
<< /*do loop*/ 2 << CollapseInfo.DirectiveKind
<< OpenACCClauseKind::Collapse;
assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Collapse;
// Remove the value so that we don't get cascading errors in the body. The
// caller RAII object will restore this.
CollapseInfo.CurCollapseCount = std::nullopt;
}
if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) {
Diag(DoLoc, diag::err_acc_invalid_in_loop)
<< /*do loop*/ 2 << TileInfo.DirectiveKind << OpenACCClauseKind::Tile;
assert(TileInfo.ActiveTile && "tile count without object?");
Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Tile;
// Remove the value so that we don't get cascading errors in the body. The
// caller RAII object will restore this.
TileInfo.CurTileCount = std::nullopt;
}
}
void SemaOpenACC::ForStmtBeginHelper(SourceLocation ForLoc,
ForStmtBeginChecker &C) {
assert(getLangOpts().OpenACC && "Check enabled when not OpenACC?");
// Enable the while/do-while checking.
LoopInfo.TopLevelLoopSeen = true;
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
C.check();
// OpenACC 3.3 2.9.1:
// Each associated loop, except the innermost, must contain exactly one loop
// or loop nest.
// This checks for more than 1 loop at the current level, the
// 'depth'-satisifed checking manages the 'not zero' case.
if (LoopInfo.CurLevelHasLoopAlready) {
Diag(ForLoc, diag::err_acc_clause_multiple_loops)
<< CollapseInfo.DirectiveKind << OpenACCClauseKind::Collapse;
assert(CollapseInfo.ActiveCollapse && "No collapse object?");
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Collapse;
} else {
--(*CollapseInfo.CurCollapseCount);
// Once we've hit zero here, we know we have deep enough 'for' loops to
// get to the bottom.
if (*CollapseInfo.CurCollapseCount == 0)
CollapseInfo.CollapseDepthSatisfied = true;
}
}
if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) {
C.check();
if (LoopInfo.CurLevelHasLoopAlready) {
Diag(ForLoc, diag::err_acc_clause_multiple_loops)
<< TileInfo.DirectiveKind << OpenACCClauseKind::Tile;
assert(TileInfo.ActiveTile && "No tile object?");
Diag(TileInfo.ActiveTile->getBeginLoc(),
diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Tile;
} else {
--(*TileInfo.CurTileCount);
// Once we've hit zero here, we know we have deep enough 'for' loops to
// get to the bottom.
if (*TileInfo.CurTileCount == 0)
TileInfo.TileDepthSatisfied = true;
}
}
// Set this to 'false' for the body of this loop, so that the next level
// checks independently.
LoopInfo.CurLevelHasLoopAlready = false;
}
namespace {
bool isValidLoopVariableType(QualType LoopVarTy) {
// Just skip if it is dependent, it could be any of the below.
if (LoopVarTy->isDependentType())
return true;
// The loop variable must be of integer,
if (LoopVarTy->isIntegerType())
return true;
// C/C++ pointer,
if (LoopVarTy->isPointerType())
return true;
// or C++ random-access iterator type.
if (const auto *RD = LoopVarTy->getAsCXXRecordDecl()) {
// Note: Only do CXXRecordDecl because RecordDecl can't be a random access
// iterator type!
// We could either do a lot of work to see if this matches
// random-access-iterator, but it seems that just checking that the
// 'iterator_category' typedef is more than sufficient. If programmers are
// willing to lie about this, we can let them.
for (const auto *TD :
llvm::make_filter_range(RD->decls(), llvm::IsaPred<TypedefNameDecl>)) {
const auto *TDND = cast<TypedefNameDecl>(TD)->getCanonicalDecl();
if (TDND->getName() != "iterator_category")
continue;
// If there is no type for this decl, return false.
if (TDND->getUnderlyingType().isNull())
return false;
const CXXRecordDecl *ItrCategoryDecl =
TDND->getUnderlyingType()->getAsCXXRecordDecl();
// If the category isn't a record decl, it isn't the tag type.
if (!ItrCategoryDecl)
return false;
auto IsRandomAccessIteratorTag = [](const CXXRecordDecl *RD) {
if (RD->getName() != "random_access_iterator_tag")
return false;
// Checks just for std::random_access_iterator_tag.
return RD->getEnclosingNamespaceContext()->isStdNamespace();
};
if (IsRandomAccessIteratorTag(ItrCategoryDecl))
return true;
// We can also support types inherited from the
// random_access_iterator_tag.
for (CXXBaseSpecifier BS : ItrCategoryDecl->bases()) {
if (IsRandomAccessIteratorTag(BS.getType()->getAsCXXRecordDecl()))
return true;
}
return false;
}
}
return false;
}
} // namespace
void SemaOpenACC::ForStmtBeginChecker::check() {
if (SemaRef.LoopWithoutSeqInfo.Kind == OpenACCDirectiveKind::Invalid)
return;
if (AlreadyChecked)
return;
AlreadyChecked = true;
// OpenACC3.3 2.1:
// A loop associated with a loop construct that does not have a seq clause
// must be written to meet all the following conditions:
// - The loop variable must be of integer, C/C++ pointer, or C++ random-access
// iterator type.
// - The loop variable must monotonically increase or decrease in the
// direction of its termination condition.
// - The loop trip count must be computable in constant time when entering the
// loop construct.
//
// For a C++ range-based for loop, the loop variable
// identified by the above conditions is the internal iterator, such as a
// pointer, that the compiler generates to iterate the range. it is not the
// variable declared by the for loop.
if (IsRangeFor) {
// If the range-for is being instantiated and didn't change, don't
// re-diagnose.
if (!RangeFor.has_value())
return;
// For a range-for, we can assume everything is 'corect' other than the type
// of the iterator, so check that.
const DeclStmt *RangeStmt = (*RangeFor)->getBeginStmt();
// In some dependent contexts, the autogenerated range statement doesn't get
// included until instantiation, so skip for now.
if (!RangeStmt)
return;
const ValueDecl *InitVar = cast<ValueDecl>(RangeStmt->getSingleDecl());
QualType VarType = InitVar->getType().getNonReferenceType();
if (!isValidLoopVariableType(VarType)) {
SemaRef.Diag(InitVar->getBeginLoc(), diag::err_acc_loop_variable_type)
<< SemaRef.LoopWithoutSeqInfo.Kind << VarType;
SemaRef.Diag(SemaRef.LoopWithoutSeqInfo.Loc,
diag::note_acc_construct_here)
<< SemaRef.LoopWithoutSeqInfo.Kind;
}
return;
}
// Else we are in normal 'ForStmt', so we can diagnose everything.
// We only have to check cond/inc if they have changed, but 'init' needs to
// just suppress its diagnostics if it hasn't changed.
const ValueDecl *InitVar = checkInit();
if (Cond.has_value())
checkCond();
if (Inc.has_value())
checkInc(InitVar);
}
const ValueDecl *SemaOpenACC::ForStmtBeginChecker::checkInit() {
if (!Init) {
if (InitChanged) {
SemaRef.Diag(ForLoc, diag::err_acc_loop_variable)
<< SemaRef.LoopWithoutSeqInfo.Kind;
SemaRef.Diag(SemaRef.LoopWithoutSeqInfo.Loc,
diag::note_acc_construct_here)
<< SemaRef.LoopWithoutSeqInfo.Kind;
}
return nullptr;
}
auto DiagLoopVar = [&]() {
if (InitChanged) {
SemaRef.Diag(Init->getBeginLoc(), diag::err_acc_loop_variable)
<< SemaRef.LoopWithoutSeqInfo.Kind;
SemaRef.Diag(SemaRef.LoopWithoutSeqInfo.Loc,
diag::note_acc_construct_here)
<< SemaRef.LoopWithoutSeqInfo.Kind;
}
return nullptr;
};
if (const auto *ExprTemp = dyn_cast<ExprWithCleanups>(Init))
Init = ExprTemp->getSubExpr();
if (const auto *E = dyn_cast<Expr>(Init))
Init = E->IgnoreParenImpCasts();
const ValueDecl *InitVar = nullptr;
if (const auto *BO = dyn_cast<BinaryOperator>(Init)) {
// Allow assignment operator here.
if (!BO->isAssignmentOp())
return DiagLoopVar();
const Expr *LHS = BO->getLHS()->IgnoreParenImpCasts();
if (const auto *DRE = dyn_cast<DeclRefExpr>(LHS))
InitVar = DRE->getDecl();
} else if (const auto *DS = dyn_cast<DeclStmt>(Init)) {
// Allow T t = <whatever>
if (!DS->isSingleDecl())
return DiagLoopVar();
InitVar = dyn_cast<ValueDecl>(DS->getSingleDecl());
// Ensure we have an initializer, unless this is a record/dependent type.
if (InitVar) {
if (!isa<VarDecl>(InitVar))
return DiagLoopVar();
if (!InitVar->getType()->isRecordType() &&
!InitVar->getType()->isDependentType() &&
!cast<VarDecl>(InitVar)->hasInit())
return DiagLoopVar();
}
} else if (auto *CE = dyn_cast<CXXOperatorCallExpr>(Init)) {
// Allow assignment operator call.
if (CE->getOperator() != OO_Equal)
return DiagLoopVar();
const Expr *LHS = CE->getArg(0)->IgnoreParenImpCasts();
if (auto *DRE = dyn_cast<DeclRefExpr>(LHS)) {
InitVar = DRE->getDecl();
} else if (auto *ME = dyn_cast<MemberExpr>(LHS)) {
if (isa<CXXThisExpr>(ME->getBase()->IgnoreParenImpCasts()))
InitVar = ME->getMemberDecl();
}
}
if (!InitVar)
return DiagLoopVar();
InitVar = cast<ValueDecl>(InitVar->getCanonicalDecl());
QualType VarType = InitVar->getType().getNonReferenceType();
// Since we have one, all we need to do is ensure it is the right type.
if (!isValidLoopVariableType(VarType)) {
if (InitChanged) {
SemaRef.Diag(InitVar->getBeginLoc(), diag::err_acc_loop_variable_type)
<< SemaRef.LoopWithoutSeqInfo.Kind << VarType;
SemaRef.Diag(SemaRef.LoopWithoutSeqInfo.Loc,
diag::note_acc_construct_here)
<< SemaRef.LoopWithoutSeqInfo.Kind;
}
return nullptr;
}
return InitVar;
}
void SemaOpenACC::ForStmtBeginChecker::checkCond() {
if (!*Cond) {
SemaRef.Diag(ForLoc, diag::err_acc_loop_terminating_condition)
<< SemaRef.LoopWithoutSeqInfo.Kind;
SemaRef.Diag(SemaRef.LoopWithoutSeqInfo.Loc, diag::note_acc_construct_here)
<< SemaRef.LoopWithoutSeqInfo.Kind;
}
// Nothing else to do here. we could probably do some additional work to look
// into the termination condition, but that error-prone. For now, we don't
// implement anything other than 'there is a termination condition', and if
// codegen/MLIR comes up with some necessary restrictions, we can implement
// them here.
}
void SemaOpenACC::ForStmtBeginChecker::checkInc(const ValueDecl *Init) {
if (!*Inc) {
SemaRef.Diag(ForLoc, diag::err_acc_loop_not_monotonic)
<< SemaRef.LoopWithoutSeqInfo.Kind;
SemaRef.Diag(SemaRef.LoopWithoutSeqInfo.Loc, diag::note_acc_construct_here)
<< SemaRef.LoopWithoutSeqInfo.Kind;
return;
}
auto DiagIncVar = [this] {
SemaRef.Diag((*Inc)->getBeginLoc(), diag::err_acc_loop_not_monotonic)
<< SemaRef.LoopWithoutSeqInfo.Kind;
SemaRef.Diag(SemaRef.LoopWithoutSeqInfo.Loc, diag::note_acc_construct_here)
<< SemaRef.LoopWithoutSeqInfo.Kind;
return;
};
if (const auto *ExprTemp = dyn_cast<ExprWithCleanups>(*Inc))
Inc = ExprTemp->getSubExpr();
if (const auto *E = dyn_cast<Expr>(*Inc))
Inc = E->IgnoreParenImpCasts();
auto getDeclFromExpr = [](const Expr *E) -> const ValueDecl * {
E = E->IgnoreParenImpCasts();
if (const auto *FE = dyn_cast<FullExpr>(E))
E = FE->getSubExpr();
E = E->IgnoreParenImpCasts();
if (!E)
return nullptr;
if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
return dyn_cast<ValueDecl>(DRE->getDecl());
if (const auto *ME = dyn_cast<MemberExpr>(E))
if (isa<CXXThisExpr>(ME->getBase()->IgnoreParenImpCasts()))
return ME->getMemberDecl();
return nullptr;
};
const ValueDecl *IncVar = nullptr;
// Here we enforce the monotonically increase/decrease:
if (const auto *UO = dyn_cast<UnaryOperator>(*Inc)) {
// Allow increment/decrement ops.
if (!UO->isIncrementDecrementOp())
return DiagIncVar();
IncVar = getDeclFromExpr(UO->getSubExpr());
} else if (const auto *BO = dyn_cast<BinaryOperator>(*Inc)) {
switch (BO->getOpcode()) {
default:
return DiagIncVar();
case BO_AddAssign:
case BO_SubAssign:
case BO_MulAssign:
case BO_DivAssign:
case BO_Assign:
// += -= *= /= should all be fine here, this should be all of the
// 'monotonical' compound-assign ops.
// Assignment we just give up on, we could do better, and ensure that it
// is a binary/operator expr doing more work, but that seems like a lot
// of work for an error prone check.
break;
}
IncVar = getDeclFromExpr(BO->getLHS());
} else if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(*Inc)) {
switch (CE->getOperator()) {
default:
return DiagIncVar();
case OO_PlusPlus:
case OO_MinusMinus:
case OO_PlusEqual:
case OO_MinusEqual:
case OO_StarEqual:
case OO_SlashEqual:
case OO_Equal:
// += -= *= /= should all be fine here, this should be all of the
// 'monotonical' compound-assign ops.
// Assignment we just give up on, we could do better, and ensure that it
// is a binary/operator expr doing more work, but that seems like a lot
// of work for an error prone check.
break;
}
IncVar = getDeclFromExpr(CE->getArg(0));
} else if (const auto *ME = dyn_cast<CXXMemberCallExpr>(*Inc)) {
IncVar = getDeclFromExpr(ME->getImplicitObjectArgument());
// We can't really do much for member expressions, other than hope they are
// doing the right thing, so give up here.
}
if (!IncVar)
return DiagIncVar();
// InitVar shouldn't be null unless there was an error, so don't diagnose if
// that is the case. Else we should ensure that it refers to the loop
// value.
if (Init && IncVar->getCanonicalDecl() != Init->getCanonicalDecl())
return DiagIncVar();
return;
}
void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc, const Stmt *OldFirst,
const Stmt *First, const Stmt *OldSecond,
const Stmt *Second, const Stmt *OldThird,
const Stmt *Third) {
if (!getLangOpts().OpenACC)
return;
std::optional<const Stmt *> S;
if (OldSecond == Second)
S = std::nullopt;
else
S = Second;
std::optional<const Stmt *> T;
if (OldThird == Third)
S = std::nullopt;
else
S = Third;
bool InitChanged = false;
if (OldFirst != First) {
InitChanged = true;
// VarDecls are always rebuild because they are dependent, so we can do a
// little work to suppress some of the double checking based on whether the
// type is instantiation dependent.
QualType OldVDTy;
QualType NewVDTy;
if (const auto *DS = dyn_cast<DeclStmt>(OldFirst))
if (const VarDecl *VD = dyn_cast_if_present<VarDecl>(
DS->isSingleDecl() ? DS->getSingleDecl() : nullptr))
OldVDTy = VD->getType();
if (const auto *DS = dyn_cast<DeclStmt>(First))
if (const VarDecl *VD = dyn_cast_if_present<VarDecl>(
DS->isSingleDecl() ? DS->getSingleDecl() : nullptr))
NewVDTy = VD->getType();
if (!OldVDTy.isNull() && !NewVDTy.isNull())
InitChanged = OldVDTy->isInstantiationDependentType() !=
NewVDTy->isInstantiationDependentType();
}
ForStmtBeginChecker FSBC{*this, ForLoc, First, InitChanged, S, T};
if (!LoopInfo.TopLevelLoopSeen) {
FSBC.check();
}
ForStmtBeginHelper(ForLoc, FSBC);
}
void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc, const Stmt *First,
const Stmt *Second, const Stmt *Third) {
if (!getLangOpts().OpenACC)
return;
ForStmtBeginChecker FSBC{*this, ForLoc, First, /*InitChanged=*/true,
Second, Third};
if (!LoopInfo.TopLevelLoopSeen) {
FSBC.check();
}
ForStmtBeginHelper(ForLoc, FSBC);
}
void SemaOpenACC::ActOnRangeForStmtBegin(SourceLocation ForLoc,
const Stmt *OldRangeFor,
const Stmt *RangeFor) {
if (!getLangOpts().OpenACC)
return;
std::optional<const CXXForRangeStmt *> RF;
if (OldRangeFor == RangeFor)
RF = std::nullopt;
else
RF = cast<CXXForRangeStmt>(RangeFor);
ForStmtBeginChecker FSBC{*this, ForLoc, RF};
if (!LoopInfo.TopLevelLoopSeen) {
FSBC.check();
}
ForStmtBeginHelper(ForLoc, FSBC);
}
void SemaOpenACC::ActOnRangeForStmtBegin(SourceLocation ForLoc,
const Stmt *RangeFor) {
if (!getLangOpts().OpenACC)
return;
ForStmtBeginChecker FSBC{*this, ForLoc, cast<CXXForRangeStmt>(RangeFor)};
if (!LoopInfo.TopLevelLoopSeen) {
FSBC.check();
}
ForStmtBeginHelper(ForLoc, FSBC);
}
namespace {
SourceLocation FindInterveningCodeInLoop(const Stmt *CurStmt) {
// We should diagnose on anything except `CompoundStmt`, `NullStmt`,
// `ForStmt`, `CXXForRangeStmt`, since those are legal, and `WhileStmt` and
// `DoStmt`, as those are caught as a violation elsewhere.
// For `CompoundStmt` we need to search inside of it.
if (!CurStmt ||
isa<ForStmt, NullStmt, ForStmt, CXXForRangeStmt, WhileStmt, DoStmt>(
CurStmt))
return SourceLocation{};
// Any other construct is an error anyway, so it has already been diagnosed.
if (isa<OpenACCConstructStmt>(CurStmt))
return SourceLocation{};
// Search inside the compound statement, this allows for arbitrary nesting
// of compound statements, as long as there isn't any code inside.
if (const auto *CS = dyn_cast<CompoundStmt>(CurStmt)) {
for (const auto *ChildStmt : CS->children()) {
SourceLocation ChildStmtLoc = FindInterveningCodeInLoop(ChildStmt);
if (ChildStmtLoc.isValid())
return ChildStmtLoc;
}
// Empty/not invalid compound statements are legal.
return SourceLocation{};
}
return CurStmt->getBeginLoc();
}
} // namespace
void SemaOpenACC::ActOnForStmtEnd(SourceLocation ForLoc, StmtResult Body) {
if (!getLangOpts().OpenACC)
return;
// Set this to 'true' so if we find another one at this level we can diagnose.
LoopInfo.CurLevelHasLoopAlready = true;
if (!Body.isUsable())
return;
bool IsActiveCollapse = CollapseInfo.CurCollapseCount &&
*CollapseInfo.CurCollapseCount > 0 &&
!CollapseInfo.ActiveCollapse->hasForce();
bool IsActiveTile = TileInfo.CurTileCount && *TileInfo.CurTileCount > 0;
if (IsActiveCollapse || IsActiveTile) {
SourceLocation OtherStmtLoc = FindInterveningCodeInLoop(Body.get());
if (OtherStmtLoc.isValid() && IsActiveCollapse) {
Diag(OtherStmtLoc, diag::err_acc_intervening_code)
<< OpenACCClauseKind::Collapse << CollapseInfo.DirectiveKind;
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Collapse;
}
if (OtherStmtLoc.isValid() && IsActiveTile) {
Diag(OtherStmtLoc, diag::err_acc_intervening_code)
<< OpenACCClauseKind::Tile << TileInfo.DirectiveKind;
Diag(TileInfo.ActiveTile->getBeginLoc(),
diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Tile;
}
}
}
bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc) {
SemaRef.DiscardCleanupsInEvaluationContext();
SemaRef.PopExpressionEvaluationContext();
// OpenACC 3.3 2.9.1:
// Intervening code must not contain other OpenACC directives or calls to API
// routines.
//
// ALL constructs are ill-formed if there is an active 'collapse'
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
Diag(StartLoc, diag::err_acc_invalid_in_loop)
<< /*OpenACC Construct*/ 0 << CollapseInfo.DirectiveKind
<< OpenACCClauseKind::Collapse << K;
assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Collapse;
}
if (TileInfo.CurTileCount && *TileInfo.CurTileCount > 0) {
Diag(StartLoc, diag::err_acc_invalid_in_loop)
<< /*OpenACC Construct*/ 0 << TileInfo.DirectiveKind
<< OpenACCClauseKind::Tile << K;
assert(TileInfo.ActiveTile && "Tile count without object?");
Diag(TileInfo.ActiveTile->getBeginLoc(), diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Tile;
}
return diagnoseConstructAppertainment(*this, K, StartLoc, /*IsStmt=*/true);
}
StmtResult SemaOpenACC::ActOnEndStmtDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc,
SourceLocation DirLoc,
SourceLocation EndLoc,
ArrayRef<OpenACCClause *> Clauses,
StmtResult AssocStmt) {
switch (K) {
default:
return StmtEmpty();
case OpenACCDirectiveKind::Invalid:
return StmtError();
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels: {
return OpenACCComputeConstruct::Create(
getASTContext(), K, StartLoc, DirLoc, EndLoc, Clauses,
AssocStmt.isUsable() ? AssocStmt.get() : nullptr);
}
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop: {
return OpenACCCombinedConstruct::Create(
getASTContext(), K, StartLoc, DirLoc, EndLoc, Clauses,
AssocStmt.isUsable() ? AssocStmt.get() : nullptr);
}
case OpenACCDirectiveKind::Loop: {
return OpenACCLoopConstruct::Create(
getASTContext(), ActiveComputeConstructInfo.Kind, StartLoc, DirLoc,
EndLoc, Clauses, AssocStmt.isUsable() ? AssocStmt.get() : nullptr);
}
case OpenACCDirectiveKind::Data: {
return OpenACCDataConstruct::Create(
getASTContext(), StartLoc, DirLoc, EndLoc, Clauses,
AssocStmt.isUsable() ? AssocStmt.get() : nullptr);
}
case OpenACCDirectiveKind::EnterData: {
return OpenACCEnterDataConstruct::Create(getASTContext(), StartLoc, DirLoc,
EndLoc, Clauses);
}
case OpenACCDirectiveKind::ExitData: {
return OpenACCExitDataConstruct::Create(getASTContext(), StartLoc, DirLoc,
EndLoc, Clauses);
}
case OpenACCDirectiveKind::HostData: {
return OpenACCHostDataConstruct::Create(
getASTContext(), StartLoc, DirLoc, EndLoc, Clauses,
AssocStmt.isUsable() ? AssocStmt.get() : nullptr);
}
}
llvm_unreachable("Unhandled case in directive handling?");
}
StmtResult SemaOpenACC::ActOnAssociatedStmt(
SourceLocation DirectiveLoc, OpenACCDirectiveKind K,
ArrayRef<const OpenACCClause *> Clauses, StmtResult AssocStmt) {
switch (K) {
default:
llvm_unreachable("Unimplemented associated statement application");
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ExitData:
llvm_unreachable(
"these don't have associated statements, so shouldn't get here");
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Data:
case OpenACCDirectiveKind::HostData:
// There really isn't any checking here that could happen. As long as we
// have a statement to associate, this should be fine.
// OpenACC 3.3 Section 6:
// Structured Block: in C or C++, an executable statement, possibly
// compound, with a single entry at the top and a single exit at the
// bottom.
// FIXME: Should we reject DeclStmt's here? The standard isn't clear, and
// an interpretation of it is to allow this and treat the initializer as
// the 'structured block'.
return AssocStmt;
case OpenACCDirectiveKind::Loop:
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
if (!AssocStmt.isUsable())
return StmtError();
if (!isa<CXXForRangeStmt, ForStmt>(AssocStmt.get())) {
Diag(AssocStmt.get()->getBeginLoc(), diag::err_acc_loop_not_for_loop)
<< K;
Diag(DirectiveLoc, diag::note_acc_construct_here) << K;
return StmtError();
}
if (!CollapseInfo.CollapseDepthSatisfied || !TileInfo.TileDepthSatisfied) {
if (!CollapseInfo.CollapseDepthSatisfied) {
Diag(DirectiveLoc, diag::err_acc_insufficient_loops)
<< OpenACCClauseKind::Collapse;
assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Collapse;
}
if (!TileInfo.TileDepthSatisfied) {
Diag(DirectiveLoc, diag::err_acc_insufficient_loops)
<< OpenACCClauseKind::Tile;
assert(TileInfo.ActiveTile && "Collapse count without object?");
Diag(TileInfo.ActiveTile->getBeginLoc(),
diag::note_acc_active_clause_here)
<< OpenACCClauseKind::Tile;
}
return StmtError();
}
return AssocStmt.get();
}
llvm_unreachable("Invalid associated statement application");
}
bool SemaOpenACC::ActOnStartDeclDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc) {
// OpenCC3.3 2.1 (line 889)
// A program must not depend on the order of evaluation of expressions in
// clause arguments or on any side effects of the evaluations.
SemaRef.DiscardCleanupsInEvaluationContext();
SemaRef.PopExpressionEvaluationContext();
return diagnoseConstructAppertainment(*this, K, StartLoc, /*IsStmt=*/false);
}
DeclGroupRef SemaOpenACC::ActOnEndDeclDirective() { return DeclGroupRef{}; }
ExprResult
SemaOpenACC::BuildOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) {
return OpenACCAsteriskSizeExpr::Create(getASTContext(), AsteriskLoc);
}
ExprResult
SemaOpenACC::ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) {
return BuildOpenACCAsteriskSizeExpr(AsteriskLoc);
}