[flang][OpenMP] Parse iterators, add to MAP clause, TODO for lowering (#113167)

Define `OmpIteratorSpecifier` and `OmpIteratorModifier` parser classes,
and add parsing for them. Those are reusable between any clauses that
use iterator modifiers.

Add support for iterator modifiers to the MAP clause up to lowering,
where a TODO message is emitted.
This commit is contained in:
Krzysztof Parzyszek
2024-10-23 08:31:53 -05:00
committed by GitHub
parent 1f9953c055
commit 973fa983af
16 changed files with 768 additions and 142 deletions

View File

@@ -474,6 +474,8 @@ public:
NODE(parser, NullInit)
NODE(parser, ObjectDecl)
NODE(parser, OldParameterStmt)
NODE(parser, OmpIteratorSpecifier)
NODE(parser, OmpIteratorModifier)
NODE(parser, OmpAlignedClause)
NODE(parser, OmpAtomic)
NODE(parser, OmpAtomicCapture)

View File

@@ -3424,7 +3424,17 @@ struct AssignedGotoStmt {
WRAPPER_CLASS(PauseStmt, std::optional<StopCode>);
// Parse tree nodes for OpenMP 4.5 directives and clauses
// Parse tree nodes for OpenMP 5.2 directives and clauses
// [5.0] 2.1.6 iterator-specifier -> type-declaration-stmt = subscript-triple
// iterator-modifier -> iterator-specifier-list
struct OmpIteratorSpecifier {
TUPLE_CLASS_BOILERPLATE(OmpIteratorSpecifier);
CharBlock source;
std::tuple<TypeDeclarationStmt, SubscriptTriplet> t;
};
WRAPPER_CLASS(OmpIteratorModifier, std::list<OmpIteratorSpecifier>);
// 2.5 proc-bind-clause -> PROC_BIND (MASTER | CLOSE | SPREAD)
struct OmpProcBindClause {
@@ -3450,16 +3460,25 @@ struct OmpObject {
WRAPPER_CLASS(OmpObjectList, std::list<OmpObject>);
// 2.15.5.1 map ->
// MAP ([ [map-type-modifiers [,] ] map-type : ] variable-name-list)
// map-type-modifiers -> map-type-modifier [,] [...]
// MAP ([[map-type-modifier-list [,]] [iterator-modifier [,]] map-type : ]
// variable-name-list)
// map-type-modifier-list -> map-type-modifier [,] [...]
// map-type-modifier -> ALWAYS | CLOSE | PRESENT | OMPX_HOLD
// map-type -> TO | FROM | TOFROM | ALLOC | RELEASE | DELETE
struct OmpMapClause {
ENUM_CLASS(TypeModifier, Always, Close, Present, Ompx_Hold);
ENUM_CLASS(Type, To, From, Tofrom, Alloc, Release, Delete)
TUPLE_CLASS_BOILERPLATE(OmpMapClause);
std::tuple<std::optional<std::list<TypeModifier>>, std::optional<Type>,
OmpObjectList>
// All modifiers are parsed into optional lists, even if they are unique.
// The checks for satisfying those constraints are deferred to semantics.
// In OpenMP 5.2 the non-comma syntax has been deprecated: keep the
// information about separator presence to emit a diagnostic if needed.
std::tuple<std::optional<std::list<TypeModifier>>,
std::optional<std::list<OmpIteratorModifier>>, // unique
std::optional<std::list<Type>>, // unique
OmpObjectList,
bool> // were the modifiers comma-separated?
t;
};

View File

@@ -61,7 +61,7 @@ class Scope {
public:
ENUM_CLASS(Kind, Global, IntrinsicModules, Module, MainProgram, Subprogram,
BlockData, DerivedType, BlockConstruct, Forall, OtherConstruct,
OpenACCConstruct, ImpliedDos)
OpenACCConstruct, ImpliedDos, OtherClause)
using ImportKind = common::ImportKind;
// Create the Global scope -- the root of the scope tree

View File

@@ -936,57 +936,64 @@ bool ClauseProcessor::processMap(
llvm::SmallVector<OmpMapMemberIndicesData>>
parentMemberIndices;
bool clauseFound = findRepeatableClause<omp::clause::Map>(
[&](const omp::clause::Map &clause, const parser::CharBlock &source) {
using Map = omp::clause::Map;
mlir::Location clauseLocation = converter.genLocation(source);
const auto &mapType = std::get<std::optional<Map::MapType>>(clause.t);
llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;
// If the map type is specified, then process it else Tofrom is the
// default.
Map::MapType type = mapType.value_or(Map::MapType::Tofrom);
switch (type) {
case Map::MapType::To:
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO;
break;
case Map::MapType::From:
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
break;
case Map::MapType::Tofrom:
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
break;
case Map::MapType::Alloc:
case Map::MapType::Release:
// alloc and release is the default map_type for the Target Data
// Ops, i.e. if no bits for map_type is supplied then alloc/release
// is implicitly assumed based on the target directive. Default
// value for Target Data and Enter Data is alloc and for Exit Data
// it is release.
break;
case Map::MapType::Delete:
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE;
}
auto process = [&](const omp::clause::Map &clause,
const parser::CharBlock &source) {
using Map = omp::clause::Map;
mlir::Location clauseLocation = converter.genLocation(source);
const auto &mapType = std::get<std::optional<Map::MapType>>(clause.t);
llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;
// If the map type is specified, then process it else Tofrom is the
// default.
Map::MapType type = mapType.value_or(Map::MapType::Tofrom);
switch (type) {
case Map::MapType::To:
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO;
break;
case Map::MapType::From:
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
break;
case Map::MapType::Tofrom:
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
break;
case Map::MapType::Alloc:
case Map::MapType::Release:
// alloc and release is the default map_type for the Target Data
// Ops, i.e. if no bits for map_type is supplied then alloc/release
// is implicitly assumed based on the target directive. Default
// value for Target Data and Enter Data is alloc and for Exit Data
// it is release.
break;
case Map::MapType::Delete:
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE;
}
auto &modTypeMods =
std::get<std::optional<Map::MapTypeModifiers>>(clause.t);
if (modTypeMods) {
if (llvm::is_contained(*modTypeMods, Map::MapTypeModifier::Always))
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS;
// Diagnose unimplemented map-type-modifiers.
if (llvm::any_of(*modTypeMods, [](Map::MapTypeModifier m) {
return m != Map::MapTypeModifier::Always;
})) {
TODO(currentLocation, "Map type modifiers (other than 'ALWAYS')"
" are not implemented yet");
}
}
processMapObjects(stmtCtx, clauseLocation,
std::get<omp::ObjectList>(clause.t), mapTypeBits,
parentMemberIndices, result.mapVars, *ptrMapSyms);
});
auto &modTypeMods =
std::get<std::optional<Map::MapTypeModifiers>>(clause.t);
if (modTypeMods) {
if (llvm::is_contained(*modTypeMods, Map::MapTypeModifier::Always))
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS;
// Diagnose unimplemented map-type-modifiers.
if (llvm::any_of(*modTypeMods, [](Map::MapTypeModifier m) {
return m != Map::MapTypeModifier::Always;
})) {
TODO(currentLocation, "Map type modifiers (other than 'ALWAYS')"
" are not implemented yet");
}
}
if (std::get<std::optional<omp::clause::Iterator>>(clause.t)) {
TODO(currentLocation,
"Support for iterator modifiers is not implemented yet");
}
processMapObjects(stmtCtx, clauseLocation,
std::get<omp::ObjectList>(clause.t), mapTypeBits,
parentMemberIndices, result.mapVars, *ptrMapSyms);
};
bool clauseFound = findRepeatableClause<omp::clause::Map>(process);
insertChildMapInfoIntoParent(converter, parentMemberIndices, result.mapVars,
*ptrMapSyms);

View File

@@ -232,6 +232,46 @@ MAKE_INCOMPLETE_CLASS(Match, Match);
// MAKE_INCOMPLETE_CLASS(Otherwise, ); // missing-in-parser
MAKE_INCOMPLETE_CLASS(When, When);
List<IteratorSpecifier>
makeIteratorSpecifiers(const parser::OmpIteratorSpecifier &inp,
semantics::SemanticsContext &semaCtx) {
List<IteratorSpecifier> specifiers;
auto &[begin, end, step] = std::get<parser::SubscriptTriplet>(inp.t).t;
assert(begin && end && "Expecting begin/end values");
evaluate::ExpressionAnalyzer ea{semaCtx};
MaybeExpr rbegin{ea.Analyze(*begin)}, rend{ea.Analyze(*end)};
MaybeExpr rstep;
if (step)
rstep = ea.Analyze(*step);
assert(rbegin && rend && "Unable to get range bounds");
Range range{{*rbegin, *rend, rstep}};
auto &tds = std::get<parser::TypeDeclarationStmt>(inp.t);
auto &entities = std::get<std::list<parser::EntityDecl>>(tds.t);
for (const parser::EntityDecl &ed : entities) {
auto &name = std::get<parser::ObjectName>(ed.t);
assert(name.symbol && "Expecting symbol for iterator variable");
auto *stype = name.symbol->GetType();
assert(stype && "Expecting symbol type");
IteratorSpecifier spec{{evaluate::DynamicType::From(*stype),
makeObject(name, semaCtx), range}};
specifiers.emplace_back(std::move(spec));
}
return specifiers;
}
Iterator makeIterator(const parser::OmpIteratorModifier &inp,
semantics::SemanticsContext &semaCtx) {
Iterator iterator;
for (auto &&spec : inp.v)
llvm::append_range(iterator, makeIteratorSpecifiers(spec, semaCtx));
return iterator;
}
DefinedOperator makeDefinedOperator(const parser::DefinedOperator &inp,
semantics::SemanticsContext &semaCtx) {
CLAUSET_ENUM_CONVERT( //
@@ -851,10 +891,24 @@ Map make(const parser::OmpClause::Map &inp,
);
auto &t0 = std::get<std::optional<std::list<wrapped::TypeModifier>>>(inp.v.t);
auto &t1 = std::get<std::optional<wrapped::Type>>(inp.v.t);
auto &t2 = std::get<parser::OmpObjectList>(inp.v.t);
auto &t1 =
std::get<std::optional<std::list<parser::OmpIteratorModifier>>>(inp.v.t);
auto &t2 = std::get<std::optional<std::list<wrapped::Type>>>(inp.v.t);
auto &t3 = std::get<parser::OmpObjectList>(inp.v.t);
std::optional<Map::MapType> maybeType = maybeApply(convert1, t1);
// These should have been diagnosed already.
assert((!t1 || t1->size() == 1) && "Only one iterator modifier is allowed");
assert((!t2 || t2->size() == 1) && "Only one map type is allowed");
auto iterator = [&]() -> std::optional<Iterator> {
if (t1)
return makeIterator(t1->front(), semaCtx);
return std::nullopt;
}();
std::optional<Map::MapType> maybeType;
if (t2)
maybeType = maybeApply(convert1, std::optional<wrapped::Type>(t2->front()));
std::optional<Map::MapTypeModifiers> maybeTypeMods = maybeApply(
[&](const std::list<wrapped::TypeModifier> &typeMods) {
@@ -867,8 +921,8 @@ Map make(const parser::OmpClause::Map &inp,
return Map{{/*MapType=*/maybeType,
/*MapTypeModifiers=*/maybeTypeMods,
/*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
/*LocatorList=*/makeObjects(t2, semaCtx)}};
/*Mapper=*/std::nullopt, /*Iterator=*/std::move(iterator),
/*LocatorList=*/makeObjects(t3, semaCtx)}};
}
// Match: incomplete

View File

@@ -9,6 +9,7 @@
#define FORTRAN_LOWER_OPENMP_CLAUSES_H
#include "flang/Evaluate/expression.h"
#include "flang/Evaluate/type.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/expression.h"
#include "flang/Semantics/semantics.h"
@@ -29,12 +30,7 @@ namespace Fortran::lower::omp {
using namespace Fortran;
using SomeExpr = semantics::SomeExpr;
using MaybeExpr = semantics::MaybeExpr;
// evaluate::SomeType doesn't provide == operation. It's not really used in
// flang's clauses so far, so a trivial implementation is sufficient.
struct TypeTy : public evaluate::SomeType {
bool operator==(const TypeTy &t) const { return true; }
};
using TypeTy = evaluate::DynamicType;
template <typename ExprTy>
struct IdTyTemplate {
@@ -150,6 +146,9 @@ std::optional<Object> getBaseObject(const Object &object,
semantics::SemanticsContext &semaCtx);
namespace clause {
using Range = tomp::type::RangeT<ExprTy>;
using Iterator = tomp::type::IteratorT<TypeTy, IdTy, ExprTy>;
using IteratorSpecifier = tomp::type::IteratorSpecifierT<TypeTy, IdTy, ExprTy>;
using DefinedOperator = tomp::type::DefinedOperatorT<IdTy, ExprTy>;
using ProcedureDesignator = tomp::type::ProcedureDesignatorT<IdTy, ExprTy>;
using ReductionOperator = tomp::type::ReductionIdentifierT<IdTy, ExprTy>;

View File

@@ -23,71 +23,160 @@ namespace Fortran::parser {
constexpr auto startOmpLine = skipStuffBeforeStatement >> "!$OMP "_sptok;
constexpr auto endOmpLine = space >> endOfLine;
// Map modifiers come from two categories: map-type-modifier and map-type.
// There can be zero or more map-type-modifiers, and zero or one map-type.
// Helper class to deal with a list of modifiers of various types.
// The list (to be parsed) is assumed to start with all modifiers of the
// first type, followed by a list of modifiers of the second type, etc.
// Each list can be empty, e.g.
// mod_of_kind_2, mod_of_kind_3, mod_of_kind_5, ...
// The result type is a tuple of optional lists of each modifier type.
template <typename Separator, typename Parser, typename... Parsers>
struct ConcatSeparated {
template <typename P>
using OptListOf = std::optional<std::list<typename P::resultType>>;
template <typename P> using TupleFor = std::tuple<OptListOf<P>>;
using resultType = std::tuple<OptListOf<Parser>, OptListOf<Parsers>...>;
constexpr ConcatSeparated(ConcatSeparated &&) = default;
constexpr ConcatSeparated(const ConcatSeparated &) = default;
constexpr ConcatSeparated(Separator sep, Parser p, Parsers... ps)
: parser_(p), sepAndParsers_(sep, ps...) {}
std::optional<resultType> Parse(ParseState &state) const {
// firstParser is a list parser, it returns optional<list>.
auto firstParser =
attempt(nonemptySeparated(parser_, std::get<0>(sepAndParsers_)));
if constexpr (sizeof...(Parsers) == 0) {
return TupleFor<Parser>{std::move(firstParser.Parse(state))};
} else {
using restParserType = ConcatSeparated<Separator, Parsers...>;
auto restParser = std::make_from_tuple<restParserType>(sepAndParsers_);
if (auto first{firstParser.Parse(state)}) {
if (attempt(std::get<0>(sepAndParsers_)).Parse(state)) {
return std::tuple_cat(TupleFor<Parser>(std::move(*first)),
std::move(*restParser.Parse(state)));
}
return std::tuple_cat(TupleFor<Parser>{std::move(*first)},
std::tuple<OptListOf<Parsers>...>{});
}
return std::tuple_cat(
TupleFor<Parser>{}, std::move(*restParser.Parse(state)));
}
}
private:
const Parser parser_;
const std::tuple<Separator, Parsers...> sepAndParsers_;
};
// Map modifiers come from four categories:
// - map-type-modifier,
// - mapper (not parsed yet),
// - iterator,
// - map-type.
// There can be zero or more map-type-modifiers, and zero or one modifier
// of every other kind.
// Syntax-wise they look like a single list, where the last element could
// be a map-type, and all elements in that list are comma-separated[1].
// Only if there was at least one modifier (of any kind) specified, the
// list must end with ":".
// [1] Any of the commas are optional, but that syntax has been deprecated,
// and the parsing code is intended to identify that. There are complications
// coming from the fact that the comma separating the two kinds of modifiers
// is only allowed if there is at least one modifier of each kind.
// The MapModifiers parser parses the modifier list as a whole, and returns
// a tuple with the (optional) map-type-modifier list, and the (optional)
// type modifier as its members.
// The list is then parsed, first with a mandatory separator, and if that
// fails, with an optional one. If the latter succeeds, a deprecation
// message is printed.
// There are complications coming from the fact that the comma separating the
// two kinds of modifiers is only allowed if there is at least one modifier of
// each kind. The MapModifiers parser utilizes the ConcatSeparated parser, which
// takes care of that. ConcatSeparated returns a tuple with optional lists of
// modifiers for every type.
// [1] Any of the commas are optional, but that syntax has been deprecated
// in OpenMP 5.2, and the parsing code keeps a record of whether the commas
// were present.
template <typename Separator> struct MapModifiers {
constexpr MapModifiers(
Separator sep, std::optional<MessageFixedText> msg = std::nullopt)
: sep_(sep), msg_(msg) {}
constexpr MapModifiers(Separator sep) : sep_(sep) {}
constexpr MapModifiers(const MapModifiers &) = default;
constexpr MapModifiers(MapModifiers &&) = default;
using resultType =
std::tuple<std::optional<std::list<OmpMapClause::TypeModifier>>,
std::optional<OmpMapClause::Type>>;
// Parsing of mappers is not supported yet.
using TypeModParser = Parser<OmpMapClause::TypeModifier>;
using IterParser = Parser<OmpIteratorModifier>;
using TypeParser = Parser<OmpMapClause::Type>;
using ModParser =
ConcatSeparated<Separator, TypeModParser, IterParser, TypeParser>;
using resultType = typename ModParser::resultType;
std::optional<resultType> Parse(ParseState &state) const {
auto pmod{Parser<OmpMapClause::TypeModifier>{}};
auto ptype{Parser<OmpMapClause::Type>{}};
auto startLoc{state.GetLocation()};
auto &&[mods, type] = [&]() -> resultType {
// The 'maybe' will return optional<optional<list>>, and the outer
// optional will never be nullopt.
if (auto mods{
*maybe(attempt(nonemptySeparated(pmod, sep_))).Parse(state)}) {
// mods = optional<list>, and the list is nonempty.
return attempt(sep_).Parse(state)
? resultType(mods, *maybe(attempt(ptype)).Parse(state))
: resultType(mods, std::nullopt);
auto mp = ModParser(sep_, TypeModParser{}, IterParser{}, TypeParser{});
auto mods = mp.Parse(state);
// The ModParser always "succeeds", i.e. even if the input is junk, it
// will return a tuple filled with nullopts. If any of the components
// is not a nullopt, expect a ":".
if (std::apply([](auto &&...opts) { return (... || !!opts); }, *mods)) {
if (!attempt(":"_tok).Parse(state)) {
return std::nullopt;
}
return {std::nullopt, *maybe(attempt(ptype)).Parse(state)};
}();
auto endLoc{state.GetLocation()};
// The above always "succeeds", i.e. even if the input is junk, it will
// return a tuple with two nullopts. If any of the components is not a
// nullopt, expect a ":".
if ((mods.has_value() || type.has_value()) &&
!attempt(":"_tok).Parse(state)) {
return std::nullopt;
}
if (msg_) {
state.Say(CharBlock{startLoc, endLoc}, *msg_);
}
return resultType(mods, type);
return std::move(mods);
}
private:
const Separator sep_;
std::optional<MessageFixedText> msg_;
};
// OpenMP Clauses
// [5.0] 2.1.6 iterator-specifier -> type-declaration-stmt = subscript-triple |
// identifier = subscript-triple
// [5.0:47:17-18] In an iterator-specifier, if the iterator-type is not
// specified then the type of that iterator is default integer.
// [5.0:49:14] The iterator-type must be an integer type.
static std::list<EntityDecl> makeEntityList(std::list<ObjectName> &&names) {
std::list<EntityDecl> entities;
for (auto iter = names.begin(), end = names.end(); iter != end; ++iter) {
EntityDecl entityDecl(
/*ObjectName=*/std::move(*iter), std::optional<ArraySpec>{},
std::optional<CoarraySpec>{}, std::optional<CharLength>{},
std::optional<Initialization>{});
entities.push_back(std::move(entityDecl));
}
return entities;
}
static TypeDeclarationStmt makeIterSpecDecl(
DeclarationTypeSpec &&spec, std::list<ObjectName> &&names) {
return TypeDeclarationStmt(
std::move(spec), std::list<AttrSpec>{}, makeEntityList(std::move(names)));
}
static TypeDeclarationStmt makeIterSpecDecl(std::list<ObjectName> &&names) {
// Assume INTEGER without kind selector.
DeclarationTypeSpec typeSpec(
IntrinsicTypeSpec{IntegerTypeSpec{std::nullopt}});
return TypeDeclarationStmt(std::move(typeSpec), std::list<AttrSpec>{},
makeEntityList(std::move(names)));
}
TYPE_PARSER(construct<OmpIteratorSpecifier>(
// Using Parser<TypeDeclarationStmt> or Parser<EntityDecl> has the problem
// that they will attempt to treat what follows the '=' as initialization.
// There are several issues with that,
// 1. integer :: i = 0:10 will be parsed as "integer :: i = 0", followed
// by triplet ":10".
// 2. integer :: j = i:10 will be flagged as an error because the
// initializer 'i' must be constant (in declarations). In an iterator
// specifier the 'j' is not an initializer and can be a variable.
(applyFunction<TypeDeclarationStmt>(makeIterSpecDecl,
Parser<DeclarationTypeSpec>{} / maybe("::"_tok),
nonemptyList(Parser<ObjectName>{}) / "="_tok) ||
applyFunction<TypeDeclarationStmt>(
makeIterSpecDecl, nonemptyList(Parser<ObjectName>{}) / "="_tok)),
subscriptTriplet))
// [5.0] 2.1.6 iterator -> iterator-specifier-list
TYPE_PARSER(construct<OmpIteratorModifier>("ITERATOR" >>
parenthesized(nonemptyList(sourced(Parser<OmpIteratorSpecifier>{})))))
// 2.15.3.1 DEFAULT (PRIVATE | FIRSTPRIVATE | SHARED | NONE)
TYPE_PARSER(construct<OmpDefaultClause>(
"PRIVATE" >> pure(OmpDefaultClause::Type::Private) ||
@@ -121,20 +210,22 @@ TYPE_PARSER(
"TO"_id >> pure(OmpMapClause::Type::To) ||
"TOFROM" >> pure(OmpMapClause::Type::Tofrom)))
template <bool CommasEverywhere>
static inline OmpMapClause makeMapClause(
std::tuple<std::optional<std::list<OmpMapClause::TypeModifier>>,
std::optional<OmpMapClause::Type>> &&mod,
OmpObjectList &&obj) {
return OmpMapClause{
std::move(std::get<0>(mod)), std::move(std::get<1>(mod)), std::move(obj)};
std::optional<std::list<OmpIteratorModifier>>,
std::optional<std::list<OmpMapClause::Type>>> &&mods,
OmpObjectList &&objs) {
auto &&[tm, it, ty] = std::move(mods);
return OmpMapClause{std::move(tm), std::move(it), std::move(ty),
std::move(objs), CommasEverywhere};
}
TYPE_PARSER(construct<OmpMapClause>(applyFunction<OmpMapClause>(makeMapClause,
(MapModifiers(","_tok) ||
MapModifiers(maybe(","_tok),
"the specification of modifiers without comma separators for the "
"'MAP' clause has been deprecated"_port_en_US)),
Parser<OmpObjectList>{})))
TYPE_PARSER(construct<OmpMapClause>(
applyFunction<OmpMapClause>(
makeMapClause<true>, MapModifiers(","_tok), Parser<OmpObjectList>{}) ||
applyFunction<OmpMapClause>(makeMapClause<false>,
MapModifiers(maybe(","_tok)), Parser<OmpObjectList>{})))
// [OpenMP 5.0]
// 2.19.7.2 defaultmap(implicit-behavior[:variable-category])

View File

@@ -85,6 +85,7 @@ constexpr Parser<Variable> variable; // R902
constexpr Parser<Substring> substring; // R908
constexpr Parser<DataRef> dataRef; // R911, R914, R917
constexpr Parser<StructureComponent> structureComponent; // R913
constexpr Parser<SubscriptTriplet> subscriptTriplet; // R921
constexpr Parser<AllocateStmt> allocateStmt; // R927
constexpr Parser<StatVariable> statVariable; // R929
constexpr Parser<StatOrErrmsg> statOrErrmsg; // R942 & R1165

View File

@@ -2067,6 +2067,16 @@ public:
},
x.u);
}
void Unparse(const OmpIteratorSpecifier &x) {
Walk(std::get<TypeDeclarationStmt>(x.t));
Put(" = ");
Walk(std::get<SubscriptTriplet>(x.t));
}
void Unparse(const OmpIteratorModifier &x) {
Word("ITERATOR(");
Walk(x.v);
Put(")");
}
void Unparse(const OmpLastprivateClause &x) {
Walk(
std::get<std::optional<OmpLastprivateClause::LastprivateModifier>>(x.t),
@@ -2076,13 +2086,32 @@ public:
void Unparse(const OmpMapClause &x) {
auto &typeMod =
std::get<std::optional<std::list<OmpMapClause::TypeModifier>>>(x.t);
auto &type = std::get<std::optional<OmpMapClause::Type>>(x.t);
Walk(typeMod);
if (typeMod.has_value() && type.has_value()) {
Put(", ");
auto &iter = std::get<std::optional<std::list<OmpIteratorModifier>>>(x.t);
auto &type = std::get<std::optional<std::list<OmpMapClause::Type>>>(x.t);
// For a given list of items, if the item has a value, then walk it.
// Print commas between items that have values.
// Return 'true' if something did get printed, otherwise 'false'.
bool needComma{false};
if (typeMod) {
Walk(*typeMod);
needComma = true;
}
Walk(type);
if (typeMod.has_value() || type.has_value()) {
if (iter) {
if (needComma) {
Put(", ");
}
Walk(*iter);
needComma = true;
}
if (type) {
if (needComma) {
Put(", ");
}
Walk(*type);
needComma = true;
}
if (needComma) {
Put(": ");
}
Walk(std::get<OmpObjectList>(x.t));

View File

@@ -9,6 +9,7 @@
#include "check-omp-structure.h"
#include "definable.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/expression.h"
#include "flang/Semantics/tools.h"
namespace Fortran::semantics {
@@ -556,6 +557,66 @@ void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) {
}
}
void OmpStructureChecker::CheckIteratorRange(
const parser::OmpIteratorSpecifier &x) {
// Check:
// 1. Whether begin/end are present.
// 2. Whether the step value is non-zero.
// 3. If the step has a known sign, whether the lower/upper bounds form
// a proper interval.
const auto &[begin, end, step]{std::get<parser::SubscriptTriplet>(x.t).t};
if (!begin || !end) {
context_.Say(x.source,
"The begin and end expressions in iterator range-specification are "
"mandatory"_err_en_US);
}
// [5.2:67:19] In a range-specification, if the step is not specified its
// value is implicitly defined to be 1.
if (auto stepv{step ? GetIntValue(*step) : std::optional<int64_t>{1}}) {
if (*stepv == 0) {
context_.Say(
x.source, "The step value in the iterator range is 0"_warn_en_US);
} else if (begin && end) {
std::optional<int64_t> beginv{GetIntValue(*begin)};
std::optional<int64_t> endv{GetIntValue(*end)};
if (beginv && endv) {
if (*stepv > 0 && *beginv > *endv) {
context_.Say(x.source,
"The begin value is greater than the end value in iterator "
"range-specification with a positive step"_warn_en_US);
} else if (*stepv < 0 && *beginv < *endv) {
context_.Say(x.source,
"The begin value is less than the end value in iterator "
"range-specification with a negative step"_warn_en_US);
}
}
}
}
}
void OmpStructureChecker::CheckIteratorModifier(
const parser::OmpIteratorModifier &x) {
// Check if all iterator variables have integer type.
for (auto &&iterSpec : x.v) {
bool isInteger{true};
auto &typeDecl{std::get<parser::TypeDeclarationStmt>(iterSpec.t)};
auto &typeSpec{std::get<parser::DeclarationTypeSpec>(typeDecl.t)};
if (!std::holds_alternative<parser::IntrinsicTypeSpec>(typeSpec.u)) {
isInteger = false;
} else {
auto &intrinType{std::get<parser::IntrinsicTypeSpec>(typeSpec.u)};
if (!std::holds_alternative<parser::IntegerTypeSpec>(intrinType.u)) {
isInteger = false;
}
}
if (!isInteger) {
context_.Say(iterSpec.source,
"The iterator variable must be of integer type"_err_en_US);
}
CheckIteratorRange(iterSpec);
}
}
void OmpStructureChecker::CheckLoopItrVariableIsInt(
const parser::OpenMPLoopConstruct &x) {
if (const auto &loopConstruct{
@@ -3082,9 +3143,40 @@ void OmpStructureChecker::CheckAllowedMapTypes(
void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_map);
using TypeMod = parser::OmpMapClause::TypeModifier;
using Type = parser::OmpMapClause::Type;
using IterMod = parser::OmpIteratorModifier;
unsigned version{context_.langOptions().OpenMPVersion};
if (auto commas{std::get<bool>(x.v.t)}; !commas && version >= 52) {
context_.Say(GetContext().clauseSource,
"The specification of modifiers without comma separators for the "
"'MAP' clause has been deprecated in OpenMP 5.2"_port_en_US);
}
if (auto &mapTypeMod{std::get<std::optional<std::list<TypeMod>>>(x.v.t)}) {
if (auto *dup{FindDuplicateEntry(*mapTypeMod)}) {
context_.Say(GetContext().clauseSource,
"Duplicate map-type-modifier entry '%s' will be ignored"_warn_en_US,
parser::ToUpperCaseLetters(parser::OmpMapClause::EnumToString(*dup)));
}
}
// The size of any of the optional lists is never 0, instead of the list
// being empty, it will be a nullopt.
if (auto &iterMod{std::get<std::optional<std::list<IterMod>>>(x.v.t)}) {
if (iterMod->size() != 1) {
context_.Say(GetContext().clauseSource,
"Only one iterator-modifier is allowed"_err_en_US);
}
CheckIteratorModifier(iterMod->front());
}
if (auto &mapType{std::get<std::optional<std::list<Type>>>(x.v.t)}) {
if (mapType->size() != 1) {
context_.Say(GetContext().clauseSource,
"Multiple map types are not allowed"_err_en_US);
return;
}
parser::OmpMapClause::Type type{mapType->front()};
if (const auto &mapType{std::get<std::optional<Type>>(x.v.t)}) {
switch (GetContext().directive) {
case llvm::omp::Directive::OMPD_target:
case llvm::omp::Directive::OMPD_target_teams:
@@ -3094,13 +3186,13 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
case llvm::omp::Directive::OMPD_target_data:
CheckAllowedMapTypes(
*mapType, {Type::To, Type::From, Type::Tofrom, Type::Alloc});
type, {Type::To, Type::From, Type::Tofrom, Type::Alloc});
break;
case llvm::omp::Directive::OMPD_target_enter_data:
CheckAllowedMapTypes(*mapType, {Type::To, Type::Alloc});
CheckAllowedMapTypes(type, {Type::To, Type::Alloc});
break;
case llvm::omp::Directive::OMPD_target_exit_data:
CheckAllowedMapTypes(*mapType, {Type::From, Type::Release, Type::Delete});
CheckAllowedMapTypes(type, {Type::From, Type::Release, Type::Delete});
break;
default:
break;

View File

@@ -153,6 +153,7 @@ private:
const parser::OmpScheduleModifierType::ModType &);
void CheckAllowedMapTypes(const parser::OmpMapClause::Type &,
const std::list<parser::OmpMapClause::Type> &);
template <typename T> const T *FindDuplicateEntry(const std::list<T> &);
llvm::StringRef getClauseName(llvm::omp::Clause clause) override;
llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override;
@@ -181,6 +182,8 @@ private:
bool CheckTargetBlockOnlyTeams(const parser::Block &);
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
void CheckIteratorModifier(const parser::OmpIteratorModifier &x);
void CheckLoopItrVariableIsInt(const parser::OpenMPLoopConstruct &x);
void CheckDoWhile(const parser::OpenMPLoopConstruct &x);
void CheckAssociatedLoopConstraints(const parser::OpenMPLoopConstruct &x);
@@ -245,5 +248,27 @@ private:
SymbolSourceMap deferredNonVariables_;
};
template <typename T>
const T *OmpStructureChecker::FindDuplicateEntry(const std::list<T> &list) {
// Add elements of the list to a set. If the insertion fails, return
// the address of the failing element.
// The objects of type T may not be copyable, so add their addresses
// to the set. The set will need to compare the actual objects, so
// the custom comparator is provided.
struct less {
bool operator()(const T *a, const T *b) const { return *a < *b; }
};
std::set<const T *, less> uniq;
for (const T &item : list) {
if (!uniq.insert(&item).second) {
return &item;
}
}
return nullptr;
}
} // namespace Fortran::semantics
#endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_

View File

@@ -591,9 +591,12 @@ public:
void Post(const parser::OmpMapClause &x) {
Symbol::Flag ompFlag = Symbol::Flag::OmpMapToFrom;
// There is only one `type' allowed, but it's parsed as a list. Multiple
// types are diagnosed in the semantic checks for OpenMP.
if (const auto &mapType{
std::get<std::optional<parser::OmpMapClause::Type>>(x.t)}) {
switch (*mapType) {
std::get<std::optional<std::list<parser::OmpMapClause::Type>>>(
x.t)}) {
switch (mapType->front()) {
case parser::OmpMapClause::Type::To:
ompFlag = Symbol::Flag::OmpMapTo;
break;

View File

@@ -1432,6 +1432,7 @@ public:
void AddOmpSourceRange(const parser::CharBlock &);
static bool NeedsScope(const parser::OpenMPBlockConstruct &);
static bool NeedsScope(const parser::OmpClause &);
bool Pre(const parser::OpenMPRequiresConstruct &x) {
AddOmpSourceRange(x.source);
@@ -1547,6 +1548,17 @@ public:
void Post(const parser::OpenMPAtomicConstruct &) {
messageHandler().set_currStmtSource(std::nullopt);
}
bool Pre(const parser::OmpClause &x) {
if (NeedsScope(x)) {
PushScope(Scope::Kind::OtherClause, nullptr);
}
return true;
}
void Post(const parser::OmpClause &x) {
if (NeedsScope(x)) {
PopScope();
}
}
};
bool OmpVisitor::NeedsScope(const parser::OpenMPBlockConstruct &x) {
@@ -1562,6 +1574,12 @@ bool OmpVisitor::NeedsScope(const parser::OpenMPBlockConstruct &x) {
}
}
bool OmpVisitor::NeedsScope(const parser::OmpClause &x) {
// Iterators contain declarations, whose scope extends until the end
// the clause.
return llvm::omp::canHaveIterator(x.Id());
}
void OmpVisitor::AddOmpSourceRange(const parser::CharBlock &source) {
messageHandler().set_currStmtSource(source);
currScope().AddSourceRange(source);
@@ -2422,7 +2440,7 @@ void ScopeHandler::PushScope(Scope &scope) {
currScope_ = &scope;
auto kind{currScope_->kind()};
if (kind != Scope::Kind::BlockConstruct &&
kind != Scope::Kind::OtherConstruct) {
kind != Scope::Kind::OtherConstruct && kind != Scope::Kind::OtherClause) {
BeginScope(scope);
}
// The name of a module or submodule cannot be "used" in its scope,
@@ -7698,6 +7716,23 @@ public:
return true;
}
// Iterator-modifiers contain variable declarations, and do introduce
// a new scope. These variables can only have integer types, and their
// scope only extends until the end of the clause. A potential alternative
// to the code below may be to ignore OpenMP clauses, but it's not clear
// if OMP-specific checks can be avoided altogether.
bool Pre(const parser::OmpClause &x) {
if (OmpVisitor::NeedsScope(x)) {
PushScope();
}
return true;
}
void Post(const parser::OmpClause &x) {
if (OmpVisitor::NeedsScope(x)) {
PopScope();
}
}
protected:
bool IsHidden(SourceName name) {
for (const auto &scope : nestedScopes_) {

View File

@@ -24,6 +24,7 @@ end
!PARSE-TREE: | | TypeModifier = Close
!PARSE-TREE: | | Type = To
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | | bool = 'true'
subroutine f01(x)
integer :: x
@@ -47,6 +48,7 @@ end
!PARSE-TREE: | | TypeModifier = Present
!PARSE-TREE: | | TypeModifier = Close
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | | bool = 'true'
subroutine f02(x)
integer :: x
@@ -67,6 +69,7 @@ end
!PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
!PARSE-TREE: | | Type = From
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | | bool = 'true'
subroutine f03(x)
integer :: x
@@ -86,15 +89,16 @@ end
!PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
!PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | | bool = 'true'
subroutine f10(x)
subroutine f04(x)
integer :: x
!$omp target map(ompx_hold always, present, close, to: x)
x = x + 1
!$omp end target
end
!UNPARSE: SUBROUTINE f10 (x)
!UNPARSE: SUBROUTINE f04 (x)
!UNPARSE: INTEGER x
!UNPARSE: !$OMP TARGET MAP(OMPX_HOLD, ALWAYS, PRESENT, CLOSE, TO: x)
!UNPARSE: x=x+1_4
@@ -110,15 +114,16 @@ end
!PARSE-TREE: | | TypeModifier = Close
!PARSE-TREE: | | Type = To
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | | bool = 'false'
subroutine f11(x)
subroutine f05(x)
integer :: x
!$omp target map(ompx_hold, always, present, close: x)
x = x + 1
!$omp end target
end
!UNPARSE: SUBROUTINE f11 (x)
!UNPARSE: SUBROUTINE f05 (x)
!UNPARSE: INTEGER x
!UNPARSE: !$OMP TARGET MAP(OMPX_HOLD, ALWAYS, PRESENT, CLOSE: x)
!UNPARSE: x=x+1_4
@@ -134,3 +139,180 @@ end
!PARSE-TREE: | | TypeModifier = Close
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
!PARSE-TREE: | | bool = 'true'
subroutine f10(x)
integer :: x(10)
!$omp target map(present, iterator(integer :: i = 1:10), to: x(i))
x = x + 1
!$omp end target
end
!UNPARSE: SUBROUTINE f10 (x)
!UNPARSE: INTEGER x(10_4)
!UNPARSE: !$OMP TARGET MAP(PRESENT, ITERATOR(INTEGER i = 1_4:10_4), TO: x(i))
!UNPARSE: x=x+1_4
!UNPARSE: !$OMP END TARGET
!UNPARSE: END SUBROUTINE
!PARSE-TREE: OmpBeginBlockDirective
!PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
!PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
!PARSE-TREE: | | TypeModifier = Present
!PARSE-TREE: | | OmpIteratorModifier -> OmpIteratorSpecifier
!PARSE-TREE: | | | TypeDeclarationStmt
!PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
!PARSE-TREE: | | | | EntityDecl
!PARSE-TREE: | | | | | Name = 'i'
!PARSE-TREE: | | | SubscriptTriplet
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '1_4'
!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '1'
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '10_4'
!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '10'
!PARSE-TREE: | | Type = To
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> ArrayElement
!PARSE-TREE: | | | DataRef -> Name = 'x'
!PARSE-TREE: | | | SectionSubscript -> Integer -> Expr = 'i'
!PARSE-TREE: | | | | Designator -> DataRef -> Name = 'i'
!PARSE-TREE: | | bool = 'true'
subroutine f11(x)
integer :: x(10)
!$omp target map(present, iterator(i = 1:10), to: x(i))
x = x + 1
!$omp end target
end
!UNPARSE: SUBROUTINE f11 (x)
!UNPARSE: INTEGER x(10_4)
!UNPARSE: !$OMP TARGET MAP(PRESENT, ITERATOR(INTEGER i = 1_4:10_4), TO: x(i))
!UNPARSE: x=x+1_4
!UNPARSE: !$OMP END TARGET
!UNPARSE: END SUBROUTINE
!PARSE-TREE: OmpBeginBlockDirective
!PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
!PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
!PARSE-TREE: | | TypeModifier = Present
!PARSE-TREE: | | OmpIteratorModifier -> OmpIteratorSpecifier
!PARSE-TREE: | | | TypeDeclarationStmt
!PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
!PARSE-TREE: | | | | EntityDecl
!PARSE-TREE: | | | | | Name = 'i'
!PARSE-TREE: | | | SubscriptTriplet
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '1_4'
!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '1'
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '10_4'
!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '10'
!PARSE-TREE: | | Type = To
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> ArrayElement
!PARSE-TREE: | | | DataRef -> Name = 'x'
!PARSE-TREE: | | | SectionSubscript -> Integer -> Expr = 'i'
!PARSE-TREE: | | | | Designator -> DataRef -> Name = 'i'
!PARSE-TREE: | | bool = 'true'
subroutine f12(x)
integer :: x(10)
!$omp target map(present, iterator(i = 1:10, integer :: j = 1:10), to: x((i + j) / 2))
x = x + 1
!$omp end target
end
!UNPARSE: SUBROUTINE f12 (x)
!UNPARSE: INTEGER x(10_4)
!UNPARSE: !$OMP TARGET MAP(PRESENT, ITERATOR(INTEGER i = 1_4:10_4, INTEGER j = 1_4:10_4), TO: x((i+j)/2_4))
!UNPARSE: x=x+1_4
!UNPARSE: !$OMP END TARGET
!UNPARSE: END SUBROUTINE
!PARSE-TREE: OmpBeginBlockDirective
!PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
!PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
!PARSE-TREE: | | TypeModifier = Present
!PARSE-TREE: | | OmpIteratorModifier -> OmpIteratorSpecifier
!PARSE-TREE: | | | TypeDeclarationStmt
!PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
!PARSE-TREE: | | | | EntityDecl
!PARSE-TREE: | | | | | Name = 'i'
!PARSE-TREE: | | | SubscriptTriplet
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '1_4'
!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '1'
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '10_4'
!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '10'
!PARSE-TREE: | | OmpIteratorSpecifier
!PARSE-TREE: | | | TypeDeclarationStmt
!PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
!PARSE-TREE: | | | | EntityDecl
!PARSE-TREE: | | | | | Name = 'j'
!PARSE-TREE: | | | SubscriptTriplet
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '1_4'
!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '1'
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '10_4'
!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '10'
!PARSE-TREE: | | Type = To
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> ArrayElement
!PARSE-TREE: | | | DataRef -> Name = 'x'
!PARSE-TREE: | | | SectionSubscript -> Integer -> Expr = '(i+j)/2_4'
!PARSE-TREE: | | | | Divide
!PARSE-TREE: | | | | | Expr = '(i+j)'
!PARSE-TREE: | | | | | | Parentheses -> Expr = 'i+j'
!PARSE-TREE: | | | | | | | Add
!PARSE-TREE: | | | | | | | | Expr = 'i'
!PARSE-TREE: | | | | | | | | | Designator -> DataRef -> Name = 'i'
!PARSE-TREE: | | | | | | | | Expr = 'j'
!PARSE-TREE: | | | | | | | | | Designator -> DataRef -> Name = 'j'
!PARSE-TREE: | | | | | Expr = '2_4'
!PARSE-TREE: | | | | | | LiteralConstant -> IntLiteralConstant = '2'
!PARSE-TREE: | | bool = 'true'
subroutine f90(x, y)
integer :: x(10)
integer :: y
integer, parameter :: p = 23
!$omp target map(present, iterator(i, j = y:p, k = i:j), to: x(k))
x = x + 1
!$omp end target
end
!UNPARSE: SUBROUTINE f90 (x, y)
!UNPARSE: INTEGER x(10_4)
!UNPARSE: INTEGER y
!UNPARSE: INTEGER, PARAMETER :: p = 23_4
!UNPARSE: !$OMP TARGET MAP(PRESENT, ITERATOR(INTEGER i, j = y:23_4, INTEGER k = i:j), TO: x(k))
!UNPARSE: x=x+1_4
!UNPARSE: !$OMP END TARGET
!UNPARSE: END SUBROUTINE
!PARSE-TREE: OmpBeginBlockDirective
!PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
!PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
!PARSE-TREE: | | TypeModifier = Present
!PARSE-TREE: | | OmpIteratorModifier -> OmpIteratorSpecifier
!PARSE-TREE: | | | TypeDeclarationStmt
!PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
!PARSE-TREE: | | | | EntityDecl
!PARSE-TREE: | | | | | Name = 'i'
!PARSE-TREE: | | | | EntityDecl
!PARSE-TREE: | | | | | Name = 'j'
!PARSE-TREE: | | | SubscriptTriplet
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = 'y'
!PARSE-TREE: | | | | | Designator -> DataRef -> Name = 'y'
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '23_4'
!PARSE-TREE: | | | | | Designator -> DataRef -> Name = 'p'
!PARSE-TREE: | | OmpIteratorSpecifier
!PARSE-TREE: | | | TypeDeclarationStmt
!PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
!PARSE-TREE: | | | | EntityDecl
!PARSE-TREE: | | | | | Name = 'k'
!PARSE-TREE: | | | SubscriptTriplet
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = 'i'
!PARSE-TREE: | | | | | Designator -> DataRef -> Name = 'i'
!PARSE-TREE: | | | | Scalar -> Integer -> Expr = 'j'
!PARSE-TREE: | | | | | Designator -> DataRef -> Name = 'j'
!PARSE-TREE: | | Type = To
!PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> ArrayElement
!PARSE-TREE: | | | DataRef -> Name = 'x'
!PARSE-TREE: | | | SectionSubscript -> Integer -> Expr = 'k'
!PARSE-TREE: | | | | Designator -> DataRef -> Name = 'k'
!PARSE-TREE: | | bool = 'true'

View File

@@ -1,8 +1,8 @@
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -Werror
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52 -Werror
subroutine f10(x)
integer :: x
!PORTABILITY: the specification of modifiers without comma separators for the 'MAP' clause has been deprecated
!PORTABILITY: The specification of modifiers without comma separators for the 'MAP' clause has been deprecated in OpenMP 5.2
!$omp target map(always, present close, to: x)
x = x + 1
!$omp end target
@@ -10,9 +10,81 @@ end
subroutine f11(x)
integer :: x
!PORTABILITY: the specification of modifiers without comma separators for the 'MAP' clause has been deprecated
!PORTABILITY: The specification of modifiers without comma separators for the 'MAP' clause has been deprecated in OpenMP 5.2
!$omp target map(always, present, close to: x)
x = x + 1
!$omp end target
end
subroutine f12(x)
integer :: x
!WARNING: Duplicate map-type-modifier entry 'PRESENT' will be ignored
!$omp target map(always, present, close, present, to: x)
x = x + 1
!$omp end target
end
subroutine f13(x)
integer :: x(10)
!ERROR: The iterator variable must be of integer type
!ERROR: Must have INTEGER type, but is REAL(4)
!$omp target map(present, iterator(real :: i = 1:10), to: x(i))
x = x + 1
!$omp end target
end
subroutine f14(x)
integer :: x(10)
!ERROR: The begin and end expressions in iterator range-specification are mandatory
!$omp target map(present, iterator(integer :: i = :10:1), to: x(i))
x = x + 1
!$omp end target
end
subroutine f15(x)
integer :: x(10)
!ERROR: The begin and end expressions in iterator range-specification are mandatory
!$omp target map(present, iterator(integer :: i = 1:), to: x(i))
x = x + 1
!$omp end target
end
subroutine f16(x)
integer :: x(10)
!ERROR: The begin and end expressions in iterator range-specification are mandatory
!$omp target map(present, iterator(integer :: i = 1::-1), to: x(i))
x = x + 1
!$omp end target
end
subroutine f17(x)
integer :: x(10)
!WARNING: The step value in the iterator range is 0
!$omp target map(present, iterator(integer :: i = 1:2:0), to: x(i))
x = x + 1
!$omp end target
end
subroutine f18(x)
integer :: x(10)
!WARNING: The begin value is less than the end value in iterator range-specification with a negative step
!$omp target map(present, iterator(integer :: i = 1:10:-2), to: x(i))
x = x + 1
!$omp end target
end
subroutine f19(x)
integer :: x(10)
!WARNING: The begin value is greater than the end value in iterator range-specification with a positive step
!$omp target map(present, iterator(integer :: i = 12:1:2), to: x(i))
x = x + 1
!$omp end target
end
subroutine f1a(x)
integer :: x(10)
!ERROR: Only one iterator-modifier is allowed
!$omp target map(present, iterator(i = 1:2), iterator(j = 1:2), to: x(i + j))
x = x + 1
!$omp end target
end

View File

@@ -32,6 +32,21 @@ bool isLeafConstruct(Directive D);
bool isCompositeConstruct(Directive D);
bool isCombinedConstruct(Directive D);
/// Can clause C have an iterator-modifier.
static constexpr inline bool canHaveIterator(Clause C) {
// [5.2:67:5]
switch (C) {
case OMPC_affinity:
case OMPC_depend:
case OMPC_from:
case OMPC_map:
case OMPC_to:
return true;
default:
return false;
}
}
/// Create a nicer version of a function name for humans to look at.
std::string prettifyFunctionName(StringRef FunctionName);