Skip to content
Open
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1703aa6
Support for dispatch construct (Sema & Codegen) support. Support for …
Sep 20, 2024
5ae3ffe
Taking care of feedback comments from Alexey Bataev.
Dec 9, 2024
daf681c
Changed the comments for replaceWithNewTraitsOrDirectCall().
Dec 10, 2024
bc3c018
Removing an unnecessary bracket in if statement in ActOnOpenMPCall().
Dec 10, 2024
b83e0ba
Removing unnecessary brackets around if statement.
Dec 10, 2024
997fe7c
1) Claiming support for "dispatch" construct.
Dec 15, 2024
182d026
Spacing problems for claiming "dispatch construct".
Dec 15, 2024
dca846e
Adding support for dispatch construct in Release Notes.
Dec 16, 2024
26427bf
re-wording message in warn_omp_dispatch_clause_novariants_nocontext.
Dec 18, 2024
e379325
Changes in expected-warning message for "#pragma omp dispatch
Dec 18, 2024
bab22e6
Removing the word CodeGen from comments, which can lead to confusion.
Jan 2, 2025
00ac6cf
Moving the helper functions to CodeGen by using AnnotateAttr in Sema.
Feb 1, 2025
ff2f370
Changing the commit message from:
Feb 4, 2025
de5b7cc
All clang/test/OpenMP/dispatch_*.cpp tests pass with the changes in t…
Feb 4, 2025
5d5b152
Merge branch 'main' into dispatch_depend
Feb 5, 2025
817f6df
Handling:
Feb 22, 2025
984446d
Undoing changes to handle templates.
Mar 18, 2025
4cb7bdd
Merge branch 'main' into dispatch_depend
Mar 18, 2025
bd2e38a
Merge branch 'main' into dispatch_depend
Mar 18, 2025
c1a83cc
Separating virtual regions of taskwait directive and dispatch directive
Mar 25, 2025
07354f0
Merge branch 'main' into dispatch_depend
Mar 25, 2025
f204bc4
Merge branch 'main' into dispatch_depend
Mar 25, 2025
ce9bff0
Merge branch 'main' into dispatch_depend
Mar 25, 2025
bb176c8
Moving handling of dispatch depend into CodeGen.
Mar 29, 2025
d217bc5
Changes to be committed:
Mar 29, 2025
8fba558
-ast-dump is working with addition of extra virtual region. CodeGen is
Apr 3, 2025
5ec20c3
modified: clang/lib/Sema/SemaOpenMP.cpp
Apr 3, 2025
cd8fae5
Formatting problems.
Apr 3, 2025
f47769a
CodeGen works with this change.
Apr 4, 2025
503222e
All tests pass with this change.
Apr 4, 2025
de8b736
1) Changing the name of the function from
Apr 9, 2025
f1e91f0
Merge branch 'main' into dispatch_depend
Apr 9, 2025
b2571fa
Merge branch 'main' into dispatch_depend
Apr 9, 2025
b60c252
Added OMPLexicalScope before emitting if-else statement.
Apr 9, 2025
d23c523
Changing LexicalScope to OMPD_dispatch.
Apr 9, 2025
f186576
Removed unused statements.
Apr 10, 2025
fd150c2
Replacing getCapturedExprFromImplicitCastExpr() with
Apr 16, 2025
07d8827
Adding support for clauses novariants and nocontext occuring on the same
Apr 22, 2025
4206c6e
Rolling back changes to make template related changes in this patch.
Apr 24, 2025
6a35664
Minor changes based on review feedback.
May 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/OpenMPSupport.rst
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ implementation.
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| misc | dispatch construct and function variant argument adjustment | :part:`worked on` | D99537, D99679 |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| misc | dispatch construct | :part:`worked on` | |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| misc | assumes directives | :part:`worked on` | |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| misc | assume directive | :good:`done` | |
Expand Down
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ Python Binding Changes

OpenMP Support
--------------
- Added support for 'omp dispatch' directive.
- Added support 'no_openmp_constructs' assumption clause.
- Added support for 'self_maps' in map and requirement clause.
- Added support for 'omp stripe' directive.
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/OpenMPKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@ bool isOpenMPTaskLoopDirective(OpenMPDirectiveKind DKind);
/// parallel', otherwise - false.
bool isOpenMPParallelDirective(OpenMPDirectiveKind DKind);

/// Checks if the specified directive is a dispatch-kind directive.
/// \param DKind Specified directive.
/// \return true - the directive is a dispatch-like directive like 'omp
/// dispatch', otherwise - false.
bool isOpenMPDispatchDirective(OpenMPDirectiveKind DKind);

/// Checks if the specified directive is a target code offload directive.
/// \param DKind Specified directive.
/// \return true - the directive is a target code offload directive like
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/SemaOpenMP.h
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,10 @@ class SemaOpenMP : public SemaBase {
: OMPDeclareVariantScopes.back().TI;
}

void annotateAStmt(const ASTContext &Context, Stmt *StmtP,
SemaOpenMP *SemaPtr, ArrayRef<OMPClause *> &Clauses,
SourceLocation StartLoc);

/// The current `omp begin/end declare variant` scopes.
SmallVector<OMPDeclareVariantScope, 4> OMPDeclareVariantScopes;

Expand Down
10 changes: 9 additions & 1 deletion clang/lib/Basic/OpenMPKinds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,11 @@ bool clang::isOpenMPParallelDirective(OpenMPDirectiveKind DKind) {
llvm::is_contained(getLeafConstructs(DKind), OMPD_parallel);
}

bool clang::isOpenMPDispatchDirective(OpenMPDirectiveKind DKind) {
return DKind == OMPD_dispatch ||
llvm::is_contained(getLeafConstructs(DKind), OMPD_dispatch);
}

bool clang::isOpenMPTargetExecutionDirective(OpenMPDirectiveKind DKind) {
return DKind == OMPD_target ||
llvm::is_contained(getLeafConstructs(DKind), OMPD_target);
Expand Down Expand Up @@ -796,6 +801,10 @@ void clang::getOpenMPCaptureRegions(
CaptureRegions.push_back(OMPD_task);
CaptureRegions.push_back(OMPD_target);
break;
case OMPD_dispatch:
CaptureRegions.push_back(OMPD_task);
CaptureRegions.push_back(OMPD_dispatch);
break;
case OMPD_task:
case OMPD_target_enter_data:
case OMPD_target_exit_data:
Expand All @@ -819,7 +828,6 @@ void clang::getOpenMPCaptureRegions(
else
return true;
break;
case OMPD_dispatch:
case OMPD_distribute:
case OMPD_for:
case OMPD_ordered:
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
EmitOMPInteropDirective(cast<OMPInteropDirective>(*S));
break;
case Stmt::OMPDispatchDirectiveClass:
CGM.ErrorUnsupported(S, "OpenMP dispatch directive");
EmitOMPDispatchDirective(cast<OMPDispatchDirective>(*S));
break;
case Stmt::OMPScopeDirectiveClass:
EmitOMPScopeDirective(cast<OMPScopeDirective>(*S));
Expand Down
194 changes: 194 additions & 0 deletions clang/lib/CodeGen/CGStmtOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4529,6 +4529,191 @@ void CodeGenFunction::EmitOMPMasterDirective(const OMPMasterDirective &S) {
emitMaster(*this, S);
}

static Expr *replaceWithNewTraitsOrDirectCall(CapturedDecl *CDecl,
Expr *NewExpr) {
Expr *CurrentCallExpr = nullptr;
Stmt *CallExprStmt = CDecl->getBody();

if (BinaryOperator *BinaryCopyOpr = dyn_cast<BinaryOperator>(CallExprStmt)) {
CurrentCallExpr = BinaryCopyOpr->getRHS();
BinaryCopyOpr->setRHS(NewExpr);
} else {
CurrentCallExpr = dyn_cast<Expr>(CallExprStmt);
CDecl->setBody(NewExpr);
}

return CurrentCallExpr;
}

static Expr *transformCallInStmt(Stmt *StmtP, bool NoContext = false) {
Expr *CurrentExpr = nullptr;
if (auto *CptStmt = dyn_cast<CapturedStmt>(StmtP)) {
CapturedDecl *CDecl = CptStmt->getCapturedDecl();

CallExpr *NewCallExpr = nullptr;
for (const auto *attr : CDecl->attrs()) {
if (NoContext) {
if (const auto *annotateAttr =
llvm::dyn_cast<clang::AnnotateAttr>(attr);
annotateAttr && annotateAttr->getAnnotation() == "NoContextAttr") {
NewCallExpr = llvm::dyn_cast<CallExpr>(*annotateAttr->args_begin());
}
} else {
if (const auto *annotateAttr =
llvm::dyn_cast<clang::AnnotateAttr>(attr);
annotateAttr && annotateAttr->getAnnotation() == "NoVariantsAttr") {
NewCallExpr = llvm::dyn_cast<CallExpr>(*annotateAttr->args_begin());
}
}
}

CurrentExpr = replaceWithNewTraitsOrDirectCall(CDecl, NewCallExpr);
}
return CurrentExpr;
}

Comment on lines +4532 to +4574
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think these functions should not be needed, if something is required, it should be build in Sema and stored in AST. If you need to replace some AST values by some LVM IR values, use OpaqueValue nodes, which can be replaced in codegen by special RAIIs.

Copy link
Contributor Author

@SunilKuravinakop SunilKuravinakop May 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please clarify your comment based on my following points?

  1. There is a pre-computed call trait (pre-computed in Sema) stored in AnnotateAttr. I am using those AnnotateAttrs in transformCallInStmt. In replaceWithNewTraitsOrDirectCall I am trying to set the call based on BinaryExpr under the dispatch directive.
  2. If you want me to pre-compute and store it in Sema I will have to clone the CDecl and store it. Earlier, in the helper functions (in Sema), I created new AST attributes, but according to your suggestion now I will have to store it in AnnotateAttr.
  3. An OpaqueValueExpr node copies the pointer of the SourceExpr. This does not help in noting the alternative call trait.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with these transformations is that sometimes they do not work as expected and miss some cases. Especially it happens with C++, which has lots of idioms like classes (especially inheritance), templates, implicit conversions (functions), etc. All these tree traversal techniques tend to be not fully correct in many cases. That;s why the preferred way to build helper expressions is doing it in Sema, because Sema knows how to handle all these cases correctly, but codegen does not. That's why I'm asking about compatibility with C++, it has lots of corner cases, and it would be good somehow to handle them in this patch rather than fixing it later

// emitIfElse is used for the following conditions:
//
// NoVariants = 0 && NoContext = 1
// if (Condition_NoContext) {
// foo_variant2(); // Present in AnnotationAttr
// } else {
// foo_variant();
// }
//
// NoVariants = 1 && NoContext = 0
// if (Condition_NoVariants) {
// foo();
// } else {
// foo_variant();
// }
//
// NoVariants = 1 && NoContext = 1
// if (Condition_NoVariants) { // ==> label if.then.NoVariants
// foo();
// } else { // ==> label else.NoVariants
// if (Condition_NoContext) { // ==> label if.then.NoContext
// foo_variant2(); // Present in AnnotationAttr
// } else { // ==> label else
// foo_variant();
// }
// }
//
static void emitIfElse(CodeGenFunction *CGF, Stmt *AssociatedStmt,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rather doubt it will correctly with constructors/destructors

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have an example of constructors/destructors where it will not work? The codegen for the nocontext & novariant clauses occurring together is present in the lit test case. I have written the code according to truth table mentioned by Deepak:

novariants(0), nocontext(0): Allow selective variant substitution according to context match
novariants(0), nocontext(1): Allow selective variant substitution according to context match (NO MATCH
                             for selectors that require **dispatch** trait)
novariants(1), nocontext(0): Always call the base function (shuts off any variant substitution for the call)
novariants(1), nocontext(1): Always call the base function (shuts off any variant substitution for the call)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to add tests with classes, which evaluate to boolean, and create immediate class instance and see, that the destructors are called correctly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexey-bataev Both @SunilKuravinakop and I are unclear what test you're asking for here.

When you say "test with classes", do you mean that the called function (and its variant) should have a local declaration of a variable of class type? Such as:

void f_v()
{
   MyType2 v; // MyType2 constructor called on v
   ...
   // MyType2 destructor called on v

}

#pragma omp declare variant(f_v) match(construct={dispatch})
void f()
{
   MyType v; // MyType constructor called on v
   ...
   // MyType destructor called on v
}

int main()
{
   #pragma omp dispatch
   f();
}

But then, what do you mean by "which evaluate to boolean"?

Can you provide a quick sketch of what this test should be doing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I thought about the condition, which involves instantiation of the class, to check that the codegen handles correctly constructors/destructors calls (that's always a problem in C++). Is it possible to create a condition, that includes class, something like:

class AAA {
...
};
#pragma omp dispatch if(AAA()) <---- the constructor and then destructor is called here
...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification. So something like this?

#include <iostream>                                                                                                                                                                                                                                                          
int main()
{
    class IsPositive {
public:
    IsPositive(int x) : _x(x) {
        std::cout << "constructing IsPositive(" << x << ") object\n";
    }
    ~IsPositive() {
        std::cout << "destructing IsPositive(" << _x << ") object\n";
    }
    operator bool() const { return _x > 0; }
private:
        int _x;
    };

  // case 1
#pragma omp parallel if(IsPositive(10))
    {
        std::cout << "[1] in parallel\n";
    }

  // case 2
    auto cond1 = IsPositive(5);
#pragma omp parallel if(cond1)
    {
        std::cout << "[2] in parallel\n";
    }

  // case 3
#pragma omp parallel if(IsPositive(-10))
    {
        std::cout << "[3] in parallel\n";
    }

  // case 4
    auto cond2 = IsPositive(-5);
#pragma omp parallel if(cond2)
    {
        std::cout << "[4] in parallel\n";
    }

  

The output for the above with OMP_NUM_THREADS=2 is:

constructing IsPositive(10) object
[1] in parallel
[1] in parallel
destructing IsPositive(10) object
constructing IsPositive(5) object
[2] in parallel
[2] in parallel
constructing IsPositive(-10) object
[3] in parallel
destructing IsPositive(-10) object
constructing IsPositive(-5) object
[4] in parallel
destructing IsPositive(-5) object
destructing IsPositive(5) object

So, the destructor for the instantiated class object used in the if condition is called upon completing each parallel region. On the other hand, if I move the condition expression outside the directive (as for cases 2 and 4), then the destructor for the instantiated class is called at the end of the function.

Does this match the expected behavior?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, something like this but for dispatch. We need to handle classes construction/deconstruction correctly, I just want to be sure that we don't miss anything here and avoid bugs, that will require significant efforts to fix/rework later.

Expr *Condition_NoVariants, Expr *Condition_NoContext) {
llvm::BasicBlock *ThenBlock = CGF->createBasicBlock("if.then");
llvm::BasicBlock *ElseBlock = CGF->createBasicBlock("if.else");
llvm::BasicBlock *MergeBlock = CGF->createBasicBlock("if.end");
llvm::BasicBlock *ThenNoVariantsBlock = nullptr;
llvm::BasicBlock *ElseNoVariantsBlock = nullptr;
llvm::BasicBlock *ThenNoContextBlock = nullptr;
Expr *ElseCall = nullptr;

if (Condition_NoVariants && Condition_NoContext) {
ThenNoVariantsBlock = CGF->createBasicBlock("if.then.NoVariants");
ElseNoVariantsBlock = CGF->createBasicBlock("else.NoVariants");
ThenNoContextBlock = CGF->createBasicBlock("if.then.NoContext");

CGF->EmitBranchOnBoolExpr(Condition_NoVariants, ThenNoVariantsBlock,
ElseNoVariantsBlock, 0);

} else if (Condition_NoVariants)
CGF->EmitBranchOnBoolExpr(Condition_NoVariants, ThenBlock, ElseBlock, 0);
else
CGF->EmitBranchOnBoolExpr(Condition_NoContext, ThenBlock, ElseBlock, 0);

if (Condition_NoVariants && Condition_NoContext) {
// Emit the NoVariants (if then, for the NoVariants) block.
CGF->EmitBlock(ThenNoVariantsBlock);
Stmt *ThenStmt = AssociatedStmt;
ElseCall = transformCallInStmt(ThenStmt, false);
CGF->EmitStmt(ThenStmt);
CGF->Builder.CreateBr(MergeBlock);

CGF->EmitBlock(ElseNoVariantsBlock);
CGF->EmitBranchOnBoolExpr(Condition_NoVariants, ThenNoContextBlock,
ElseBlock, 0);
// Emit the NoContext (else if, for the NoContext) block.
CGF->EmitBlock(ThenNoContextBlock);
Stmt *ThenNoContextStmt = AssociatedStmt;
transformCallInStmt(ThenNoContextStmt, true);
CGF->EmitStmt(ThenNoContextStmt);
CGF->Builder.CreateBr(MergeBlock);

} else if (Condition_NoVariants) {
// Emit the NoVariants (then) block.
CGF->EmitBlock(ThenBlock);
Stmt *ThenStmt = AssociatedStmt;
ElseCall = transformCallInStmt(ThenStmt, false);
CGF->EmitStmt(ThenStmt);
CGF->Builder.CreateBr(MergeBlock);

} else if (Condition_NoContext) {
// Emit the NoContext (then) block.
CGF->EmitBlock(ThenBlock);
Stmt *ThenStmt = AssociatedStmt;
ElseCall = transformCallInStmt(ThenStmt, true);
CGF->EmitStmt(ThenStmt);
CGF->Builder.CreateBr(MergeBlock);
}

// Emit the else block.
CGF->EmitBlock(ElseBlock);
Stmt *ElseStmt = AssociatedStmt;
if (auto *CaptStmt = dyn_cast<CapturedStmt>(ElseStmt)) {
CapturedDecl *CDecl = CaptStmt->getCapturedDecl();
replaceWithNewTraitsOrDirectCall(CDecl, ElseCall);
}
CGF->EmitStmt(ElseStmt);
CGF->Builder.CreateBr(MergeBlock);

CGF->EmitBlock(MergeBlock);
}

void CodeGenFunction::EmitOMPDispatchDirective(const OMPDispatchDirective &S) {
ArrayRef<OMPClause *> Clauses = S.clauses();

Stmt *AssociatedStmt = const_cast<Stmt *>(S.getAssociatedStmt());
if (auto *AssocStmt = dyn_cast<CapturedStmt>(AssociatedStmt))
if (auto *InnerCapturedStmt =
dyn_cast<CapturedStmt>(AssocStmt->getCapturedStmt())) {
AssociatedStmt = InnerCapturedStmt;
}
CodeGenFunction::CGCapturedStmtInfo CapStmtInfo;
if (!CapturedStmtInfo)
CapturedStmtInfo = &CapStmtInfo;

Expr *NoVariantsCondition = nullptr;
Expr *NoContextCondition = nullptr;
if (!Clauses.empty()) {
if (S.hasClausesOfKind<OMPDependClause>())
EmitOMPDispatchToTaskwaitDirective(S);

if (S.hasClausesOfKind<OMPNovariantsClause>() ||
S.hasClausesOfKind<OMPNocontextClause>()) {
if (const OMPNovariantsClause *NoVariantsC =
OMPExecutableDirective::getSingleClause<OMPNovariantsClause>(
Clauses)) {
NoVariantsCondition = NoVariantsC->getCondition();
if (const OMPNocontextClause *NoContextC =
OMPExecutableDirective::getSingleClause<OMPNocontextClause>(
Clauses)) {
NoContextCondition = NoContextC->getCondition();
}
} else {
const OMPNocontextClause *NoContextC =
OMPExecutableDirective::getSingleClause<OMPNocontextClause>(
Clauses);
NoContextCondition = NoContextC->getCondition();
}

OMPLexicalScope Scope(*this, S, OMPD_dispatch);
emitIfElse(this, AssociatedStmt, NoVariantsCondition, NoContextCondition);
}
} else
EmitStmt(AssociatedStmt);
}

static void emitMasked(CodeGenFunction &CGF, const OMPExecutableDirective &S) {
auto &&CodeGen = [&S](CodeGenFunction &CGF, PrePostActionTy &Action) {
Action.Enter(CGF);
Expand Down Expand Up @@ -5549,6 +5734,15 @@ void CodeGenFunction::EmitOMPBarrierDirective(const OMPBarrierDirective &S) {
CGM.getOpenMPRuntime().emitBarrierCall(*this, S.getBeginLoc(), OMPD_barrier);
}

void CodeGenFunction::EmitOMPDispatchToTaskwaitDirective(
const OMPDispatchDirective &S) {
OMPTaskDataTy Data;
// Build list of dependences
buildDependences(S, Data);
Data.HasNowaitClause = S.hasClausesOfKind<OMPNowaitClause>();
CGM.getOpenMPRuntime().emitTaskwaitCall(*this, S.getBeginLoc(), Data);
}

void CodeGenFunction::EmitOMPTaskwaitDirective(const OMPTaskwaitDirective &S) {
OMPTaskDataTy Data;
// Build list of dependences
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3663,6 +3663,8 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitCXXForRangeStmt(const CXXForRangeStmt &S,
ArrayRef<const Attr *> Attrs = {});

void EmitOMPDispatchToTaskwaitDirective(const OMPDispatchDirective &S);

/// Controls insertion of cancellation exit blocks in worksharing constructs.
class OMPCancelStackRAII {
CodeGenFunction &CGF;
Expand Down Expand Up @@ -3855,6 +3857,7 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitOMPSectionDirective(const OMPSectionDirective &S);
void EmitOMPSingleDirective(const OMPSingleDirective &S);
void EmitOMPMasterDirective(const OMPMasterDirective &S);
void EmitOMPDispatchDirective(const OMPDispatchDirective &S);
void EmitOMPMaskedDirective(const OMPMaskedDirective &S);
void EmitOMPCriticalDirective(const OMPCriticalDirective &S);
void EmitOMPParallelForDirective(const OMPParallelForDirective &S);
Expand Down
Loading
Loading