[OpenMP 5.0] Parsing/sema support for to clause with mapper modifier.

This patch implements the parsing and sema support for OpenMP to clause
with potential user-defined mappers attached. User defined mapper is a
new feature in OpenMP 5.0. A to/from clause can have an explicit or
implicit associated mapper, which instructs the compiler to generate and
use customized mapping functions. An example is shown below:

    struct S { int len; int *d; };
    #pragma omp declare mapper(id: struct S s) map(s, s.d[0:s.len])
    struct S ss;
    #pragma omp target update to(mapper(id): ss) // use the mapper with name 'id' to map ss to device

Contributed-by: <lildmh@gmail.com>

Differential Revision: https://reviews.llvm.org/D58523

llvm-svn: 354698
This commit is contained in:
Michael Kruse 2019-02-22 22:29:42 +00:00
parent 8fffa1dfa3
commit 01f670df8f
18 changed files with 309 additions and 118 deletions

View File

@ -158,8 +158,11 @@ public:
/// This structure contains most locations needed for by an OMPVarListClause.
struct OMPVarListLocTy {
/// Starting location of the clause (the clause keyword).
SourceLocation StartLoc;
/// Location of '('.
SourceLocation LParenLoc;
/// Ending location of the clause.
SourceLocation EndLoc;
OMPVarListLocTy() = default;
OMPVarListLocTy(SourceLocation StartLoc, SourceLocation LParenLoc,
@ -3609,9 +3612,13 @@ protected:
/// This structure contains all sizes needed for by an
/// OMPMappableExprListClause.
struct OMPMappableExprListSizeTy {
/// Number of expressions listed.
unsigned NumVars;
/// Number of unique base declarations.
unsigned NumUniqueDeclarations;
/// Number of component lists.
unsigned NumComponentLists;
/// Total number of expression components.
unsigned NumComponents;
OMPMappableExprListSizeTy() = default;
OMPMappableExprListSizeTy(unsigned NumVars, unsigned NumUniqueDeclarations,
@ -4969,6 +4976,10 @@ class OMPToClause final : public OMPMappableExprListClause<OMPToClause>,
/// Build clause with number of variables \a NumVars.
///
/// \param MapperQualifierLoc C++ nested name specifier for the associated
/// user-defined mapper.
/// \param MapperIdInfo The identifier of associated user-defined mapper.
/// \param MapType Map type.
/// \param Locs Locations needed to build a mappable clause. It includes 1)
/// StartLoc: starting location of the clause (the clause keyword); 2)
/// LParenLoc: location of '('; 3) EndLoc: ending location of the clause.
@ -4977,9 +4988,12 @@ class OMPToClause final : public OMPMappableExprListClause<OMPToClause>,
/// NumUniqueDeclarations: number of unique base declarations in this clause;
/// 3) NumComponentLists: number of component lists in this clause; and 4)
/// NumComponents: total number of expression components in the clause.
explicit OMPToClause(const OMPVarListLocTy &Locs,
explicit OMPToClause(NestedNameSpecifierLoc MapperQualifierLoc,
DeclarationNameInfo MapperIdInfo,
const OMPVarListLocTy &Locs,
const OMPMappableExprListSizeTy &Sizes)
: OMPMappableExprListClause(OMPC_to, Locs, Sizes) {}
: OMPMappableExprListClause(OMPC_to, Locs, Sizes, &MapperQualifierLoc,
&MapperIdInfo) {}
/// Build an empty clause.
///
@ -4994,7 +5008,9 @@ class OMPToClause final : public OMPMappableExprListClause<OMPToClause>,
/// Define the sizes of each trailing object array except the last one. This
/// is required for TrailingObjects to work properly.
size_t numTrailingObjects(OverloadToken<Expr *>) const {
return varlist_size();
// There are varlist_size() of expressions, and varlist_size() of
// user-defined mappers.
return 2 * varlist_size();
}
size_t numTrailingObjects(OverloadToken<ValueDecl *>) const {
return getUniqueDeclarationsNum();
@ -5013,10 +5029,18 @@ public:
/// \param Vars The original expression used in the clause.
/// \param Declarations Declarations used in the clause.
/// \param ComponentLists Component lists used in the clause.
/// \param UDMapperRefs References to user-defined mappers associated with
/// expressions used in the clause.
/// \param UDMQualifierLoc C++ nested name specifier for the associated
/// user-defined mapper.
/// \param MapperId The identifier of associated user-defined mapper.
static OMPToClause *Create(const ASTContext &C, const OMPVarListLocTy &Locs,
ArrayRef<Expr *> Vars,
ArrayRef<ValueDecl *> Declarations,
MappableExprComponentListsRef ComponentLists);
MappableExprComponentListsRef ComponentLists,
ArrayRef<Expr *> UDMapperRefs,
NestedNameSpecifierLoc UDMQualifierLoc,
DeclarationNameInfo MapperId);
/// Creates an empty clause with the place for \a NumVars variables.
///

View File

@ -1175,7 +1175,7 @@ def err_omp_declare_target_unexpected_clause: Error<
def err_omp_expected_clause: Error<
"expected at least one clause on '#pragma omp %0' directive">;
def err_omp_mapper_illegal_identifier : Error<
"illegal identifier on 'omp declare mapper' directive">;
"illegal OpenMP user-defined mapper identifier">;
def err_omp_mapper_expected_declarator : Error<
"expected declarator on 'omp declare mapper' directive">;

View File

@ -122,6 +122,9 @@
#ifndef OPENMP_MAP_MODIFIER_KIND
#define OPENMP_MAP_MODIFIER_KIND(Name)
#endif
#ifndef OPENMP_TO_MODIFIER_KIND
#define OPENMP_TO_MODIFIER_KIND(Name)
#endif
#ifndef OPENMP_DIST_SCHEDULE_KIND
#define OPENMP_DIST_SCHEDULE_KIND(Name)
#endif
@ -578,6 +581,9 @@ OPENMP_MAP_MODIFIER_KIND(always)
OPENMP_MAP_MODIFIER_KIND(close)
OPENMP_MAP_MODIFIER_KIND(mapper)
// Modifiers for 'to' clause.
OPENMP_TO_MODIFIER_KIND(mapper)
// Clauses allowed for OpenMP directive 'taskloop'.
OPENMP_TASKLOOP_CLAUSE(if)
OPENMP_TASKLOOP_CLAUSE(shared)
@ -934,6 +940,7 @@ OPENMP_DECLARE_MAPPER_CLAUSE(map)
#undef OPENMP_FOR_SIMD_CLAUSE
#undef OPENMP_MAP_KIND
#undef OPENMP_MAP_MODIFIER_KIND
#undef OPENMP_TO_MODIFIER_KIND
#undef OPENMP_DISTRIBUTE_CLAUSE
#undef OPENMP_DIST_SCHEDULE_KIND
#undef OPENMP_DEFAULTMAP_KIND

View File

@ -105,6 +105,14 @@ enum OpenMPMapModifierKind {
OMPC_MAP_MODIFIER_last
};
/// OpenMP modifier kind for 'to' clause.
enum OpenMPToModifierKind {
#define OPENMP_TO_MODIFIER_KIND(Name) \
OMPC_TO_MODIFIER_##Name,
#include "clang/Basic/OpenMPKinds.def"
OMPC_TO_MODIFIER_unknown
};
/// OpenMP attributes for 'dist_schedule' clause.
enum OpenMPDistScheduleClauseKind {
#define OPENMP_DIST_SCHEDULE_KIND(Name) OMPC_DIST_SCHEDULE_##Name,

View File

@ -2925,6 +2925,8 @@ public:
ParsedType ObjectType,
SourceLocation *TemplateKWLoc,
UnqualifiedId &Result);
/// Parses the mapper modifier in map, to, and from clauses.
bool parseMapperModifier(OpenMPVarListDataTy &Data);
/// Parses map-type-modifiers in map clause.
/// map([ [map-type-modifier[,] [map-type-modifier[,] ...] map-type : ] list)
/// where, map-type-modifier ::= always | close | mapper(mapper-identifier)

View File

@ -9471,8 +9471,11 @@ public:
SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation MLoc,
SourceLocation KindLoc, SourceLocation EndLoc);
/// Called on well-formed 'to' clause.
OMPClause *ActOnOpenMPToClause(ArrayRef<Expr *> VarList,
const OMPVarListLocTy &Locs);
OMPClause *
ActOnOpenMPToClause(ArrayRef<Expr *> VarList, CXXScopeSpec &MapperIdScopeSpec,
DeclarationNameInfo &MapperId,
const OMPVarListLocTy &Locs,
ArrayRef<Expr *> UnresolvedMappers = llvm::None);
/// Called on well-formed 'from' clause.
OMPClause *ActOnOpenMPFromClause(ArrayRef<Expr *> VarList,
const OMPVarListLocTy &Locs);

View File

@ -845,11 +845,11 @@ OMPMapClause::CreateEmpty(const ASTContext &C,
return new (Mem) OMPMapClause(Sizes);
}
OMPToClause *OMPToClause::Create(const ASTContext &C,
const OMPVarListLocTy &Locs,
ArrayRef<Expr *> Vars,
ArrayRef<ValueDecl *> Declarations,
MappableExprComponentListsRef ComponentLists) {
OMPToClause *OMPToClause::Create(
const ASTContext &C, const OMPVarListLocTy &Locs, ArrayRef<Expr *> Vars,
ArrayRef<ValueDecl *> Declarations,
MappableExprComponentListsRef ComponentLists, ArrayRef<Expr *> UDMapperRefs,
NestedNameSpecifierLoc UDMQualifierLoc, DeclarationNameInfo MapperId) {
OMPMappableExprListSizeTy Sizes;
Sizes.NumVars = Vars.size();
Sizes.NumUniqueDeclarations = getUniqueDeclarationsTotalNumber(Declarations);
@ -857,8 +857,8 @@ OMPToClause *OMPToClause::Create(const ASTContext &C,
Sizes.NumComponents = getComponentsTotalNumber(ComponentLists);
// We need to allocate:
// NumVars x Expr* - we have an original list expression for each clause list
// entry.
// 2 x NumVars x Expr* - we have an original list expression and an associated
// user-defined mapper for each clause list entry.
// NumUniqueDeclarations x ValueDecl* - unique base declarations associated
// with each component list.
// (NumUniqueDeclarations + NumComponentLists) x unsigned - we specify the
@ -869,13 +869,14 @@ OMPToClause *OMPToClause::Create(const ASTContext &C,
void *Mem = C.Allocate(
totalSizeToAlloc<Expr *, ValueDecl *, unsigned,
OMPClauseMappableExprCommon::MappableComponent>(
Sizes.NumVars, Sizes.NumUniqueDeclarations,
2 * Sizes.NumVars, Sizes.NumUniqueDeclarations,
Sizes.NumUniqueDeclarations + Sizes.NumComponentLists,
Sizes.NumComponents));
OMPToClause *Clause = new (Mem) OMPToClause(Locs, Sizes);
auto *Clause = new (Mem) OMPToClause(UDMQualifierLoc, MapperId, Locs, Sizes);
Clause->setVarRefs(Vars);
Clause->setUDMapperRefs(UDMapperRefs);
Clause->setClauseInfo(Declarations, ComponentLists);
return Clause;
}
@ -885,7 +886,7 @@ OMPToClause *OMPToClause::CreateEmpty(const ASTContext &C,
void *Mem = C.Allocate(
totalSizeToAlloc<Expr *, ValueDecl *, unsigned,
OMPClauseMappableExprCommon::MappableComponent>(
Sizes.NumVars, Sizes.NumUniqueDeclarations,
2 * Sizes.NumVars, Sizes.NumUniqueDeclarations,
Sizes.NumUniqueDeclarations + Sizes.NumComponentLists,
Sizes.NumComponents));
return new (Mem) OMPToClause(Sizes);
@ -1444,7 +1445,19 @@ void OMPClausePrinter::VisitOMPMapClause(OMPMapClause *Node) {
void OMPClausePrinter::VisitOMPToClause(OMPToClause *Node) {
if (!Node->varlist_empty()) {
OS << "to";
VisitOMPClauseList(Node, '(');
DeclarationNameInfo MapperId = Node->getMapperIdInfo();
if (MapperId.getName() && !MapperId.getName().isEmpty()) {
OS << '(';
OS << "mapper(";
NestedNameSpecifier *MapperNNS =
Node->getMapperQualifierLoc().getNestedNameSpecifier();
if (MapperNNS)
MapperNNS->print(OS, Policy);
OS << MapperId << "):";
VisitOMPClauseList(Node, ' ');
} else {
VisitOMPClauseList(Node, '(');
}
OS << ")";
}
}

View File

@ -116,6 +116,12 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind,
.Case(#Name, static_cast<unsigned>(OMPC_MAP_MODIFIER_##Name))
#include "clang/Basic/OpenMPKinds.def"
.Default(OMPC_MAP_unknown);
case OMPC_to:
return llvm::StringSwitch<unsigned>(Str)
#define OPENMP_TO_MODIFIER_KIND(Name) \
.Case(#Name, static_cast<unsigned>(OMPC_TO_MODIFIER_##Name))
#include "clang/Basic/OpenMPKinds.def"
.Default(OMPC_TO_MODIFIER_unknown);
case OMPC_dist_schedule:
return llvm::StringSwitch<OpenMPDistScheduleClauseKind>(Str)
#define OPENMP_DIST_SCHEDULE_KIND(Name) .Case(#Name, OMPC_DIST_SCHEDULE_##Name)
@ -174,7 +180,6 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind,
case OMPC_num_tasks:
case OMPC_hint:
case OMPC_uniform:
case OMPC_to:
case OMPC_from:
case OMPC_use_device_ptr:
case OMPC_is_device_ptr:
@ -260,6 +265,18 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
break;
}
llvm_unreachable("Invalid OpenMP 'map' clause type");
case OMPC_to:
switch (Type) {
case OMPC_TO_MODIFIER_unknown:
return "unknown";
#define OPENMP_TO_MODIFIER_KIND(Name) \
case OMPC_TO_MODIFIER_##Name: \
return #Name;
#include "clang/Basic/OpenMPKinds.def"
default:
break;
}
llvm_unreachable("Invalid OpenMP 'to' clause type");
case OMPC_dist_schedule:
switch (Type) {
case OMPC_DIST_SCHEDULE_unknown:
@ -333,7 +350,6 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
case OMPC_num_tasks:
case OMPC_hint:
case OMPC_uniform:
case OMPC_to:
case OMPC_from:
case OMPC_use_device_ptr:
case OMPC_is_device_ptr:

View File

@ -1959,6 +1959,34 @@ static OpenMPMapModifierKind isMapModifier(Parser &P) {
return TypeModifier;
}
/// Parse the mapper modifier in map, to, and from clauses.
bool Parser::parseMapperModifier(OpenMPVarListDataTy &Data) {
// Parse '('.
BalancedDelimiterTracker T(*this, tok::l_paren, tok::colon);
if (T.expectAndConsume(diag::err_expected_lparen_after, "mapper")) {
SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
StopBeforeMatch);
return true;
}
// Parse mapper-identifier
if (getLangOpts().CPlusPlus)
ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec,
/*ObjectType=*/nullptr,
/*EnteringContext=*/false);
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_default)) {
Diag(Tok.getLocation(), diag::err_omp_mapper_illegal_identifier);
SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
StopBeforeMatch);
return true;
}
auto &DeclNames = Actions.getASTContext().DeclarationNames;
Data.ReductionOrMapperId = DeclarationNameInfo(
DeclNames.getIdentifier(Tok.getIdentifierInfo()), Tok.getLocation());
ConsumeToken();
// Parse ')'.
return T.consumeClose();
}
/// Parse map-type-modifiers in map clause.
/// map([ [map-type-modifier[,] [map-type-modifier[,] ...] map-type : ] list)
/// where, map-type-modifier ::= always | close | mapper(mapper-identifier)
@ -1974,30 +2002,8 @@ bool Parser::parseMapTypeModifiers(OpenMPVarListDataTy &Data) {
Data.MapTypeModifiers.push_back(TypeModifier);
Data.MapTypeModifiersLoc.push_back(Tok.getLocation());
ConsumeToken();
// Parse '('.
BalancedDelimiterTracker T(*this, tok::l_paren, tok::colon);
if (T.expectAndConsume(diag::err_expected_lparen_after,
getOpenMPSimpleClauseTypeName(
OMPC_map, OMPC_MAP_MODIFIER_mapper))) {
SkipUntil(tok::colon, tok::annot_pragma_openmp_end, StopBeforeMatch);
if (parseMapperModifier(Data))
return true;
}
// Parse mapper-identifier
if (getLangOpts().CPlusPlus)
ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec,
/*ObjectType=*/nullptr,
/*EnteringContext=*/false);
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_default)) {
Diag(Tok.getLocation(), diag::err_omp_mapper_illegal_identifier);
SkipUntil(tok::colon, tok::annot_pragma_openmp_end, StopBeforeMatch);
return true;
}
auto &DeclNames = Actions.getASTContext().DeclarationNames;
Data.ReductionOrMapperId = DeclarationNameInfo(
DeclNames.getIdentifier(Tok.getIdentifierInfo()), Tok.getLocation());
ConsumeToken();
// Parse ')'.
T.consumeClose();
} else {
// For the case of unknown map-type-modifier or a map-type.
// Map-type is followed by a colon; the function returns when it
@ -2053,7 +2059,7 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
OpenMPVarListDataTy &Data) {
UnqualifiedId UnqualifiedReductionId;
bool InvalidReductionId = false;
bool IsInvalidMapModifier = false;
bool IsInvalidMapperModifier = false;
// Parse '('.
BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
@ -2142,9 +2148,11 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
// Only parse map-type-modifier[s] and map-type if a colon is present in
// the map clause.
if (ColonPresent) {
IsInvalidMapModifier = parseMapTypeModifiers(Data);
if (!IsInvalidMapModifier)
IsInvalidMapperModifier = parseMapTypeModifiers(Data);
if (!IsInvalidMapperModifier)
parseMapType(*this, Data);
else
SkipUntil(tok::colon, tok::annot_pragma_openmp_end, StopBeforeMatch);
}
if (Data.MapType == OMPC_MAP_unknown) {
Data.MapType = OMPC_MAP_tofrom;
@ -2153,6 +2161,28 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
if (Tok.is(tok::colon))
Data.ColonLoc = ConsumeToken();
} else if (Kind == OMPC_to) {
if (Tok.is(tok::identifier)) {
bool IsMapperModifier = false;
auto Modifier = static_cast<OpenMPToModifierKind>(
getOpenMPSimpleClauseType(Kind, PP.getSpelling(Tok)));
if (Modifier == OMPC_TO_MODIFIER_mapper)
IsMapperModifier = true;
if (IsMapperModifier) {
// Parse the mapper modifier.
ConsumeToken();
IsInvalidMapperModifier = parseMapperModifier(Data);
if (Tok.isNot(tok::colon)) {
if (!IsInvalidMapperModifier)
Diag(Tok, diag::warn_pragma_expected_colon) << ")";
SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end,
StopBeforeMatch);
}
// Consume ':'.
if (Tok.is(tok::colon))
ConsumeToken();
}
}
}
bool IsComma =
@ -2214,7 +2244,7 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
Vars.empty()) ||
(Kind != OMPC_depend && Kind != OMPC_map && Vars.empty()) ||
(MustHaveTail && !Data.TailExpr) || InvalidReductionId ||
IsInvalidMapModifier;
IsInvalidMapperModifier;
}
/// Parsing of OpenMP clause 'private', 'firstprivate', 'lastprivate',
@ -2247,10 +2277,10 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
/// 'depend' '(' in | out | inout : list | source ')'
/// map-clause:
/// 'map' '(' [ [ always [,] ] [ close [,] ]
/// [ mapper(mapper-identifier) [,] ]
/// [ mapper '(' mapper-identifier ')' [,] ]
/// to | from | tofrom | alloc | release | delete ':' ] list ')';
/// to-clause:
/// 'to' '(' list ')'
/// 'to' '(' [ mapper '(' mapper-identifier ')' ':' ] list ')'
/// from-clause:
/// 'from' '(' list ')'
/// use_device_ptr-clause:

View File

@ -9802,7 +9802,8 @@ OMPClause *Sema::ActOnOpenMPVarListClause(
DepLinMapLoc, ColonLoc, VarList, Locs);
break;
case OMPC_to:
Res = ActOnOpenMPToClause(VarList, Locs);
Res = ActOnOpenMPToClause(VarList, ReductionOrMapperIdScopeSpec,
ReductionOrMapperId, Locs);
break;
case OMPC_from:
Res = ActOnOpenMPFromClause(VarList, Locs);
@ -13140,17 +13141,23 @@ struct MappableVarListInfo {
static void checkMappableExpressionList(
Sema &SemaRef, DSAStackTy *DSAS, OpenMPClauseKind CKind,
MappableVarListInfo &MVLI, SourceLocation StartLoc,
CXXScopeSpec &MapperIdScopeSpec, DeclarationNameInfo MapperId,
ArrayRef<Expr *> UnresolvedMappers,
OpenMPMapClauseKind MapType = OMPC_MAP_unknown,
bool IsMapTypeImplicit = false, CXXScopeSpec *MapperIdScopeSpec = nullptr,
const DeclarationNameInfo *MapperId = nullptr,
ArrayRef<Expr *> UnresolvedMappers = llvm::None) {
bool IsMapTypeImplicit = false) {
// We only expect mappable expressions in 'to', 'from', and 'map' clauses.
assert((CKind == OMPC_map || CKind == OMPC_to || CKind == OMPC_from) &&
"Unexpected clause kind with mappable expressions!");
assert(
((CKind == OMPC_map && MapperIdScopeSpec && MapperId) ||
(CKind != OMPC_map && !MapperIdScopeSpec && !MapperId)) &&
"Map clauses and only map clauses have user-defined mapper identifiers.");
// If the identifier of user-defined mapper is not specified, it is "default".
// We do not change the actual name in this clause to distinguish whether a
// mapper is specified explicitly, i.e., it is not explicitly specified when
// MapperId.getName() is empty.
if (!MapperId.getName() || MapperId.getName().isEmpty()) {
auto &DeclNames = SemaRef.getASTContext().DeclarationNames;
MapperId.setName(DeclNames.getIdentifier(
&SemaRef.getASTContext().Idents.get("default")));
}
// Iterators to find the current unresolved mapper expression.
auto UMIt = UnresolvedMappers.begin(), UMEnd = UnresolvedMappers.end();
@ -13183,16 +13190,14 @@ static void checkMappableExpressionList(
if (VE->isValueDependent() || VE->isTypeDependent() ||
VE->isInstantiationDependent() ||
VE->containsUnexpandedParameterPack()) {
if (CKind == OMPC_map) {
if (CKind != OMPC_from) {
// Try to find the associated user-defined mapper.
ExprResult ER = buildUserDefinedMapperRef(
SemaRef, DSAS->getCurScope(), *MapperIdScopeSpec, *MapperId,
SemaRef, DSAS->getCurScope(), MapperIdScopeSpec, MapperId,
VE->getType().getCanonicalType(), UnresolvedMapper);
if (ER.isInvalid())
continue;
MVLI.UDMapperList.push_back(ER.get());
} else {
MVLI.UDMapperList.push_back(nullptr);
}
// We can only analyze this information once the missing information is
// resolved.
@ -13225,16 +13230,14 @@ static void checkMappableExpressionList(
if (const auto *TE = dyn_cast<CXXThisExpr>(BE)) {
// Add store "this" pointer to class in DSAStackTy for future checking
DSAS->addMappedClassesQualTypes(TE->getType());
if (CKind == OMPC_map) {
if (CKind != OMPC_from) {
// Try to find the associated user-defined mapper.
ExprResult ER = buildUserDefinedMapperRef(
SemaRef, DSAS->getCurScope(), *MapperIdScopeSpec, *MapperId,
SemaRef, DSAS->getCurScope(), MapperIdScopeSpec, MapperId,
VE->getType().getCanonicalType(), UnresolvedMapper);
if (ER.isInvalid())
continue;
MVLI.UDMapperList.push_back(ER.get());
} else {
MVLI.UDMapperList.push_back(nullptr);
}
// Skip restriction checking for variable or field declarations
MVLI.ProcessedVarList.push_back(RE);
@ -13352,16 +13355,16 @@ static void checkMappableExpressionList(
continue;
}
}
}
// Try to find the associated user-defined mapper.
// Try to find the associated user-defined mapper.
if (CKind != OMPC_from) {
ExprResult ER = buildUserDefinedMapperRef(
SemaRef, DSAS->getCurScope(), *MapperIdScopeSpec, *MapperId,
SemaRef, DSAS->getCurScope(), MapperIdScopeSpec, MapperId,
Type.getCanonicalType(), UnresolvedMapper);
if (ER.isInvalid())
continue;
MVLI.UDMapperList.push_back(ER.get());
} else {
MVLI.UDMapperList.push_back(nullptr);
}
// Save the current expression.
@ -13410,17 +13413,10 @@ OMPClause *Sema::ActOnOpenMPMapClause(
++Count;
}
// If the identifier of user-defined mapper is not specified, it is "default".
if (!MapperId.getName() || MapperId.getName().isEmpty()) {
auto &DeclNames = getASTContext().DeclarationNames;
MapperId.setName(
DeclNames.getIdentifier(&getASTContext().Idents.get("default")));
}
MappableVarListInfo MVLI(VarList);
checkMappableExpressionList(*this, DSAStack, OMPC_map, MVLI, Locs.StartLoc,
MapType, IsMapTypeImplicit, &MapperIdScopeSpec,
&MapperId, UnresolvedMappers);
MapperIdScopeSpec, MapperId, UnresolvedMappers,
MapType, IsMapTypeImplicit);
// We need to produce a map clause even if we don't have variables so that
// other diagnostics related with non-existing map clauses are accurate.
@ -14169,20 +14165,30 @@ void Sema::checkDeclIsAllowedInOpenMPTarget(Expr *E, Decl *D,
}
OMPClause *Sema::ActOnOpenMPToClause(ArrayRef<Expr *> VarList,
const OMPVarListLocTy &Locs) {
CXXScopeSpec &MapperIdScopeSpec,
DeclarationNameInfo &MapperId,
const OMPVarListLocTy &Locs,
ArrayRef<Expr *> UnresolvedMappers) {
MappableVarListInfo MVLI(VarList);
checkMappableExpressionList(*this, DSAStack, OMPC_to, MVLI, Locs.StartLoc);
checkMappableExpressionList(*this, DSAStack, OMPC_to, MVLI, Locs.StartLoc,
MapperIdScopeSpec, MapperId, UnresolvedMappers);
if (MVLI.ProcessedVarList.empty())
return nullptr;
return OMPToClause::Create(Context, Locs, MVLI.ProcessedVarList,
MVLI.VarBaseDeclarations, MVLI.VarComponents);
return OMPToClause::Create(
Context, Locs, MVLI.ProcessedVarList, MVLI.VarBaseDeclarations,
MVLI.VarComponents, MVLI.UDMapperList,
MapperIdScopeSpec.getWithLocInContext(Context), MapperId);
}
OMPClause *Sema::ActOnOpenMPFromClause(ArrayRef<Expr *> VarList,
const OMPVarListLocTy &Locs) {
MappableVarListInfo MVLI(VarList);
checkMappableExpressionList(*this, DSAStack, OMPC_from, MVLI, Locs.StartLoc);
CXXScopeSpec MapperIdScopeSpec;
DeclarationNameInfo MapperId;
ArrayRef<Expr *> UnresolvedMappers;
checkMappableExpressionList(*this, DSAStack, OMPC_from, MVLI, Locs.StartLoc,
MapperIdScopeSpec, MapperId, UnresolvedMappers);
if (MVLI.ProcessedVarList.empty())
return nullptr;

View File

@ -1902,8 +1902,12 @@ public:
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
OMPClause *RebuildOMPToClause(ArrayRef<Expr *> VarList,
const OMPVarListLocTy &Locs) {
return getSema().ActOnOpenMPToClause(VarList, Locs);
CXXScopeSpec &MapperIdScopeSpec,
DeclarationNameInfo &MapperId,
const OMPVarListLocTy &Locs,
ArrayRef<Expr *> UnresolvedMappers) {
return getSema().ActOnOpenMPToClause(VarList, MapperIdScopeSpec, MapperId,
Locs, UnresolvedMappers);
}
/// Build a new OpenMP 'from' clause.
@ -8811,34 +8815,37 @@ TreeTransform<Derived>::TransformOMPDeviceClause(OMPDeviceClause *C) {
C->getLParenLoc(), C->getEndLoc());
}
template <typename Derived>
OMPClause *TreeTransform<Derived>::TransformOMPMapClause(OMPMapClause *C) {
llvm::SmallVector<Expr *, 16> Vars;
template <typename Derived, class T>
bool transformOMPMappableExprListClause(
TreeTransform<Derived> &TT, OMPMappableExprListClause<T> *C,
llvm::SmallVectorImpl<Expr *> &Vars, CXXScopeSpec &MapperIdScopeSpec,
DeclarationNameInfo &MapperIdInfo,
llvm::SmallVectorImpl<Expr *> &UnresolvedMappers) {
// Transform expressions in the list.
Vars.reserve(C->varlist_size());
for (auto *VE : C->varlists()) {
ExprResult EVar = getDerived().TransformExpr(cast<Expr>(VE));
ExprResult EVar = TT.getDerived().TransformExpr(cast<Expr>(VE));
if (EVar.isInvalid())
return nullptr;
return true;
Vars.push_back(EVar.get());
}
// Transform mapper scope specifier and identifier.
NestedNameSpecifierLoc QualifierLoc;
if (C->getMapperQualifierLoc()) {
QualifierLoc = getDerived().TransformNestedNameSpecifierLoc(
QualifierLoc = TT.getDerived().TransformNestedNameSpecifierLoc(
C->getMapperQualifierLoc());
if (!QualifierLoc)
return nullptr;
return true;
}
CXXScopeSpec MapperIdScopeSpec;
MapperIdScopeSpec.Adopt(QualifierLoc);
DeclarationNameInfo MapperIdInfo = C->getMapperIdInfo();
MapperIdInfo = C->getMapperIdInfo();
if (MapperIdInfo.getName()) {
MapperIdInfo = getDerived().TransformDeclarationNameInfo(MapperIdInfo);
MapperIdInfo = TT.getDerived().TransformDeclarationNameInfo(MapperIdInfo);
if (!MapperIdInfo.getName())
return nullptr;
return true;
}
// Build a list of all candidate OMPDeclareMapperDecls, which is provided by
// the previous user-defined mapper lookup in dependent environment.
llvm::SmallVector<Expr *, 16> UnresolvedMappers;
for (auto *E : C->mapperlists()) {
// Transform all the decls.
if (E) {
@ -8846,18 +8853,31 @@ OMPClause *TreeTransform<Derived>::TransformOMPMapClause(OMPMapClause *C) {
UnresolvedSet<8> Decls;
for (auto *D : ULE->decls()) {
NamedDecl *InstD =
cast<NamedDecl>(getDerived().TransformDecl(E->getExprLoc(), D));
cast<NamedDecl>(TT.getDerived().TransformDecl(E->getExprLoc(), D));
Decls.addDecl(InstD, InstD->getAccess());
}
UnresolvedMappers.push_back(UnresolvedLookupExpr::Create(
SemaRef.Context, /*NamingClass=*/nullptr,
MapperIdScopeSpec.getWithLocInContext(SemaRef.Context), MapperIdInfo,
/*ADL=*/false, ULE->isOverloaded(), Decls.begin(), Decls.end()));
TT.getSema().Context, /*NamingClass=*/nullptr,
MapperIdScopeSpec.getWithLocInContext(TT.getSema().Context),
MapperIdInfo, /*ADL=*/true, ULE->isOverloaded(), Decls.begin(),
Decls.end()));
} else {
UnresolvedMappers.push_back(nullptr);
}
}
return false;
}
template <typename Derived>
OMPClause *TreeTransform<Derived>::TransformOMPMapClause(OMPMapClause *C) {
OMPVarListLocTy Locs(C->getBeginLoc(), C->getLParenLoc(), C->getEndLoc());
llvm::SmallVector<Expr *, 16> Vars;
CXXScopeSpec MapperIdScopeSpec;
DeclarationNameInfo MapperIdInfo;
llvm::SmallVector<Expr *, 16> UnresolvedMappers;
if (transformOMPMappableExprListClause<Derived, OMPMapClause>(
*this, C, Vars, MapperIdScopeSpec, MapperIdInfo, UnresolvedMappers))
return nullptr;
return getDerived().RebuildOMPMapClause(
C->getMapTypeModifiers(), C->getMapTypeModifiersLoc(), MapperIdScopeSpec,
MapperIdInfo, C->getMapType(), C->isImplicitMapType(), C->getMapLoc(),
@ -8942,16 +8962,16 @@ TreeTransform<Derived>::TransformOMPDefaultmapClause(OMPDefaultmapClause *C) {
template <typename Derived>
OMPClause *TreeTransform<Derived>::TransformOMPToClause(OMPToClause *C) {
llvm::SmallVector<Expr *, 16> Vars;
Vars.reserve(C->varlist_size());
for (auto *VE : C->varlists()) {
ExprResult EVar = getDerived().TransformExpr(cast<Expr>(VE));
if (EVar.isInvalid())
return 0;
Vars.push_back(EVar.get());
}
OMPVarListLocTy Locs(C->getBeginLoc(), C->getLParenLoc(), C->getEndLoc());
return getDerived().RebuildOMPToClause(Vars, Locs);
llvm::SmallVector<Expr *, 16> Vars;
CXXScopeSpec MapperIdScopeSpec;
DeclarationNameInfo MapperIdInfo;
llvm::SmallVector<Expr *, 16> UnresolvedMappers;
if (transformOMPMappableExprListClause<Derived, OMPToClause>(
*this, C, Vars, MapperIdScopeSpec, MapperIdInfo, UnresolvedMappers))
return nullptr;
return getDerived().RebuildOMPToClause(Vars, MapperIdScopeSpec, MapperIdInfo,
Locs, UnresolvedMappers);
}
template <typename Derived>

View File

@ -12396,6 +12396,10 @@ void OMPClauseReader::VisitOMPDefaultmapClause(OMPDefaultmapClause *C) {
void OMPClauseReader::VisitOMPToClause(OMPToClause *C) {
C->setLParenLoc(Record.readSourceLocation());
C->setMapperQualifierLoc(Record.readNestedNameSpecifierLoc());
DeclarationNameInfo DNI;
Record.readDeclarationNameInfo(DNI);
C->setMapperIdInfo(DNI);
auto NumVars = C->varlist_size();
auto UniqueDecls = C->getUniqueDeclarationsNum();
auto TotalLists = C->getTotalComponentListNum();
@ -12407,6 +12411,12 @@ void OMPClauseReader::VisitOMPToClause(OMPToClause *C) {
Vars.push_back(Record.readSubExpr());
C->setVarRefs(Vars);
SmallVector<Expr *, 16> UDMappers;
UDMappers.reserve(NumVars);
for (unsigned I = 0; I < NumVars; ++I)
UDMappers.push_back(Record.readSubExpr());
C->setUDMapperRefs(UDMappers);
SmallVector<ValueDecl *, 16> Decls;
Decls.reserve(UniqueDecls);
for (unsigned i = 0; i < UniqueDecls; ++i)

View File

@ -6861,8 +6861,12 @@ void OMPClauseWriter::VisitOMPToClause(OMPToClause *C) {
Record.push_back(C->getTotalComponentListNum());
Record.push_back(C->getTotalComponentsNum());
Record.AddSourceLocation(C->getLParenLoc());
Record.AddNestedNameSpecifierLoc(C->getMapperQualifierLoc());
Record.AddDeclarationNameInfo(C->getMapperIdInfo());
for (auto *E : C->varlists())
Record.AddStmt(E);
for (auto *E : C->mapperlists())
Record.AddStmt(E);
for (auto *D : C->all_decls())
Record.AddDeclRef(D);
for (auto N : C->all_num_lists())

View File

@ -48,6 +48,8 @@ int main() {
#pragma omp target map(mapper(default), from: dd[0:10])
// CHECK: #pragma omp target map(mapper(default),from: dd[0:10])
{ dd[0].i++; }
#pragma omp target update to(mapper(id): vv)
// CHECK: #pragma omp target update to(mapper(id): vv)
}
return 0;
}

View File

@ -81,6 +81,8 @@ T foo(T a) {
{ fd.a++; }
#pragma omp target map(mapper(idd) alloc: fd.b)
{ fd.b.k++; }
#pragma omp target update to(mapper(id): fd)
#pragma omp target update to(mapper(idd): fd.b)
return 0;
}
@ -93,6 +95,8 @@ T foo(T a) {
// CHECK: }
// CHECK: #pragma omp target map(mapper(id),alloc: fd)
// CHECK: #pragma omp target map(mapper(idd),alloc: fd.b)
// CHECK: #pragma omp target update to(mapper(id): fd)
// CHECK: #pragma omp target update to(mapper(idd): fd.b)
// CHECK: }
// CHECK: template<> int foo<int>(int a) {
// CHECK: #pragma omp declare mapper (id : struct foodat v) map(tofrom: v.a)
@ -103,6 +107,8 @@ T foo(T a) {
// CHECK: }
// CHECK: #pragma omp target map(mapper(id),alloc: fd)
// CHECK: #pragma omp target map(mapper(idd),alloc: fd.b)
// CHECK: #pragma omp target update to(mapper(id): fd)
// CHECK: #pragma omp target update to(mapper(idd): fd.b)
// CHECK: }
// CHECK: int main() {
@ -119,6 +125,12 @@ int main() {
#pragma omp target map(mapper(default) tofrom: dd)
// CHECK: #pragma omp target map(mapper(default),tofrom: dd)
{ dd.d++; }
#pragma omp target update to(mapper(N1::id) : vc)
// CHECK: #pragma omp target update to(mapper(N1::id): vc)
#pragma omp target update to(mapper(dat<double>::id): vvv)
// CHECK: #pragma omp target update to(mapper(dat<double>::id): vvv)
#pragma omp declare mapper(id: N1::vec v) map(v.len)
// CHECK: #pragma omp declare mapper (id : N1::vec v) map(tofrom: v.len)
{

View File

@ -26,10 +26,12 @@ public:
#pragma omp declare mapper(id: C s) map(s.a)
// CHECK-LABEL: @.__omp_offloading_{{.*}}foo{{.*}}_l49.region_id = weak constant i8 0
// CHECK-LABEL: @.__omp_offloading_{{.*}}foo{{.*}}_l52.region_id = weak constant i8 0
// CHECK-DAG: [[SIZES:@.+]] = {{.+}}constant [1 x i{{64|32}}] [i{{64|32}} 4]
// CHECK-DAG: [[TYPES:@.+]] = {{.+}}constant [1 x i64] [i64 35]
// CHECK: [[SIZES:@.+]] = {{.+}}constant [1 x i[[sz:64|32]]] [i{{64|32}} 4]
// CHECK: [[TYPES:@.+]] = {{.+}}constant [1 x i64] [i64 35]
// CHECK: [[TSIZES:@.+]] = {{.+}}constant [1 x i[[sz]]] [i[[sz]] 4]
// CHECK: [[TTYPES:@.+]] = {{.+}}constant [1 x i64] [i64 33]
// CHECK-LABEL: foo{{.*}}(
void foo(int a){
@ -46,11 +48,22 @@ void foo(int a){
// CHECK-DAG: [[CP1:%.+]] = bitcast i8** [[P1]] to %class.C**
// CHECK-DAG: store %class.C* [[VAL:%[^,]+]], %class.C** [[CBP1]]
// CHECK-DAG: store %class.C* [[VAL]], %class.C** [[CP1]]
// CHECK: call void [[KERNEL:@.+]](%class.C* [[VAL]])
#pragma omp target map(mapper(id),tofrom: c)
{
++c.a;
}
// CHECK: call void [[KERNEL:@.+]](%class.C* [[VAL]])
// CHECK-DAG: call void @__tgt_target_data_update(i64 -1, i32 1, i8** [[TGEPBP:%.+]], i8** [[TGEPP:%.+]], i[[sz]]* getelementptr {{.+}}[1 x i[[sz]]]* [[TSIZES]], i32 0, i32 0), {{.+}}getelementptr {{.+}}[1 x i64]* [[TTYPES]]{{.+}})
// CHECK-DAG: [[TGEPBP]] = getelementptr inbounds {{.+}}[[TBP:%[^,]+]], i{{.+}} 0, i{{.+}} 0
// CHECK-DAG: [[TGEPP]] = getelementptr inbounds {{.+}}[[TP:%[^,]+]], i{{.+}} 0, i{{.+}} 0
// CHECK-DAG: [[TBP0:%.+]] = getelementptr inbounds {{.+}}[[TBP]], i{{.+}} 0, i{{.+}} 0
// CHECK-DAG: [[TP0:%.+]] = getelementptr inbounds {{.+}}[[TP]], i{{.+}} 0, i{{.+}} 0
// CHECK-DAG: [[TCBP0:%.+]] = bitcast i8** [[TBP0]] to %class.C**
// CHECK-DAG: [[TCP0:%.+]] = bitcast i8** [[TP0]] to %class.C**
// CHECK-DAG: store %class.C* [[VAL]], %class.C** [[TCBP0]]
// CHECK-DAG: store %class.C* [[VAL]], %class.C** [[TCP0]]
#pragma omp target update to(mapper(id): c)
}

View File

@ -22,7 +22,7 @@ struct vec { // expec
#pragma omp declare mapper(bb:struct vec v) private(v) // expected-error {{expected at least one clause on '#pragma omp declare mapper' directive}} // expected-error {{unexpected OpenMP clause 'private' in directive '#pragma omp declare mapper'}}
#pragma omp declare mapper(cc:struct vec v) map(v) ( // expected-warning {{extra tokens at the end of '#pragma omp declare mapper' are ignored}}
#pragma omp declare mapper(++: struct vec v) map(v.len) // expected-error {{illegal identifier on 'omp declare mapper' directive}}
#pragma omp declare mapper(++: struct vec v) map(v.len) // expected-error {{illegal OpenMP user-defined mapper identifier}}
#pragma omp declare mapper(id1: struct vec v) map(v.len, temp) // expected-error {{only variable v is allowed in map clauses of this 'omp declare mapper' directive}}
#pragma omp declare mapper(default : struct vec kk) map(kk.data[0:2]) // expected-note {{previous definition is here}}
#pragma omp declare mapper(struct vec v) map(v.len) // expected-error {{redefinition of user-defined mapper for type 'struct vec' with name 'default'}}
@ -50,6 +50,14 @@ int fun(int arg) {
{}
#pragma omp target map(mapper(aa) to:vv) map(close mapper(aa) from:v1)
{}
#pragma omp target update to(mapper) // expected-error {{expected '(' after 'mapper'}} expected-error {{expected expression}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper() // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected expression}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper:vv) // expected-error {{expected '(' after 'mapper'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(:vv) // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(aa :vv) // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(ab):vv) // expected-error {{cannot find a valid user-defined mapper for type 'struct vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(aa):vv)
}
}
return arg;

View File

@ -29,7 +29,7 @@ public:
#pragma omp declare mapper(bb: vec v) private(v) // expected-error {{expected at least one clause on '#pragma omp declare mapper' directive}} // expected-error {{unexpected OpenMP clause 'private' in directive '#pragma omp declare mapper'}}
#pragma omp declare mapper(cc: vec v) map(v) ( // expected-warning {{extra tokens at the end of '#pragma omp declare mapper' are ignored}}
#pragma omp declare mapper(++: vec v) map(v.len) // expected-error {{illegal identifier on 'omp declare mapper' directive}}
#pragma omp declare mapper(++: vec v) map(v.len) // expected-error {{illegal OpenMP user-defined mapper identifier}}
#pragma omp declare mapper(id1: vec v) map(v.len, temp) // expected-error {{only variable v is allowed in map clauses of this 'omp declare mapper' directive}}
#pragma omp declare mapper(default : vec kk) map(kk.data[0:2]) // expected-note {{previous definition is here}}
#pragma omp declare mapper(vec v) map(v.len) // expected-error {{redefinition of user-defined mapper for type 'vec' with name 'default'}}
@ -74,9 +74,9 @@ int fun(int arg) {
{}
#pragma omp target map(mapper(ab) :vv) // expected-error {{missing map type}} expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'ab'}}
{}
#pragma omp target map(mapper(N2::) :vv) // expected-error {{use of undeclared identifier 'N2'}} expected-error {{illegal identifier on 'omp declare mapper' directive}}
#pragma omp target map(mapper(N2::) :vv) // expected-error {{use of undeclared identifier 'N2'}} expected-error {{illegal OpenMP user-defined mapper identifier}}
{}
#pragma omp target map(mapper(N1::) :vv) // expected-error {{illegal identifier on 'omp declare mapper' directive}}
#pragma omp target map(mapper(N1::) :vv) // expected-error {{illegal OpenMP user-defined mapper identifier}}
{}
#pragma omp target map(mapper(aa) :vv) // expected-error {{missing map type}}
{}
@ -86,6 +86,19 @@ int fun(int arg) {
{}
#pragma omp target map(mapper(N1::stack<int>::id) to:vv)
{}
#pragma omp target update to(mapper) // expected-error {{expected '(' after 'mapper'}} expected-error {{expected expression}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper() // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected expression}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper:vv) // expected-error {{expected '(' after 'mapper'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(:vv) // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(aa :vv) // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(N2:: :vv) // expected-error {{use of undeclared identifier 'N2'}} expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(N1:: :vv) // expected-error {{illegal OpenMP user-defined mapper identifier}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(N1::aa) :vv) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'aa'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(ab):vv) // expected-error {{cannot find a valid user-defined mapper for type 'vec' with name 'ab'}} expected-error {{expected at least one 'to' clause or 'from' clause specified to '#pragma omp target update'}}
#pragma omp target update to(mapper(aa) a:vv) // expected-warning {{missing ':' after ) - ignoring}}
#pragma omp target update to(mapper(aa):vv)
#pragma omp target update to(mapper(N1::stack<int>::id) :vv)
}
#pragma omp declare mapper(id: vec v) map(v.len) // expected-error {{redefinition of user-defined mapper for type 'vec' with name 'id'}}
}