mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 13:16:08 +00:00

Discussions with the OpenACC Standard folks and the dialect folks showed that the ability to have 'set' have a 'device_type' with more than one architecture was a mistake, and one that will be fixed in future revisions of the standard. Since the dialect requires this anyway, we'll implement this in advance of standardization.
2603 lines
100 KiB
C++
2603 lines
100 KiB
C++
//===--- SemaOpenACCClause.cpp - Semantic Analysis for OpenACC clause -----===//
|
|
//
|
|
// 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 clauses.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "clang/AST/OpenACCClause.h"
|
|
#include "clang/Basic/DiagnosticSema.h"
|
|
#include "clang/Basic/OpenACCKinds.h"
|
|
#include "clang/Sema/SemaOpenACC.h"
|
|
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
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:
|
|
// OpenACC 3.4(prerelease) PR #511 adds 'if' to atomic.
|
|
case OpenACCDirectiveKind::Atomic:
|
|
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::Declare:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case OpenACCClauseKind::Attach:
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Parallel:
|
|
case OpenACCDirectiveKind::Serial:
|
|
case OpenACCDirectiveKind::Kernels:
|
|
case OpenACCDirectiveKind::Data:
|
|
case OpenACCDirectiveKind::EnterData:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
case OpenACCClauseKind::DevicePtr:
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Parallel:
|
|
case OpenACCDirectiveKind::Serial:
|
|
case OpenACCDirectiveKind::Kernels:
|
|
case OpenACCDirectiveKind::Data:
|
|
case OpenACCDirectiveKind::Declare:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
case OpenACCClauseKind::Async:
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Parallel:
|
|
case OpenACCDirectiveKind::Serial:
|
|
case OpenACCDirectiveKind::Kernels:
|
|
case OpenACCDirectiveKind::Data:
|
|
case OpenACCDirectiveKind::EnterData:
|
|
case OpenACCDirectiveKind::ExitData:
|
|
case OpenACCDirectiveKind::Set:
|
|
case OpenACCDirectiveKind::Update:
|
|
case OpenACCDirectiveKind::Wait:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
case OpenACCClauseKind::Wait:
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Parallel:
|
|
case OpenACCDirectiveKind::Serial:
|
|
case OpenACCDirectiveKind::Kernels:
|
|
case OpenACCDirectiveKind::Data:
|
|
case OpenACCDirectiveKind::EnterData:
|
|
case OpenACCDirectiveKind::ExitData:
|
|
case OpenACCDirectiveKind::Update:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case OpenACCClauseKind::Seq:
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Loop:
|
|
case OpenACCDirectiveKind::Routine:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case OpenACCClauseKind::Independent:
|
|
case OpenACCClauseKind::Auto:
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Loop:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case OpenACCClauseKind::Reduction:
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Parallel:
|
|
case OpenACCDirectiveKind::Serial:
|
|
case OpenACCDirectiveKind::Loop:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case OpenACCClauseKind::DeviceType:
|
|
case OpenACCClauseKind::DType:
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Parallel:
|
|
case OpenACCDirectiveKind::Serial:
|
|
case OpenACCDirectiveKind::Kernels:
|
|
case OpenACCDirectiveKind::Data:
|
|
case OpenACCDirectiveKind::Init:
|
|
case OpenACCDirectiveKind::Shutdown:
|
|
case OpenACCDirectiveKind::Set:
|
|
case OpenACCDirectiveKind::Update:
|
|
case OpenACCDirectiveKind::Loop:
|
|
case OpenACCDirectiveKind::Routine:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case OpenACCClauseKind::Collapse: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Loop:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::Tile: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Loop:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
case OpenACCClauseKind::Gang: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Loop:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
case OpenACCDirectiveKind::Routine:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
case OpenACCClauseKind::Worker: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Loop:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
case OpenACCDirectiveKind::Routine:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::Vector: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Loop:
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
case OpenACCDirectiveKind::Routine:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::Finalize: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::ExitData:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::IfPresent: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::HostData:
|
|
case OpenACCDirectiveKind::Update:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::Delete: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::ExitData:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
case OpenACCClauseKind::Detach: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::ExitData:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
case OpenACCClauseKind::DeviceNum: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Init:
|
|
case OpenACCDirectiveKind::Shutdown:
|
|
case OpenACCDirectiveKind::Set:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::Link: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Declare:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::DeviceResident: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Declare:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
case OpenACCClauseKind::UseDevice: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::HostData:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::DefaultAsync: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Set:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::Device: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Update:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::Host: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Update:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::NoHost: {
|
|
switch (DirectiveKind) {
|
|
case OpenACCDirectiveKind::Routine:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case OpenACCClauseKind::Bind: {
|
|
switch (DirectiveKind) {
|
|
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) {
|
|
// 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;
|
|
}
|
|
} else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Set ||
|
|
NewClause.getDirectiveKind() == OpenACCDirectiveKind::Init ||
|
|
NewClause.getDirectiveKind() == OpenACCDirectiveKind::Shutdown) {
|
|
// There are no restrictions on 'set', 'init', or 'shutdown'.
|
|
return false;
|
|
} else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Update) {
|
|
// OpenACC3.3 section 2.14.4: 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;
|
|
}
|
|
} else if (NewClause.getDirectiveKind() == OpenACCDirectiveKind::Routine) {
|
|
// OpenACC 3.3 section 2.15: Only the 'gang', 'worker', 'vector', 'seq', and
|
|
// 'bind' clauses may follow a device_type clause.
|
|
switch (NewClause.getClauseKind()) {
|
|
case OpenACCClauseKind::Gang:
|
|
case OpenACCClauseKind::Worker:
|
|
case OpenACCClauseKind::Vector:
|
|
case OpenACCClauseKind::Seq:
|
|
case OpenACCClauseKind::Bind:
|
|
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;
|
|
}
|
|
|
|
// A temporary function that helps implement the 'not implemented' check at the
|
|
// top of each clause checking function. This should only be used in conjunction
|
|
// with the one being currently implemented/only updated after the entire
|
|
// construct has been implemented.
|
|
bool isDirectiveKindImplemented(OpenACCDirectiveKind DK) {
|
|
return DK != OpenACCDirectiveKind::Routine;
|
|
}
|
|
|
|
// GCC looks through linkage specs, but not the other transparent declaration
|
|
// contexts for 'declare' restrictions, so this helper function helps get us
|
|
// through that.
|
|
const DeclContext *removeLinkageSpecDC(const DeclContext *DC) {
|
|
while (isa<LinkageSpecDecl>(DC))
|
|
DC = DC->getParent();
|
|
|
|
return DC;
|
|
}
|
|
|
|
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.
|
|
// -also-
|
|
// OpenACC3.3 2.15: (routine)
|
|
// Exactly one of the 'gang', 'worker', 'vector' or 'seq' clauses must appear.
|
|
bool
|
|
DiagGangWorkerVectorSeqConflict(SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
const auto *Itr =
|
|
Clause.getDirectiveKind() == OpenACCDirectiveKind::Routine
|
|
? llvm::find_if(
|
|
ExistingClauses,
|
|
llvm::IsaPred<OpenACCSeqClause, OpenACCGangClause,
|
|
OpenACCWorkerClause, OpenACCVectorClause>)
|
|
: 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;
|
|
}
|
|
|
|
OpenACCModifierKind
|
|
CheckModifierList(SemaOpenACC::OpenACCParsedClause &Clause,
|
|
OpenACCModifierKind Mods) {
|
|
auto CheckSingle = [=](OpenACCModifierKind CurMods,
|
|
OpenACCModifierKind ValidKinds,
|
|
OpenACCModifierKind Bit) {
|
|
if (!isOpenACCModifierBitSet(CurMods, Bit) ||
|
|
isOpenACCModifierBitSet(ValidKinds, Bit))
|
|
return CurMods;
|
|
|
|
SemaRef.Diag(Clause.getLParenLoc(), diag::err_acc_invalid_modifier)
|
|
<< Bit << Clause.getClauseKind();
|
|
|
|
return CurMods ^ Bit;
|
|
};
|
|
auto Check = [&](OpenACCModifierKind ValidKinds) {
|
|
if ((Mods | ValidKinds) == ValidKinds)
|
|
return Mods;
|
|
|
|
Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::Always);
|
|
Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::AlwaysIn);
|
|
Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::AlwaysOut);
|
|
Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::Readonly);
|
|
Mods = CheckSingle(Mods, ValidKinds, OpenACCModifierKind::Zero);
|
|
return Mods;
|
|
};
|
|
|
|
switch (Clause.getClauseKind()) {
|
|
default:
|
|
llvm_unreachable("Only for copy, copyin, copyout, create");
|
|
case OpenACCClauseKind::Copy:
|
|
case OpenACCClauseKind::PCopy:
|
|
case OpenACCClauseKind::PresentOrCopy:
|
|
return Check(OpenACCModifierKind::Always | OpenACCModifierKind::AlwaysIn |
|
|
OpenACCModifierKind::AlwaysOut);
|
|
case OpenACCClauseKind::CopyIn:
|
|
case OpenACCClauseKind::PCopyIn:
|
|
case OpenACCClauseKind::PresentOrCopyIn:
|
|
return Check(OpenACCModifierKind::Always | OpenACCModifierKind::AlwaysIn |
|
|
OpenACCModifierKind::Readonly);
|
|
case OpenACCClauseKind::CopyOut:
|
|
case OpenACCClauseKind::PCopyOut:
|
|
case OpenACCClauseKind::PresentOrCopyOut:
|
|
return Check(OpenACCModifierKind::Always | OpenACCModifierKind::AlwaysIn |
|
|
OpenACCModifierKind::Zero);
|
|
case OpenACCClauseKind::Create:
|
|
case OpenACCClauseKind::PCreate:
|
|
case OpenACCClauseKind::PresentOrCreate:
|
|
return Check(OpenACCModifierKind::Zero);
|
|
}
|
|
llvm_unreachable("didn't return from switch above?");
|
|
}
|
|
|
|
public:
|
|
SemaOpenACCClauseVisitor(SemaOpenACC &S,
|
|
ArrayRef<const OpenACCClause *> ExistingClauses)
|
|
: SemaRef(S), Ctx(S.getASTContext()), ExistingClauses(ExistingClauses) {}
|
|
|
|
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) {
|
|
// 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', 'set', 'enter data' and
|
|
// 'exit data' both don't, but other implmementations do this. OpenACC issue
|
|
// 519 filed for the latter two. Prose also exists for 'update'.
|
|
// GCC allows this on init/shutdown, presumably for good reason, so we do too.
|
|
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Init &&
|
|
Clause.getDirectiveKind() != OpenACCDirectiveKind::Shutdown &&
|
|
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. This only applies on compute/combined constructs.
|
|
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::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) {
|
|
// 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. This only applies on compute/combined constructs.
|
|
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Update)
|
|
return OpenACCSelfClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
|
|
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) {
|
|
// 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::VisitDeviceNumClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// Restrictions only properly implemented on certain constructs, so skip/treat
|
|
// as unimplemented in those cases.
|
|
if (!isDirectiveKindImplemented(Clause.getDirectiveKind()))
|
|
return isNotImplemented();
|
|
|
|
// OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the
|
|
// same directive.
|
|
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Set &&
|
|
checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
|
|
return nullptr;
|
|
|
|
assert(Clause.getNumIntExprs() == 1 &&
|
|
"Invalid number of expressions for device_num");
|
|
return OpenACCDeviceNumClause::Create(
|
|
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitDefaultAsyncClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the
|
|
// same directive.
|
|
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
|
|
return nullptr;
|
|
|
|
assert(Clause.getNumIntExprs() == 1 &&
|
|
"Invalid number of expressions for default_async");
|
|
return OpenACCDefaultAsyncClause::Create(
|
|
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getIntExprs()[0],
|
|
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) {
|
|
// 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.
|
|
|
|
// 'declare' has some restrictions that need to be enforced separately, so
|
|
// check it here.
|
|
if (SemaRef.CheckDeclareClause(Clause, OpenACCModifierKind::Invalid))
|
|
return nullptr;
|
|
|
|
return OpenACCPresentClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getLParenLoc(),
|
|
Clause.getVarList(), Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitHostClause(
|
|
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 OpenACCHostClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceClause(
|
|
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 OpenACCDeviceClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyClause(
|
|
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.
|
|
|
|
OpenACCModifierKind NewMods =
|
|
CheckModifierList(Clause, Clause.getModifierList());
|
|
|
|
// 'declare' has some restrictions that need to be enforced separately, so
|
|
// check it here.
|
|
if (SemaRef.CheckDeclareClause(Clause, NewMods))
|
|
return nullptr;
|
|
|
|
return OpenACCCopyClause::Create(
|
|
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
|
|
Clause.getModifierList(), Clause.getVarList(), Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitLinkClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// 'declare' has some restrictions that need to be enforced separately, so
|
|
// check it here.
|
|
if (SemaRef.CheckDeclareClause(Clause, OpenACCModifierKind::Invalid))
|
|
return nullptr;
|
|
|
|
Clause.setVarListDetails(SemaRef.CheckLinkClauseVarList(Clause.getVarList()),
|
|
OpenACCModifierKind::Invalid);
|
|
|
|
return OpenACCLinkClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceResidentClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// 'declare' has some restrictions that need to be enforced separately, so
|
|
// check it here.
|
|
if (SemaRef.CheckDeclareClause(Clause, OpenACCModifierKind::Invalid))
|
|
return nullptr;
|
|
|
|
return OpenACCDeviceResidentClause::Create(
|
|
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyInClause(
|
|
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.
|
|
|
|
OpenACCModifierKind NewMods =
|
|
CheckModifierList(Clause, Clause.getModifierList());
|
|
|
|
// 'declare' has some restrictions that need to be enforced separately, so
|
|
// check it here.
|
|
if (SemaRef.CheckDeclareClause(Clause, NewMods))
|
|
return nullptr;
|
|
|
|
return OpenACCCopyInClause::Create(
|
|
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
|
|
Clause.getModifierList(), Clause.getVarList(), Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitCopyOutClause(
|
|
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.
|
|
|
|
OpenACCModifierKind NewMods =
|
|
CheckModifierList(Clause, Clause.getModifierList());
|
|
|
|
// 'declare' has some restrictions that need to be enforced separately, so
|
|
// check it here.
|
|
if (SemaRef.CheckDeclareClause(Clause, NewMods))
|
|
return nullptr;
|
|
|
|
return OpenACCCopyOutClause::Create(
|
|
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
|
|
Clause.getModifierList(), 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.
|
|
|
|
OpenACCModifierKind NewMods =
|
|
CheckModifierList(Clause, Clause.getModifierList());
|
|
|
|
// 'declare' has some restrictions that need to be enforced separately, so
|
|
// check it here.
|
|
if (SemaRef.CheckDeclareClause(Clause, NewMods))
|
|
return nullptr;
|
|
|
|
return OpenACCCreateClause::Create(
|
|
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
|
|
Clause.getModifierList(), Clause.getVarList(), Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitAttachClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// ActOnVar ensured that everything is a valid variable reference, but we
|
|
// still have to make sure it is a pointer type.
|
|
llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
|
|
llvm::erase_if(VarList, [&](Expr *E) {
|
|
return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Attach, E);
|
|
});
|
|
Clause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
|
|
return OpenACCAttachClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitDetachClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// ActOnVar ensured that everything is a valid variable reference, but we
|
|
// still have to make sure it is a pointer type.
|
|
llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
|
|
llvm::erase_if(VarList, [&](Expr *E) {
|
|
return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::Detach, E);
|
|
});
|
|
Clause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
|
|
return OpenACCDetachClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitDeleteClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// ActOnVar ensured that everything is a valid variable reference, so there
|
|
// really isn't anything to do here. GCC does some duplicate-finding, though
|
|
// it isn't apparent in the standard where this is justified.
|
|
return OpenACCDeleteClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitUseDeviceClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// ActOnVar ensured that everything is a valid variable or array, so nothing
|
|
// left to do here.
|
|
return OpenACCUseDeviceClause::Create(
|
|
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitDevicePtrClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// ActOnVar ensured that everything is a valid variable reference, but we
|
|
// still have to make sure it is a pointer type.
|
|
llvm::SmallVector<Expr *> VarList{Clause.getVarList()};
|
|
llvm::erase_if(VarList, [&](Expr *E) {
|
|
return SemaRef.CheckVarIsPointerType(OpenACCClauseKind::DevicePtr, E);
|
|
});
|
|
Clause.setVarListDetails(VarList, OpenACCModifierKind::Invalid);
|
|
|
|
// 'declare' has some restrictions that need to be enforced separately, so
|
|
// check it here.
|
|
if (SemaRef.CheckDeclareClause(Clause, OpenACCModifierKind::Invalid))
|
|
return nullptr;
|
|
|
|
return OpenACCDevicePtrClause::Create(
|
|
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getVarList(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitWaitClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
return OpenACCWaitClause::Create(
|
|
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(), Clause.getDevNumExpr(),
|
|
Clause.getQueuesLoc(), Clause.getQueueIdExprs(), Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitDeviceTypeClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// OpenACC 3.3 2.14.3: Two instances of the same clause may not appear on the
|
|
// same directive.
|
|
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Set &&
|
|
checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
|
|
return nullptr;
|
|
|
|
// Based on discussions, having more than 1 'architecture' on a 'set' is
|
|
// nonsensical, so we're going to fix the standard to reflect this. Implement
|
|
// the limitation, since the Dialect requires this.
|
|
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Set &&
|
|
Clause.getDeviceTypeArchitectures().size() > 1) {
|
|
SemaRef.Diag(Clause.getDeviceTypeArchitectures()[1].second,
|
|
diag::err_acc_device_type_multiple_archs);
|
|
return nullptr;
|
|
}
|
|
|
|
// The list of valid device_type values. Flang also has these hardcoded in
|
|
// openacc_parsers.cpp, as there does not seem to be a reliable backend
|
|
// source. The list below is sourced from Flang, though NVC++ supports only
|
|
// 'nvidia', 'host', 'multicore', and 'default'.
|
|
const std::array<llvm::StringLiteral, 6> ValidValues{
|
|
"default", "nvidia", "acc_device_nvidia", "radeon", "host", "multicore"};
|
|
// As an optimization, we have a manually maintained list of valid values
|
|
// below, rather than trying to calculate from above. These should be kept in
|
|
// sync if/when the above list ever changes.
|
|
std::string ValidValuesString =
|
|
"'default', 'nvidia', 'acc_device_nvidia', 'radeon', 'host', 'multicore'";
|
|
|
|
llvm::SmallVector<DeviceTypeArgument> Architectures{
|
|
Clause.getDeviceTypeArchitectures()};
|
|
|
|
// The parser has ensured that we either have a single entry of just '*'
|
|
// (represented by a nullptr IdentifierInfo), or a list.
|
|
|
|
bool Diagnosed = false;
|
|
auto FilterPred = [&](const DeviceTypeArgument &Arch) {
|
|
// The '*' case.
|
|
if (!Arch.first)
|
|
return false;
|
|
return llvm::find_if(ValidValues, [&](StringRef RHS) {
|
|
return Arch.first->getName().equals_insensitive(RHS);
|
|
}) == ValidValues.end();
|
|
};
|
|
|
|
auto Diagnose = [&](const DeviceTypeArgument &Arch) {
|
|
Diagnosed = SemaRef.Diag(Arch.second, diag::err_acc_invalid_default_type)
|
|
<< Arch.first << Clause.getClauseKind() << ValidValuesString;
|
|
};
|
|
|
|
// There aren't stable enumertor versions of 'for-each-then-erase', so do it
|
|
// here. We DO keep track of whether we diagnosed something to make sure we
|
|
// don't do the 'erase_if' in the event that the first list didn't find
|
|
// anything.
|
|
llvm::for_each(llvm::make_filter_range(Architectures, FilterPred), Diagnose);
|
|
if (Diagnosed)
|
|
llvm::erase_if(Architectures, FilterPred);
|
|
|
|
return OpenACCDeviceTypeClause::Create(
|
|
Ctx, Clause.getClauseKind(), Clause.getBeginLoc(), Clause.getLParenLoc(),
|
|
Architectures, 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<OpenACCAutoClause, 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::VisitNoHostClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
return OpenACCNoHostClause::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<OpenACCIndependentClause,
|
|
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 CheckGangDimExpr(SemaOpenACC &S, Expr *E) {
|
|
// 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.
|
|
// -also-
|
|
// OpenACC 3.3 2.15: The 'dim' argument must be a constant positive integer
|
|
// with 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})};
|
|
}
|
|
|
|
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:
|
|
return CheckGangDimExpr(S, E);
|
|
}
|
|
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);
|
|
}
|
|
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");
|
|
}
|
|
|
|
ExprResult CheckGangRoutineExpr(SemaOpenACC &S, OpenACCDirectiveKind DK,
|
|
OpenACCDirectiveKind AssocKind,
|
|
OpenACCGangKind GK, Expr *E) {
|
|
switch (GK) {
|
|
// Only 'dim' is allowed on a routine, so diallow num and static.
|
|
case OpenACCGangKind::Num:
|
|
case OpenACCGangKind::Static:
|
|
return DiagIntArgInvalid(S, E, GK, OpenACCClauseKind::Gang, DK, AssocKind);
|
|
case OpenACCGangKind::Dim:
|
|
return CheckGangDimExpr(S, E);
|
|
}
|
|
llvm_unreachable("Unknown gang kind in gang serial check");
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitVectorClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
if (DiagGangWorkerVectorSeqConflict(Clause))
|
|
return nullptr;
|
|
|
|
Expr *IntExpr =
|
|
Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr;
|
|
if (IntExpr) {
|
|
switch (Clause.getDirectiveKind()) {
|
|
default:
|
|
llvm_unreachable("Invalid directive kind for this clause");
|
|
case OpenACCDirectiveKind::Loop:
|
|
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");
|
|
}
|
|
break;
|
|
case 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;
|
|
}
|
|
break;
|
|
}
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::Routine:
|
|
DiagIntArgInvalid(SemaRef, IntExpr, "length", OpenACCClauseKind::Vector,
|
|
Clause.getDirectiveKind(),
|
|
SemaRef.getActiveComputeConstructInfo().Kind);
|
|
IntExpr = nullptr;
|
|
break;
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Clause.getDirectiveKind() == OpenACCDirectiveKind::Loop) {
|
|
// 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 (DiagGangWorkerVectorSeqConflict(Clause))
|
|
return nullptr;
|
|
|
|
Expr *IntExpr =
|
|
Clause.getNumIntExprs() != 0 ? Clause.getIntExprs()[0] : nullptr;
|
|
|
|
if (IntExpr) {
|
|
switch (Clause.getDirectiveKind()) {
|
|
default:
|
|
llvm_unreachable("Invalid directive kind for this clause");
|
|
case OpenACCDirectiveKind::Loop:
|
|
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");
|
|
}
|
|
break;
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::Routine:
|
|
DiagIntArgInvalid(SemaRef, IntExpr, OpenACCGangKind::Num,
|
|
OpenACCClauseKind::Worker, Clause.getDirectiveKind(),
|
|
SemaRef.getActiveComputeConstructInfo().Kind);
|
|
IntExpr = nullptr;
|
|
break;
|
|
case OpenACCDirectiveKind::KernelsLoop: {
|
|
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 (Clause.getDirectiveKind() == OpenACCDirectiveKind::Loop) {
|
|
// 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 (DiagGangWorkerVectorSeqConflict(Clause))
|
|
return nullptr;
|
|
|
|
// 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 (Clause.getDirectiveKind() == OpenACCDirectiveKind::Loop) {
|
|
// OpenACC 3.3 2.9.2: When the parent compute construct is a kernels
|
|
// construct, the gang clause behaves as follows. ... The region of a loop
|
|
// with a gang clause may not contain another loop with a gang clause unless
|
|
// within a nested compute region.
|
|
if (SemaRef.LoopGangClauseOnKernel.Loc.isValid()) {
|
|
// This handles the 'inner loop' diagnostic, but we cannot set that we're
|
|
// on one of these until we get to the end of the construct.
|
|
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
|
|
<< OpenACCClauseKind::Gang << OpenACCClauseKind::Gang
|
|
<< /*kernels construct info*/ 1
|
|
<< SemaRef.LoopGangClauseOnKernel.DirKind;
|
|
SemaRef.Diag(SemaRef.LoopGangClauseOnKernel.Loc,
|
|
diag::note_acc_previous_clause_here);
|
|
return nullptr;
|
|
}
|
|
|
|
// OpenACC 3.3 2.9.3: The region of a loop with a 'worker' clause may not
|
|
// contain a loop with a gang or worker clause unless within a nested
|
|
// compute region.
|
|
if (SemaRef.LoopWorkerClauseLoc.isValid()) {
|
|
// This handles the 'inner loop' diagnostic, but we cannot set that we're
|
|
// on one of these until we get to the end of the construct.
|
|
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
|
|
<< OpenACCClauseKind::Gang << OpenACCClauseKind::Worker
|
|
<< /*!kernels construct info*/ 0;
|
|
SemaRef.Diag(SemaRef.LoopWorkerClauseLoc,
|
|
diag::note_acc_previous_clause_here);
|
|
return nullptr;
|
|
}
|
|
|
|
// OpenACC 3.3 2.9.4: The region of a loop with a 'vector' clause may not
|
|
// contain a loop with a gang, worker, or vector clause unless within a
|
|
// nested compute region.
|
|
if (SemaRef.LoopVectorClauseLoc.isValid()) {
|
|
// This handles the 'inner loop' diagnostic, but we cannot set that we're
|
|
// on one of these until we get to the end of the construct.
|
|
SemaRef.Diag(Clause.getBeginLoc(), diag::err_acc_clause_in_clause_region)
|
|
<< OpenACCClauseKind::Gang << OpenACCClauseKind::Vector
|
|
<< /*!kernels construct info*/ 0;
|
|
SemaRef.Diag(SemaRef.LoopVectorClauseLoc,
|
|
diag::note_acc_previous_clause_here);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return SemaRef.CheckGangClause(Clause.getDirectiveKind(), ExistingClauses,
|
|
Clause.getBeginLoc(), Clause.getLParenLoc(),
|
|
GangKinds, IntExprs, Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitFinalizeClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// There isn't anything to do here, this is only valid on one construct, and
|
|
// has no associated rules.
|
|
return OpenACCFinalizeClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitIfPresentClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
// There isn't anything to do here, this is only valid on one construct, and
|
|
// has no associated rules.
|
|
return OpenACCIfPresentClause::Create(Ctx, Clause.getBeginLoc(),
|
|
Clause.getEndLoc());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitSeqClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
|
|
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Routine) {
|
|
// 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,
|
|
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;
|
|
}
|
|
}
|
|
|
|
// OpenACC 3.3 2.9:
|
|
// A 'gang', 'worker', or 'vector' clause may not appear if a 'seq' clause
|
|
// appears.
|
|
// -also-
|
|
// OpenACC3.3 2.15: (routine)
|
|
// Exactly one of the 'gang', 'worker', 'vector' or 'seq' clauses must appear.
|
|
const auto *Itr =
|
|
Clause.getDirectiveKind() == OpenACCDirectiveKind::Routine
|
|
? llvm::find_if(ExistingClauses,
|
|
llvm::IsaPred<OpenACCGangClause, OpenACCWorkerClause,
|
|
OpenACCVectorClause, OpenACCSeqClause>)
|
|
: 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());
|
|
}
|
|
|
|
OpenACCClause *SemaOpenACCClauseVisitor::VisitBindClause(
|
|
SemaOpenACC::OpenACCParsedClause &Clause) {
|
|
if (checkAlreadyHasClauseOfKind(SemaRef, ExistingClauses, Clause))
|
|
return nullptr;
|
|
|
|
if (std::holds_alternative<StringLiteral *>(Clause.getBindDetails()))
|
|
return OpenACCBindClause::Create(
|
|
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(),
|
|
std::get<StringLiteral *>(Clause.getBindDetails()), Clause.getEndLoc());
|
|
return OpenACCBindClause::Create(
|
|
Ctx, Clause.getBeginLoc(), Clause.getLParenLoc(),
|
|
std::get<IdentifierInfo *>(Clause.getBindDetails()), Clause.getEndLoc());
|
|
}
|
|
|
|
// 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
|
|
|
|
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, llvm::IsaPred<OpenACCDeviceTypeClause>);
|
|
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?");
|
|
|
|
return Result;
|
|
}
|
|
|
|
/// 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;
|
|
}
|
|
|
|
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})};
|
|
}
|
|
|
|
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::Routine:
|
|
return CheckGangRoutineExpr(*this, 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:
|
|
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) {
|
|
// Reduction isn't possible on 'routine' so we don't bother checking it here.
|
|
if (DirKind != OpenACCDirectiveKind::Routine) {
|
|
// 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;
|
|
}
|
|
|
|
llvm::SmallVector<Expr *>
|
|
SemaOpenACC::CheckLinkClauseVarList(ArrayRef<Expr *> VarExprs) {
|
|
const DeclContext *DC = removeLinkageSpecDC(getCurContext());
|
|
|
|
// Link has no special restrictions on its var list unless it is not at NS/TU
|
|
// scope.
|
|
if (isa<NamespaceDecl, TranslationUnitDecl>(DC))
|
|
return llvm::SmallVector<Expr *>(VarExprs);
|
|
|
|
llvm::SmallVector<Expr *> NewVarList;
|
|
|
|
for (Expr *VarExpr : VarExprs) {
|
|
if (isa<DependentScopeDeclRefExpr, CXXDependentScopeMemberExpr>(VarExpr)) {
|
|
NewVarList.push_back(VarExpr);
|
|
continue;
|
|
}
|
|
|
|
// Field decls can't be global, nor extern, and declare can't refer to
|
|
// non-static fields in class-scope, so this always fails the scope check.
|
|
// BUT for now we add this so it gets diagnosed by the general 'declare'
|
|
// rules.
|
|
if (isa<MemberExpr>(VarExpr)) {
|
|
NewVarList.push_back(VarExpr);
|
|
continue;
|
|
}
|
|
|
|
const auto *DRE = cast<DeclRefExpr>(VarExpr);
|
|
const VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl());
|
|
|
|
if (!Var || !Var->hasExternalStorage())
|
|
Diag(VarExpr->getBeginLoc(), diag::err_acc_link_not_extern);
|
|
else
|
|
NewVarList.push_back(VarExpr);
|
|
}
|
|
|
|
return NewVarList;
|
|
}
|
|
bool SemaOpenACC::CheckDeclareClause(SemaOpenACC::OpenACCParsedClause &Clause,
|
|
OpenACCModifierKind Mods) {
|
|
|
|
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Declare)
|
|
return false;
|
|
|
|
const DeclContext *DC = removeLinkageSpecDC(getCurContext());
|
|
|
|
// Whether this is 'create', 'copyin', 'deviceptr', 'device_resident', or
|
|
// 'link', which have 2 special rules.
|
|
bool IsSpecialClause =
|
|
Clause.getClauseKind() == OpenACCClauseKind::Create ||
|
|
Clause.getClauseKind() == OpenACCClauseKind::CopyIn ||
|
|
Clause.getClauseKind() == OpenACCClauseKind::DevicePtr ||
|
|
Clause.getClauseKind() == OpenACCClauseKind::DeviceResident ||
|
|
Clause.getClauseKind() == OpenACCClauseKind::Link;
|
|
|
|
// OpenACC 3.3 2.13:
|
|
// In C or C++ global or namespace scope, only 'create',
|
|
// 'copyin', 'deviceptr', 'device_resident', or 'link' clauses are
|
|
// allowed.
|
|
if (!IsSpecialClause && isa<NamespaceDecl, TranslationUnitDecl>(DC)) {
|
|
return Diag(Clause.getBeginLoc(), diag::err_acc_declare_clause_at_global)
|
|
<< Clause.getClauseKind();
|
|
}
|
|
|
|
llvm::SmallVector<Expr *> FilteredVarList;
|
|
const DeclaratorDecl *CurDecl = nullptr;
|
|
for (Expr *VarExpr : Clause.getVarList()) {
|
|
if (isa<DependentScopeDeclRefExpr, CXXDependentScopeMemberExpr>(VarExpr)) {
|
|
// There isn't really anything we can do here, so we add them anyway and
|
|
// we can check them again when we instantiate this.
|
|
} else if (const auto *MemExpr = dyn_cast<MemberExpr>(VarExpr)) {
|
|
FieldDecl *FD =
|
|
cast<FieldDecl>(MemExpr->getMemberDecl()->getCanonicalDecl());
|
|
CurDecl = FD;
|
|
|
|
if (removeLinkageSpecDC(
|
|
FD->getLexicalDeclContext()->getPrimaryContext()) != DC) {
|
|
Diag(MemExpr->getBeginLoc(), diag::err_acc_declare_same_scope)
|
|
<< Clause.getClauseKind();
|
|
continue;
|
|
}
|
|
} else {
|
|
const auto *DRE = cast<DeclRefExpr>(VarExpr);
|
|
const VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl());
|
|
if (Var)
|
|
CurDecl = Var->getCanonicalDecl();
|
|
|
|
// OpenACC3.3 2.13:
|
|
// A 'declare' directive must be in the same scope as the declaration of
|
|
// any var that appears in the clauses of the directive or any scope
|
|
// within a C/C++ function.
|
|
// We can't really check 'scope' here, so we check declaration context,
|
|
// which is a reasonable approximation, but misses scopes inside of
|
|
// functions.
|
|
if (removeLinkageSpecDC(Var->getCanonicalDecl()
|
|
->getLexicalDeclContext()
|
|
->getPrimaryContext()) != DC) {
|
|
Diag(VarExpr->getBeginLoc(), diag::err_acc_declare_same_scope)
|
|
<< Clause.getClauseKind();
|
|
continue;
|
|
}
|
|
// OpenACC3.3 2.13:
|
|
// C and C++ extern variables may only appear in 'create',
|
|
// 'copyin', 'deviceptr', 'device_resident', or 'link' clauses on a
|
|
// 'declare' directive.
|
|
if (!IsSpecialClause && Var && Var->hasExternalStorage()) {
|
|
Diag(VarExpr->getBeginLoc(), diag::err_acc_declare_extern)
|
|
<< Clause.getClauseKind();
|
|
continue;
|
|
}
|
|
|
|
// OpenACC3.3 2.13:
|
|
// A var may appear at most once in all the clauses of declare
|
|
// directives for a function, subroutine, program, or module.
|
|
|
|
if (CurDecl) {
|
|
auto [Itr, Inserted] = DeclareVarReferences.try_emplace(CurDecl);
|
|
if (!Inserted) {
|
|
Diag(VarExpr->getBeginLoc(), diag::err_acc_multiple_references)
|
|
<< Clause.getClauseKind();
|
|
Diag(Itr->second, diag::note_acc_previous_reference);
|
|
continue;
|
|
} else {
|
|
Itr->second = VarExpr->getBeginLoc();
|
|
}
|
|
}
|
|
}
|
|
FilteredVarList.push_back(VarExpr);
|
|
}
|
|
|
|
Clause.setVarListDetails(FilteredVarList, Mods);
|
|
return false;
|
|
}
|