
Another simple implementation, as it uses the same work as the previous implementation, just enabling it for this construct.
3594 lines
139 KiB
C++
3594 lines
139 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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) {
|
|
// Restrictions only properly implemented on 'compute'/'combined' constructs,
|
|
// and 'compute'/'combined' 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();
|
|
|
|
// 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::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::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);
|
|
}
|