Skip to content

Commit a5a14cb

Browse files
committed
[OpenMP] Add initial support for omp [begin/end] assumes
The `assumes` directive is an OpenMP 5.1 feature that allows the user to provide assumptions to the optimizer. Assumptions can refer to directives (`absent` and `contains` clauses), expressions (`holds` clause), or generic properties (`no_openmp_routines`, `ext_ABCD`, ...). The `assumes` spelling is used for assumptions in the global scope while `assume` is used for executable contexts with an associated structured block. This patch only implements the global spellings. While clauses with arguments are "accepted" by the parser, they will simply be ignored for now. The implementation lowers the assumptions directly to the `AssumptionAttr`. Reviewed By: ABataev Differential Revision: https://reviews.llvm.org/D91980
1 parent dcaec81 commit a5a14cb

File tree

15 files changed

+740
-3
lines changed

15 files changed

+740
-3
lines changed

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,16 @@ def err_expected_end_declare_target_or_variant : Error<
12911291
def err_expected_begin_declare_variant
12921292
: Error<"'#pragma omp end declare variant' with no matching '#pragma omp "
12931293
"begin declare variant'">;
1294+
def err_expected_begin_assumes
1295+
: Error<"'#pragma omp end assumes' with no matching '#pragma omp begin assumes'">;
1296+
def warn_omp_unknown_assumption_clause_missing_id
1297+
: Warning<"valid %0 clauses start with %1; %select{token|tokens}2 will be ignored">,
1298+
InGroup<OpenMPClauses>;
1299+
def warn_omp_unknown_assumption_clause_without_args
1300+
: Warning<"%0 clause should not be followed by arguments; tokens will be ignored">,
1301+
InGroup<OpenMPClauses>;
1302+
def note_omp_assumption_clause_continue_here
1303+
: Note<"the ignored tokens spans until here">;
12941304
def err_omp_declare_target_unexpected_clause: Error<
12951305
"unexpected '%0' clause, only %select{'to' or 'link'|'to', 'link' or 'device_type'}1 clauses expected">;
12961306
def err_omp_expected_clause: Error<

clang/include/clang/Parse/Parser.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3104,6 +3104,13 @@ class Parser : public CodeCompletionHandler {
31043104
void ParseOMPDeclareVariantClauses(DeclGroupPtrTy Ptr, CachedTokens &Toks,
31053105
SourceLocation Loc);
31063106

3107+
/// Parse 'omp [begin] assume[s]' directive.
3108+
void ParseOpenMPAssumesDirective(OpenMPDirectiveKind DKind,
3109+
SourceLocation Loc);
3110+
3111+
/// Parse 'omp end assumes' directive.
3112+
void ParseOpenMPEndAssumesDirective(SourceLocation Loc);
3113+
31073114
/// Parse clauses for '#pragma omp declare target'.
31083115
DeclGroupPtrTy ParseOMPDeclareTargetClauses();
31093116
/// Parse '#pragma omp end declare target'.

clang/include/clang/Sema/Sema.h

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10128,6 +10128,13 @@ class Sema final {
1012810128
/// The current `omp begin/end declare variant` scopes.
1012910129
SmallVector<OMPDeclareVariantScope, 4> OMPDeclareVariantScopes;
1013010130

10131+
/// The current `omp begin/end assumes` scopes.
10132+
SmallVector<AssumptionAttr *, 4> OMPAssumeScoped;
10133+
10134+
/// All `omp assumes` we encountered so far.
10135+
SmallVector<AssumptionAttr *, 4> OMPAssumeGlobal;
10136+
10137+
public:
1013110138
/// The declarator \p D defines a function in the scope \p S which is nested
1013210139
/// in an `omp begin/end declare variant` scope. In this method we create a
1013310140
/// declaration for \p D and rename \p D according to the OpenMP context
@@ -10141,10 +10148,11 @@ class Sema final {
1014110148
void ActOnFinishedFunctionDefinitionInOpenMPDeclareVariantScope(
1014210149
Decl *D, SmallVectorImpl<FunctionDecl *> &Bases);
1014310150

10144-
public:
10151+
/// Act on \p D, a function definition inside of an `omp [begin/end] assumes`.
10152+
void ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(Decl *D);
1014510153

10146-
/// Can we exit a scope at the moment.
10147-
bool isInOpenMPDeclareVariantScope() {
10154+
/// Can we exit an OpenMP declare variant scope at the moment.
10155+
bool isInOpenMPDeclareVariantScope() const {
1014810156
return !OMPDeclareVariantScopes.empty();
1014910157
}
1015010158

@@ -10260,6 +10268,22 @@ class Sema final {
1026010268
ArrayRef<Expr *> VarList,
1026110269
ArrayRef<OMPClause *> Clauses,
1026210270
DeclContext *Owner = nullptr);
10271+
10272+
/// Called on well-formed '#pragma omp [begin] assume[s]'.
10273+
void ActOnOpenMPAssumesDirective(SourceLocation Loc,
10274+
OpenMPDirectiveKind DKind,
10275+
ArrayRef<StringRef> Assumptions,
10276+
bool SkippedClauses);
10277+
10278+
/// Check if there is an active global `omp begin assumes` directive.
10279+
bool isInOpenMPAssumeScope() const { return !OMPAssumeScoped.empty(); }
10280+
10281+
/// Check if there is an active global `omp assumes` directive.
10282+
bool hasGlobalOpenMPAssumes() const { return !OMPAssumeGlobal.empty(); }
10283+
10284+
/// Called on well-formed '#pragma omp end assumes'.
10285+
void ActOnOpenMPEndAssumesDirective();
10286+
1026310287
/// Called on well-formed '#pragma omp requires'.
1026410288
DeclGroupPtrTy ActOnOpenMPRequiresDirective(SourceLocation Loc,
1026510289
ArrayRef<OMPClause *> ClauseList);

clang/lib/Parse/ParseOpenMP.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "clang/Parse/RAIIObjectsForParser.h"
2222
#include "clang/Sema/Scope.h"
2323
#include "llvm/ADT/PointerIntPair.h"
24+
#include "llvm/ADT/StringSwitch.h"
2425
#include "llvm/ADT/UniqueVector.h"
2526
#include "llvm/Frontend/OpenMP/OMPContext.h"
2627

@@ -115,7 +116,9 @@ static OpenMPDirectiveKindExWrapper parseOpenMPDirectiveKind(Parser &P) {
115116
// TODO: add other combined directives in topological order.
116117
static const OpenMPDirectiveKindExWrapper F[][3] = {
117118
{OMPD_begin, OMPD_declare, OMPD_begin_declare},
119+
{OMPD_begin, OMPD_assumes, OMPD_begin_assumes},
118120
{OMPD_end, OMPD_declare, OMPD_end_declare},
121+
{OMPD_end, OMPD_assumes, OMPD_end_assumes},
119122
{OMPD_cancellation, OMPD_point, OMPD_cancellation_point},
120123
{OMPD_declare, OMPD_reduction, OMPD_declare_reduction},
121124
{OMPD_declare, OMPD_mapper, OMPD_declare_mapper},
@@ -1508,6 +1511,103 @@ bool Parser::parseOMPDeclareVariantMatchClause(SourceLocation Loc,
15081511
return false;
15091512
}
15101513

1514+
/// `omp assumes` or `omp begin/end assumes` <clause> [[,]<clause>]...
1515+
/// where
1516+
///
1517+
/// clause:
1518+
/// 'ext_IMPL_DEFINED'
1519+
/// 'absent' '(' directive-name [, directive-name]* ')'
1520+
/// 'contains' '(' directive-name [, directive-name]* ')'
1521+
/// 'holds' '(' scalar-expression ')'
1522+
/// 'no_openmp'
1523+
/// 'no_openmp_routines'
1524+
/// 'no_parallelism'
1525+
///
1526+
void Parser::ParseOpenMPAssumesDirective(OpenMPDirectiveKind DKind,
1527+
SourceLocation Loc) {
1528+
SmallVector<StringRef, 4> Assumptions;
1529+
bool SkippedClauses = false;
1530+
1531+
auto SkipBraces = [&](llvm::StringRef Spelling, bool IssueNote) {
1532+
BalancedDelimiterTracker T(*this, tok::l_paren,
1533+
tok::annot_pragma_openmp_end);
1534+
if (T.expectAndConsume(diag::err_expected_lparen_after, Spelling.data()))
1535+
return;
1536+
T.skipToEnd();
1537+
if (IssueNote && T.getCloseLocation().isValid())
1538+
Diag(T.getCloseLocation(),
1539+
diag::note_omp_assumption_clause_continue_here);
1540+
};
1541+
1542+
/// Helper to determine which AssumptionClauseMapping (ACM) in the
1543+
/// AssumptionClauseMappings table matches \p RawString. The return value is
1544+
/// the index of the matching ACM into the table or -1 if there was no match.
1545+
auto MatchACMClause = [&](StringRef RawString) {
1546+
llvm::StringSwitch<int> SS(RawString);
1547+
unsigned ACMIdx = 0;
1548+
for (const AssumptionClauseMappingInfo &ACMI : AssumptionClauseMappings) {
1549+
if (ACMI.StartsWith)
1550+
SS.StartsWith(ACMI.Identifier, ACMIdx++);
1551+
else
1552+
SS.Case(ACMI.Identifier, ACMIdx++);
1553+
}
1554+
return SS.Default(-1);
1555+
};
1556+
1557+
while (Tok.isNot(tok::annot_pragma_openmp_end)) {
1558+
IdentifierInfo *II = nullptr;
1559+
SourceLocation StartLoc = Tok.getLocation();
1560+
int Idx = -1;
1561+
if (Tok.isAnyIdentifier()) {
1562+
II = Tok.getIdentifierInfo();
1563+
Idx = MatchACMClause(II->getName());
1564+
}
1565+
ConsumeAnyToken();
1566+
1567+
bool NextIsLPar = Tok.is(tok::l_paren);
1568+
// Handle unknown clauses by skipping them.
1569+
if (Idx == -1) {
1570+
Diag(StartLoc, diag::warn_omp_unknown_assumption_clause_missing_id)
1571+
<< llvm::omp::getOpenMPDirectiveName(DKind)
1572+
<< llvm::omp::getAllAssumeClauseOptions() << NextIsLPar;
1573+
if (NextIsLPar)
1574+
SkipBraces(II ? II->getName() : "", /* IssueNote */ true);
1575+
SkippedClauses = true;
1576+
continue;
1577+
}
1578+
const AssumptionClauseMappingInfo &ACMI = AssumptionClauseMappings[Idx];
1579+
if (ACMI.HasDirectiveList || ACMI.HasExpression) {
1580+
// TODO: We ignore absent, contains, and holds assumptions for now. We
1581+
// also do not verify the content in the parenthesis at all.
1582+
SkippedClauses = true;
1583+
SkipBraces(II->getName(), /* IssueNote */ false);
1584+
continue;
1585+
}
1586+
1587+
if (NextIsLPar) {
1588+
Diag(Tok.getLocation(),
1589+
diag::warn_omp_unknown_assumption_clause_without_args)
1590+
<< II;
1591+
SkipBraces(II->getName(), /* IssueNote */ true);
1592+
}
1593+
1594+
assert(II && "Expected an identifier clause!");
1595+
StringRef Assumption = II->getName();
1596+
if (ACMI.StartsWith)
1597+
Assumption = Assumption.substr(ACMI.Identifier.size());
1598+
Assumptions.push_back(Assumption);
1599+
}
1600+
1601+
Actions.ActOnOpenMPAssumesDirective(Loc, DKind, Assumptions, SkippedClauses);
1602+
}
1603+
1604+
void Parser::ParseOpenMPEndAssumesDirective(SourceLocation Loc) {
1605+
if (Actions.isInOpenMPAssumeScope())
1606+
Actions.ActOnOpenMPEndAssumesDirective();
1607+
else
1608+
Diag(Loc, diag::err_expected_begin_assumes);
1609+
}
1610+
15111611
/// Parsing of simple OpenMP clauses like 'default' or 'proc_bind'.
15121612
///
15131613
/// default-clause:
@@ -1716,6 +1816,14 @@ void Parser::ParseOMPEndDeclareTargetDirective(OpenMPDirectiveKind DKind,
17161816
/// annot_pragma_openmp 'requires' <clause> [[[,] <clause>] ... ]
17171817
/// annot_pragma_openmp_end
17181818
///
1819+
/// assumes directive:
1820+
/// annot_pragma_openmp 'assumes' <clause> [[[,] <clause>] ... ]
1821+
/// annot_pragma_openmp_end
1822+
/// or
1823+
/// annot_pragma_openmp 'begin assumes' <clause> [[[,] <clause>] ... ]
1824+
/// annot_pragma_openmp 'end assumes'
1825+
/// annot_pragma_openmp_end
1826+
///
17191827
Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
17201828
AccessSpecifier &AS, ParsedAttributesWithRange &Attrs, bool Delayed,
17211829
DeclSpec::TST TagType, Decl *Tag) {
@@ -1853,6 +1961,13 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
18531961
ConsumeAnnotationToken();
18541962
return Actions.ActOnOpenMPRequiresDirective(StartLoc, Clauses);
18551963
}
1964+
case OMPD_assumes:
1965+
case OMPD_begin_assumes:
1966+
ParseOpenMPAssumesDirective(DKind, ConsumeToken());
1967+
break;
1968+
case OMPD_end_assumes:
1969+
ParseOpenMPEndAssumesDirective(ConsumeToken());
1970+
break;
18561971
case OMPD_declare_reduction:
18571972
ConsumeToken();
18581973
if (DeclGroupPtrTy Res = ParseOpenMPDeclareReductionDirective(AS)) {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10842,6 +10842,9 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1084210842
}
1084310843
}
1084410844

10845+
if (LangOpts.OpenMP)
10846+
ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(NewFD);
10847+
1084510848
// Semantic checking for this function declaration (in isolation).
1084610849

1084710850
if (getLangOpts().CPlusPlus) {

clang/lib/Sema/SemaLambda.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,10 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
998998
if (getLangOpts().CUDA)
999999
CUDASetLambdaAttrs(Method);
10001000

1001+
// OpenMP lambdas might get assumumption attributes.
1002+
if (LangOpts.OpenMP)
1003+
ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(Method);
1004+
10011005
// Number the lambda for linkage purposes if necessary.
10021006
handleLambdaNumbering(Class, Method);
10031007

clang/lib/Sema/SemaOpenMP.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "llvm/ADT/IndexedMap.h"
3636
#include "llvm/ADT/PointerEmbeddedInt.h"
3737
#include "llvm/ADT/STLExtras.h"
38+
#include "llvm/ADT/StringExtras.h"
3839
#include "llvm/Frontend/OpenMP/OMPConstants.h"
3940
#include <set>
4041

@@ -3195,6 +3196,64 @@ Sema::ActOnOpenMPRequiresDirective(SourceLocation Loc,
31953196
return DeclGroupPtrTy::make(DeclGroupRef(D));
31963197
}
31973198

3199+
void Sema::ActOnOpenMPAssumesDirective(SourceLocation Loc,
3200+
OpenMPDirectiveKind DKind,
3201+
ArrayRef<StringRef> Assumptions,
3202+
bool SkippedClauses) {
3203+
if (!SkippedClauses && Assumptions.empty())
3204+
Diag(Loc, diag::err_omp_no_clause_for_directive)
3205+
<< llvm::omp::getAllAssumeClauseOptions()
3206+
<< llvm::omp::getOpenMPDirectiveName(DKind);
3207+
3208+
auto *AA = AssumptionAttr::Create(Context, llvm::join(Assumptions, ","), Loc);
3209+
if (DKind == llvm::omp::Directive::OMPD_begin_assumes) {
3210+
OMPAssumeScoped.push_back(AA);
3211+
return;
3212+
}
3213+
3214+
// Global assumes without assumption clauses are ignored.
3215+
if (Assumptions.empty())
3216+
return;
3217+
3218+
assert(DKind == llvm::omp::Directive::OMPD_assumes &&
3219+
"Unexpected omp assumption directive!");
3220+
OMPAssumeGlobal.push_back(AA);
3221+
3222+
// The OMPAssumeGlobal scope above will take care of new declarations but
3223+
// we also want to apply the assumption to existing ones, e.g., to
3224+
// declarations in included headers. To this end, we traverse all existing
3225+
// declaration contexts and annotate function declarations here.
3226+
SmallVector<DeclContext *, 8> DeclContexts;
3227+
auto *Ctx = CurContext;
3228+
while (Ctx->getLexicalParent())
3229+
Ctx = Ctx->getLexicalParent();
3230+
DeclContexts.push_back(Ctx);
3231+
while (!DeclContexts.empty()) {
3232+
DeclContext *DC = DeclContexts.pop_back_val();
3233+
for (auto *SubDC : DC->decls()) {
3234+
if (SubDC->isInvalidDecl())
3235+
continue;
3236+
if (auto *CTD = dyn_cast<ClassTemplateDecl>(SubDC)) {
3237+
DeclContexts.push_back(CTD->getTemplatedDecl());
3238+
for (auto *S : CTD->specializations())
3239+
DeclContexts.push_back(S);
3240+
continue;
3241+
}
3242+
if (auto *DC = dyn_cast<DeclContext>(SubDC))
3243+
DeclContexts.push_back(DC);
3244+
if (auto *F = dyn_cast<FunctionDecl>(SubDC)) {
3245+
F->addAttr(AA);
3246+
continue;
3247+
}
3248+
}
3249+
}
3250+
}
3251+
3252+
void Sema::ActOnOpenMPEndAssumesDirective() {
3253+
assert(isInOpenMPAssumeScope() && "Not in OpenMP assumes scope!");
3254+
OMPAssumeScoped.pop_back();
3255+
}
3256+
31983257
OMPRequiresDecl *Sema::CheckOMPRequiresDecl(SourceLocation Loc,
31993258
ArrayRef<OMPClause *> ClauseList) {
32003259
/// For target specific clauses, the requires directive cannot be
@@ -5936,6 +5995,27 @@ static void setPrototype(Sema &S, FunctionDecl *FD, FunctionDecl *FDWithProto,
59365995
FD->setParams(Params);
59375996
}
59385997

5998+
void Sema::ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(Decl *D) {
5999+
if (D->isInvalidDecl())
6000+
return;
6001+
FunctionDecl *FD = nullptr;
6002+
if (auto *UTemplDecl = dyn_cast<FunctionTemplateDecl>(D))
6003+
FD = UTemplDecl->getTemplatedDecl();
6004+
else
6005+
FD = cast<FunctionDecl>(D);
6006+
assert(FD && "Expected a function declaration!");
6007+
6008+
// If we are intantiating templates we do *not* apply scoped assumptions but
6009+
// only global ones. We apply scoped assumption to the template definition
6010+
// though.
6011+
if (!inTemplateInstantiation()) {
6012+
for (AssumptionAttr *AA : OMPAssumeScoped)
6013+
FD->addAttr(AA);
6014+
}
6015+
for (AssumptionAttr *AA : OMPAssumeGlobal)
6016+
FD->addAttr(AA);
6017+
}
6018+
59396019
Sema::OMPDeclareVariantScope::OMPDeclareVariantScope(OMPTraitInfo &TI)
59406020
: TI(&TI), NameSuffix(TI.getMangledName()) {}
59416021

0 commit comments

Comments
 (0)