summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
authorDmitry Polukhin <dmitry.polukhin@gmail.com>2016-04-06 11:38:59 +0000
committerDmitry Polukhin <dmitry.polukhin@gmail.com>2016-04-06 11:38:59 +0000
commit0b0da296e6de09610621467279a99eb290522a88 (patch)
tree1658d323a4f2382dfdfdb387db998bfde5095e9d /clang/lib
parentb1f7d4d79cbc720bba2c0f82cfe0a0e0c6077896 (diff)
downloadbcm5719-llvm-0b0da296e6de09610621467279a99eb290522a88.tar.gz
bcm5719-llvm-0b0da296e6de09610621467279a99eb290522a88.zip
[OPENMP] Parsing and Sema support for 'omp declare target' directive
Add parsing, sema analysis for 'declare target' construct for OpenMP 4.0 (4.5 support will be added in separate patch). The declare target directive specifies that variables, functions (C, C++ and Fortran), and subroutines (Fortran) are mapped to a device. The declare target directive is a declarative directive. In Clang declare target is implemented as implicit attribute for the declaration. The syntax of the declare target directive is as follows: #pragma omp declare target declarations-definition-seq #pragma omp end declare target Based on patch from Michael Wong http://reviews.llvm.org/D15321 llvm-svn: 265530
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ASTContext.cpp3
-rw-r--r--clang/lib/AST/DeclPrinter.cpp5
-rw-r--r--clang/lib/Basic/OpenMPKinds.cpp2
-rw-r--r--clang/lib/Frontend/MultiplexConsumer.cpp6
-rw-r--r--clang/lib/Parse/ParseOpenMP.cpp56
-rw-r--r--clang/lib/Sema/SemaDecl.cpp3
-rw-r--r--clang/lib/Sema/SemaExpr.cpp3
-rw-r--r--clang/lib/Sema/SemaOpenMP.cpp147
-rw-r--r--clang/lib/Serialization/ASTCommon.h1
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp5
-rw-r--r--clang/lib/Serialization/ASTWriter.cpp13
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp6
12 files changed, 248 insertions, 2 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index aa9277cdc1a..3a55e2310ce 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -8528,6 +8528,9 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
return false;
} else if (isa<PragmaCommentDecl>(D))
return true;
+ else if (isa<OMPThreadPrivateDecl>(D) ||
+ D->hasAttr<OMPDeclareTargetDeclAttr>())
+ return true;
else if (isa<PragmaDetectMismatchDecl>(D))
return true;
else if (isa<OMPThreadPrivateDecl>(D))
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index f7a8eb2729f..9d76f65b62a 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -358,6 +358,11 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
if (Terminator)
Out << Terminator;
Out << "\n";
+
+ // Declare target attribute is special one, natural spelling for the pragma
+ // assumes "ending" construct so print it here.
+ if (D->hasAttr<OMPDeclareTargetDeclAttr>())
+ Out << "#pragma omp end declare target\n";
}
if (!Decls.empty())
diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp
index fa40d016c9e..07a410f052c 100644
--- a/clang/lib/Basic/OpenMPKinds.cpp
+++ b/clang/lib/Basic/OpenMPKinds.cpp
@@ -547,6 +547,8 @@ bool clang::isAllowedClauseForDirective(OpenMPDirectiveKind DKind,
break;
}
break;
+ case OMPD_declare_target:
+ case OMPD_end_declare_target:
case OMPD_unknown:
case OMPD_threadprivate:
case OMPD_section:
diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp
index ccda8c360a4..581ed13ce09 100644
--- a/clang/lib/Frontend/MultiplexConsumer.cpp
+++ b/clang/lib/Frontend/MultiplexConsumer.cpp
@@ -125,6 +125,7 @@ public:
void FunctionDefinitionInstantiated(const FunctionDecl *D) override;
void DeclarationMarkedUsed(const Decl *D) override;
void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override;
+ void DeclarationMarkedOpenMPDeclareTarget(const Decl *D) override;
void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override;
void AddedAttributeToRecord(const Attr *Attr,
const RecordDecl *Record) override;
@@ -219,6 +220,11 @@ void MultiplexASTMutationListener::DeclarationMarkedOpenMPThreadPrivate(
for (size_t i = 0, e = Listeners.size(); i != e; ++i)
Listeners[i]->DeclarationMarkedOpenMPThreadPrivate(D);
}
+void MultiplexASTMutationListener::DeclarationMarkedOpenMPDeclareTarget(
+ const Decl *D) {
+ for (auto *L : Listeners)
+ L->DeclarationMarkedOpenMPDeclareTarget(D);
+}
void MultiplexASTMutationListener::RedefinedHiddenDefinition(const NamedDecl *D,
Module *M) {
for (auto *L : Listeners)
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index dcff1595611..a280bb9e267 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -31,6 +31,8 @@ enum OpenMPDirectiveKindEx {
OMPD_cancellation = OMPD_unknown + 1,
OMPD_data,
OMPD_declare,
+ OMPD_end,
+ OMPD_end_declare,
OMPD_enter,
OMPD_exit,
OMPD_point,
@@ -51,6 +53,7 @@ static unsigned getOpenMPDirectiveKindEx(StringRef S) {
.Case("cancellation", OMPD_cancellation)
.Case("data", OMPD_data)
.Case("declare", OMPD_declare)
+ .Case("end", OMPD_end)
.Case("enter", OMPD_enter)
.Case("exit", OMPD_exit)
.Case("point", OMPD_point)
@@ -66,6 +69,9 @@ static OpenMPDirectiveKind ParseOpenMPDirectiveKind(Parser &P) {
{ OMPD_cancellation, OMPD_point, OMPD_cancellation_point },
{ OMPD_declare, OMPD_reduction, OMPD_declare_reduction },
{ OMPD_declare, OMPD_simd, OMPD_declare_simd },
+ { OMPD_declare, OMPD_target, OMPD_declare_target },
+ { OMPD_end, OMPD_declare, OMPD_end_declare },
+ { OMPD_end_declare, OMPD_target, OMPD_end_declare_target },
{ OMPD_target, OMPD_data, OMPD_target_data },
{ OMPD_target, OMPD_enter, OMPD_target_enter },
{ OMPD_target, OMPD_exit, OMPD_target_exit },
@@ -456,6 +462,53 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
return Actions.ActOnOpenMPDeclareSimdDirective(Ptr, BS,
SourceRange(Loc, EndLoc));
}
+ case OMPD_declare_target: {
+ SourceLocation DTLoc = ConsumeAnyToken();
+ if (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ Diag(Tok, diag::warn_omp_extra_tokens_at_eol)
+ << getOpenMPDirectiveName(OMPD_declare_target);
+ SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
+ }
+ // Skip the last annot_pragma_openmp_end.
+ ConsumeAnyToken();
+
+ if (!Actions.ActOnStartOpenMPDeclareTargetDirective(DTLoc))
+ return DeclGroupPtrTy();
+
+ DKind = ParseOpenMPDirectiveKind(*this);
+ while (DKind != OMPD_end_declare_target && DKind != OMPD_declare_target &&
+ Tok.isNot(tok::eof) && Tok.isNot(tok::r_brace)) {
+ ParsedAttributesWithRange attrs(AttrFactory);
+ MaybeParseCXX11Attributes(attrs);
+ MaybeParseMicrosoftAttributes(attrs);
+ ParseExternalDeclaration(attrs);
+ if (Tok.isAnnotation() && Tok.is(tok::annot_pragma_openmp)) {
+ TentativeParsingAction TPA(*this);
+ ConsumeToken();
+ DKind = ParseOpenMPDirectiveKind(*this);
+ if (DKind != OMPD_end_declare_target)
+ TPA.Revert();
+ else
+ TPA.Commit();
+ }
+ }
+
+ if (DKind == OMPD_end_declare_target) {
+ ConsumeAnyToken();
+ if (Tok.isNot(tok::annot_pragma_openmp_end)) {
+ Diag(Tok, diag::warn_omp_extra_tokens_at_eol)
+ << getOpenMPDirectiveName(OMPD_end_declare_target);
+ SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch);
+ }
+ // Skip the last annot_pragma_openmp_end.
+ ConsumeAnyToken();
+ } else {
+ Diag(Tok, diag::err_expected_end_declare_target);
+ Diag(DTLoc, diag::note_matching) << "'#pragma omp declare target'";
+ }
+ Actions.ActOnFinishOpenMPDeclareTargetDirective();
+ return DeclGroupPtrTy();
+ }
case OMPD_unknown:
Diag(Tok, diag::err_omp_unknown_directive);
break;
@@ -491,6 +544,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
case OMPD_taskloop:
case OMPD_taskloop_simd:
case OMPD_distribute:
+ case OMPD_end_declare_target:
Diag(Tok, diag::err_omp_unexpected_directive)
<< getOpenMPDirectiveName(DKind);
break;
@@ -711,6 +765,8 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
break;
}
case OMPD_declare_simd:
+ case OMPD_declare_target:
+ case OMPD_end_declare_target:
Diag(Tok, diag::err_omp_unexpected_directive)
<< getOpenMPDirectiveName(DKind);
SkipUntil(tok::annot_pragma_openmp_end);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 0d91e976d47..774af59e497 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5052,6 +5052,9 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
CurContext->addHiddenDecl(New);
}
+ if (isInOpenMPDeclareTargetContext())
+ checkDeclIsAllowedInOpenMPTarget(nullptr, New);
+
return New;
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index de9ebe77190..cfd58983dfc 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -13814,6 +13814,9 @@ void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) {
static void MarkExprReferenced(Sema &SemaRef, SourceLocation Loc,
Decl *D, Expr *E, bool MightBeOdrUse) {
+ if (SemaRef.isInOpenMPDeclareTargetContext())
+ SemaRef.checkDeclIsAllowedInOpenMPTarget(E, D);
+
if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
DoMarkVarDeclReferenced(SemaRef, Loc, Var, E);
return;
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 8d53dc4e724..0ef796557ae 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -644,6 +644,11 @@ DSAStackTy::DSAVarData DSAStackTy::getTopDSA(ValueDecl *D, bool FromParent) {
return DVar;
}
+ if (Stack.size() == 1) {
+ // Not in OpenMP execution region and top scope was already checked.
+ return DVar;
+ }
+
// OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
// in a Construct, C/C++, predetermined, p.4]
// Static data members are shared.
@@ -1706,6 +1711,8 @@ void Sema::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind, Scope *CurScope) {
case OMPD_target_exit_data:
case OMPD_declare_reduction:
case OMPD_declare_simd:
+ case OMPD_declare_target:
+ case OMPD_end_declare_target:
llvm_unreachable("OpenMP Directive is not allowed");
case OMPD_unknown:
llvm_unreachable("Unknown OpenMP directive");
@@ -3158,6 +3165,8 @@ StmtResult Sema::ActOnOpenMPExecutableDirective(
Res = ActOnOpenMPDistributeDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc, VarsWithInheritedDSA);
break;
+ case OMPD_declare_target:
+ case OMPD_end_declare_target:
case OMPD_threadprivate:
case OMPD_declare_reduction:
case OMPD_declare_simd:
@@ -10354,3 +10363,141 @@ OMPClause *Sema::ActOnOpenMPDefaultmapClause(
return new (Context)
OMPDefaultmapClause(StartLoc, LParenLoc, MLoc, KindLoc, EndLoc, Kind, M);
}
+
+bool Sema::ActOnStartOpenMPDeclareTargetDirective(SourceLocation Loc) {
+ DeclContext *CurLexicalContext = getCurLexicalContext();
+ if (!CurLexicalContext->isFileContext() &&
+ !CurLexicalContext->isExternCContext() &&
+ !CurLexicalContext->isExternCXXContext()) {
+ Diag(Loc, diag::err_omp_region_not_file_context);
+ return false;
+ }
+ if (IsInOpenMPDeclareTargetContext) {
+ Diag(Loc, diag::err_omp_enclosed_declare_target);
+ return false;
+ }
+
+ IsInOpenMPDeclareTargetContext = true;
+ return true;
+}
+
+void Sema::ActOnFinishOpenMPDeclareTargetDirective() {
+ assert(IsInOpenMPDeclareTargetContext &&
+ "Unexpected ActOnFinishOpenMPDeclareTargetDirective");
+
+ IsInOpenMPDeclareTargetContext = false;
+}
+
+static void checkDeclInTargetContext(SourceLocation SL, SourceRange SR,
+ Sema &SemaRef, Decl *D) {
+ if (!D)
+ return;
+ Decl *LD = nullptr;
+ if (isa<TagDecl>(D)) {
+ LD = cast<TagDecl>(D)->getDefinition();
+ } else if (isa<VarDecl>(D)) {
+ LD = cast<VarDecl>(D)->getDefinition();
+
+ // If this is an implicit variable that is legal and we do not need to do
+ // anything.
+ if (cast<VarDecl>(D)->isImplicit()) {
+ D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(SemaRef.Context));
+ if (ASTMutationListener *ML = SemaRef.Context.getASTMutationListener())
+ ML->DeclarationMarkedOpenMPDeclareTarget(D);
+ return;
+ }
+
+ } else if (isa<FunctionDecl>(D)) {
+ const FunctionDecl *FD = nullptr;
+ if (cast<FunctionDecl>(D)->hasBody(FD))
+ LD = const_cast<FunctionDecl *>(FD);
+
+ // If the definition is associated with the current declaration in the
+ // target region (it can be e.g. a lambda) that is legal and we do not need
+ // to do anything else.
+ if (LD == D) {
+ D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(SemaRef.Context));
+ if (ASTMutationListener *ML = SemaRef.Context.getASTMutationListener())
+ ML->DeclarationMarkedOpenMPDeclareTarget(D);
+ return;
+ }
+ }
+ if (!LD)
+ LD = D;
+ if (LD && !LD->hasAttr<OMPDeclareTargetDeclAttr>() &&
+ (isa<VarDecl>(LD) || isa<FunctionDecl>(LD))) {
+ // Outlined declaration is not declared target.
+ if (LD->isOutOfLine()) {
+ SemaRef.Diag(LD->getLocation(), diag::warn_omp_not_in_target_context);
+ SemaRef.Diag(SL, diag::note_used_here) << SR;
+ } else {
+ DeclContext *DC = LD->getDeclContext();
+ while (DC) {
+ if (isa<FunctionDecl>(DC) &&
+ cast<FunctionDecl>(DC)->hasAttr<OMPDeclareTargetDeclAttr>())
+ break;
+ DC = DC->getParent();
+ }
+ if (DC)
+ return;
+
+ // Is not declared in target context.
+ SemaRef.Diag(LD->getLocation(), diag::warn_omp_not_in_target_context);
+ SemaRef.Diag(SL, diag::note_used_here) << SR;
+ }
+ // Mark decl as declared target to prevent further diagnostic.
+ D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(SemaRef.Context));
+ if (ASTMutationListener *ML = SemaRef.Context.getASTMutationListener())
+ ML->DeclarationMarkedOpenMPDeclareTarget(D);
+ }
+}
+
+static bool checkValueDeclInTarget(SourceLocation SL, SourceRange SR,
+ Sema &SemaRef, DSAStackTy *Stack,
+ ValueDecl *VD) {
+ if (VD->hasAttr<OMPDeclareTargetDeclAttr>())
+ return true;
+ if (!CheckTypeMappable(SL, SR, SemaRef, Stack, VD->getType()))
+ return false;
+ return true;
+}
+
+void Sema::checkDeclIsAllowedInOpenMPTarget(Expr *E, Decl *D) {
+ if (!D || D->isInvalidDecl())
+ return;
+ SourceRange SR = E ? E->getSourceRange() : D->getSourceRange();
+ SourceLocation SL = E ? E->getLocStart() : D->getLocation();
+ // 2.10.6: threadprivate variable cannot appear in a declare target directive.
+ if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ if (DSAStack->isThreadPrivate(VD)) {
+ Diag(SL, diag::err_omp_threadprivate_in_target);
+ ReportOriginalDSA(*this, DSAStack, VD, DSAStack->getTopDSA(VD, false));
+ return;
+ }
+ }
+ if (ValueDecl *VD = dyn_cast<ValueDecl>(D)) {
+ // Problem if any with var declared with incomplete type will be reported
+ // as normal, so no need to check it here.
+ if ((E || !VD->getType()->isIncompleteType()) &&
+ !checkValueDeclInTarget(SL, SR, *this, DSAStack, VD)) {
+ // Mark decl as declared target to prevent further diagnostic.
+ if (isa<VarDecl>(VD) || isa<FunctionDecl>(VD)) {
+ VD->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(Context));
+ if (ASTMutationListener *ML = Context.getASTMutationListener())
+ ML->DeclarationMarkedOpenMPDeclareTarget(VD);
+ }
+ return;
+ }
+ }
+ if (!E) {
+ // Checking declaration inside declare target region.
+ if (!D->hasAttr<OMPDeclareTargetDeclAttr>() &&
+ (isa<VarDecl>(D) || isa<FunctionDecl>(D))) {
+ D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(Context));
+ if (ASTMutationListener *ML = Context.getASTMutationListener())
+ ML->DeclarationMarkedOpenMPDeclareTarget(D);
+ }
+ return;
+ }
+ checkDeclInTargetContext(E->getExprLoc(), E->getSourceRange(), *this, D);
+}
diff --git a/clang/lib/Serialization/ASTCommon.h b/clang/lib/Serialization/ASTCommon.h
index 64f583c9872..641165e4178 100644
--- a/clang/lib/Serialization/ASTCommon.h
+++ b/clang/lib/Serialization/ASTCommon.h
@@ -37,6 +37,7 @@ enum DeclUpdateKind {
UPD_MANGLING_NUMBER,
UPD_STATIC_LOCAL_NUMBER,
UPD_DECL_MARKED_OPENMP_THREADPRIVATE,
+ UPD_DECL_MARKED_OPENMP_DECLARETARGET,
UPD_DECL_EXPORTED,
UPD_ADDED_ATTR_TO_RECORD
};
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index b2fdbb8aa2d..47ac4076757 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -3879,6 +3879,11 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile,
Reader.Context, ReadSourceRange(Record, Idx)));
break;
+ case UPD_DECL_MARKED_OPENMP_DECLARETARGET:
+ D->addAttr(OMPDeclareTargetDeclAttr::CreateImplicit(
+ Reader.Context, ReadSourceRange(Record, Idx)));
+ break;
+
case UPD_DECL_EXPORTED: {
unsigned SubmoduleID = readSubmoduleID(Record, Idx);
auto *Exported = cast<NamedDecl>(D);
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index c77a7099642..3a1901d5e68 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -4760,6 +4760,11 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
D->getAttr<OMPThreadPrivateDeclAttr>()->getRange());
break;
+ case UPD_DECL_MARKED_OPENMP_DECLARETARGET:
+ Record.AddSourceRange(
+ D->getAttr<OMPDeclareTargetDeclAttr>()->getRange());
+ break;
+
case UPD_DECL_EXPORTED:
Record.push_back(getSubmoduleID(Update.getModule()));
break;
@@ -5887,6 +5892,14 @@ void ASTWriter::DeclarationMarkedOpenMPThreadPrivate(const Decl *D) {
DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_MARKED_OPENMP_THREADPRIVATE));
}
+void ASTWriter::DeclarationMarkedOpenMPDeclareTarget(const Decl *D) {
+ assert(!WritingAST && "Already writing the AST!");
+ if (!D->isFromASTFile())
+ return;
+
+ DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_MARKED_OPENMP_DECLARETARGET));
+}
+
void ASTWriter::RedefinedHiddenDefinition(const NamedDecl *D, Module *M) {
assert(!WritingAST && "Already writing the AST!");
assert(D->isHidden() && "expected a hidden declaration");
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 79453be8158..4c0fa1a201d 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2133,8 +2133,10 @@ static bool isRequiredDecl(const Decl *D, ASTContext &Context,
// An ObjCMethodDecl is never considered as "required" because its
// implementation container always is.
- // File scoped assembly or obj-c implementation must be seen.
- if (isa<FileScopeAsmDecl>(D) || isa<ObjCImplDecl>(D))
+ // File scoped assembly or obj-c or OMP declare target implementation must be
+ // seen.
+ if (isa<FileScopeAsmDecl>(D) || isa<ObjCImplDecl>(D) ||
+ D->hasAttr<OMPDeclareTargetDeclAttr>())
return true;
// ImportDecl is used by codegen to determine the set of imported modules to
OpenPOWER on IntegriCloud