diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 80140d2787608..074d280f3cdf8 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -1449,6 +1449,9 @@ def is_unexposed(self): # OpenMP fuse directive. OMP_FUSE_DIRECTIVE = 311 + # OpenMP taskgraph directive. + OMP_TASKGRAPH_DIRECTIVE = 312 + # OpenACC Compute Construct. OPEN_ACC_COMPUTE_DIRECTIVE = 320 diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index d2e5bd284d350..0121ca4a0313c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -610,6 +610,7 @@ OpenMP Support - Added support for ``defaultmap`` directive implicit-behavior ``private``. - Added parsing and semantic analysis support for ``groupprivate`` directive. - Added support for 'omp fuse' directive. +- Partial support for the 'omp taskgraph' directive. Improvements ^^^^^^^^^^^^ diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index f13d9c9307b40..6ef9f7b75b687 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -2166,6 +2166,10 @@ enum CXCursorKind { */ CXCursor_OMPFuseDirective = 311, + /** OpenMP taskgraph directive. + */ + CXCursor_OMPTaskgraphDirective = 312, + /** OpenACC Compute Construct. */ CXCursor_OpenACCComputeConstruct = 320, diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 7a2881f6124f3..bab76b781315e 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3236,6 +3236,9 @@ DEF_TRAVERSE_STMT(OMPBarrierDirective, DEF_TRAVERSE_STMT(OMPTaskwaitDirective, { TRY_TO(TraverseOMPExecutableDirective(S)); }) +DEF_TRAVERSE_STMT(OMPTaskgraphDirective, + { TRY_TO(TraverseOMPExecutableDirective(S)); }) + DEF_TRAVERSE_STMT(OMPTaskgroupDirective, { TRY_TO(TraverseOMPExecutableDirective(S)); }) diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h index bc6aeaa8d143c..be4d33c783800 100644 --- a/clang/include/clang/AST/StmtOpenMP.h +++ b/clang/include/clang/AST/StmtOpenMP.h @@ -2760,6 +2760,55 @@ class OMPTaskwaitDirective : public OMPExecutableDirective { } }; +/// This represents '#pragma omp taskgraph' directive. +/// Available with OpenMP 6.0. +/// +/// \code +/// #pragma omp taskgraph +/// \endcode +/// +class OMPTaskgraphDirective final : public OMPExecutableDirective { + friend class ASTStmtReader; + friend class OMPExecutableDirective; + /// Build directive with the given start and end location. + /// + /// \param StartLoc Starting location of the directive kind. + /// \param EndLoc Ending location of the directive. + /// + OMPTaskgraphDirective(SourceLocation StartLoc, SourceLocation EndLoc) + : OMPExecutableDirective(OMPTaskgraphDirectiveClass, + llvm::omp::OMPD_taskgraph, StartLoc, EndLoc) {} + + /// Build an empty directive. + /// + explicit OMPTaskgraphDirective() + : OMPExecutableDirective(OMPTaskgraphDirectiveClass, + llvm::omp::OMPD_taskgraph, SourceLocation(), + SourceLocation()) {} + +public: + /// Creates directive. + /// + /// \param C AST context. + /// \param StartLoc Starting location of the directive kind. + /// \param EndLoc Ending Location of the directive. + /// + static OMPTaskgraphDirective * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef Clauses, Stmt *AssociatedStmt); + + /// Creates an empty directive. + /// + /// \param C AST context. + /// + static OMPTaskgraphDirective *CreateEmpty(const ASTContext &C, + unsigned NumClauses, EmptyShell); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OMPTaskgraphDirectiveClass; + } +}; + /// This represents '#pragma omp taskgroup' directive. /// /// \code diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index bf3686bb372d5..3f9b8e63955ca 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -257,6 +257,7 @@ def OMPTaskDirective : StmtNode; def OMPTaskyieldDirective : StmtNode; def OMPBarrierDirective : StmtNode; def OMPTaskwaitDirective : StmtNode; +def OMPTaskgraphDirective : StmtNode; def OMPTaskgroupDirective : StmtNode; def OMPFlushDirective : StmtNode; def OMPDepobjDirective : StmtNode; diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index daf58b18a03cb..e8b23fb568fff 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -557,6 +557,10 @@ class SemaOpenMP : public SemaBase { /// Called on well-formed '\#pragma omp barrier'. StmtResult ActOnOpenMPBarrierDirective(SourceLocation StartLoc, SourceLocation EndLoc); + /// Called on well-formed '\#pragma omp taskgraph'. + StmtResult ActOnOpenMPTaskgraphDirective(ArrayRef Clauses, + Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc); /// Called on well-formed '\#pragma omp taskwait'. StmtResult ActOnOpenMPTaskwaitDirective(ArrayRef Clauses, SourceLocation StartLoc, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 5d09d5536e5ab..abfb2ac96d0e0 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1969,6 +1969,7 @@ enum StmtCode { STMT_OMP_ERROR_DIRECTIVE, STMT_OMP_BARRIER_DIRECTIVE, STMT_OMP_TASKWAIT_DIRECTIVE, + STMT_OMP_TASKGRAPH_DIRECTIVE, STMT_OMP_FLUSH_DIRECTIVE, STMT_OMP_DEPOBJ_DIRECTIVE, STMT_OMP_SCAN_DIRECTIVE, diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp index a5b0cd3786a28..41effd494524c 100644 --- a/clang/lib/AST/StmtOpenMP.cpp +++ b/clang/lib/AST/StmtOpenMP.cpp @@ -945,6 +945,21 @@ OMPTaskwaitDirective *OMPTaskwaitDirective::CreateEmpty(const ASTContext &C, return createEmptyDirective(C, NumClauses); } +OMPTaskgraphDirective *OMPTaskgraphDirective::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef Clauses, Stmt *AssociatedStmt) { + auto *Dir = createDirective( + C, Clauses, AssociatedStmt, /*NumChildren=*/1, StartLoc, EndLoc); + return Dir; +} + +OMPTaskgraphDirective *OMPTaskgraphDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, + EmptyShell) { + return createEmptyDirective( + C, NumClauses, /*HasAssociatedStmt=*/true, /*NumChildren=*/1); +} + OMPTaskgroupDirective *OMPTaskgroupDirective::Create( const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, ArrayRef Clauses, Stmt *AssociatedStmt, Expr *ReductionRef) { diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 586c3000f105c..301bd613aa464 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -899,6 +899,11 @@ void StmtPrinter::VisitOMPAssumeDirective(OMPAssumeDirective *Node) { PrintOMPExecutableDirective(Node); } +void StmtPrinter::VisitOMPTaskgraphDirective(OMPTaskgraphDirective *Node) { + Indent() << "#pragma omp taskgraph"; + PrintOMPExecutableDirective(Node); +} + void StmtPrinter::VisitOMPErrorDirective(OMPErrorDirective *Node) { Indent() << "#pragma omp error"; PrintOMPExecutableDirective(Node); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index f3b5478222488..69e2598fc6ba8 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1119,9 +1119,14 @@ void StmtProfiler::VisitOMPAssumeDirective(const OMPAssumeDirective *S) { VisitOMPExecutableDirective(S); } +void StmtProfiler::VisitOMPTaskgraphDirective(const OMPTaskgraphDirective *S) { + VisitOMPExecutableDirective(S); +} + void StmtProfiler::VisitOMPErrorDirective(const OMPErrorDirective *S) { VisitOMPExecutableDirective(S); } + void StmtProfiler::VisitOMPTaskgroupDirective(const OMPTaskgroupDirective *S) { VisitOMPExecutableDirective(S); if (const Expr *E = S->getReductionRef()) diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 64b2bff063340..3ad308c709541 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -881,6 +881,9 @@ void clang::getOpenMPCaptureRegions( case OMPD_taskloop: CaptureRegions.push_back(OMPD_taskloop); break; + case OMPD_taskgraph: + CaptureRegions.push_back(OMPD_taskgraph); + break; case OMPD_loop: // TODO: 'loop' may require different capture regions depending on the // bind clause or the parent directive when there is no bind clause. diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp index 8cda583313ca4..1883a2cc55dfe 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -58,6 +58,8 @@ class CGOpenMPRegionInfo : public CodeGenFunction::CGCapturedStmtInfo { ParallelOutlinedRegion, /// Region with outlined function for standalone 'task' directive. TaskOutlinedRegion, + /// Region with outlined function for standalone 'taskgraph' directive. + TaskgraphOutlinedRegion, /// Region for constructs that do not require function outlining, /// like 'for', 'sections', 'atomic' etc. directives. InlinedRegion, @@ -232,6 +234,26 @@ class CGOpenMPTaskOutlinedRegionInfo final : public CGOpenMPRegionInfo { const UntiedTaskActionTy &Action; }; +/// API for captured statement code generation in OpenMP taskgraphs. +class CGOpenMPTaskgraphRegionInfo final : public CGOpenMPRegionInfo { +public: + CGOpenMPTaskgraphRegionInfo(const CapturedStmt &CS, + const RegionCodeGenTy &CodeGen) + : CGOpenMPRegionInfo(CS, TaskgraphOutlinedRegion, CodeGen, + llvm::omp::OMPD_taskgraph, false) {} + + const VarDecl *getThreadIDVariable() const override { return 0; } + + /// Get the name of the capture helper. + StringRef getHelperName() const override { return "taskgraph.omp_outlined."; } + + static bool classof(const CGCapturedStmtInfo *Info) { + return CGOpenMPRegionInfo::classof(Info) && + cast(Info)->getRegionKind() == + TaskgraphOutlinedRegion; + } +}; + /// API for inlined captured statement code generation in OpenMP /// constructs. class CGOpenMPInlinedRegionInfo : public CGOpenMPRegionInfo { @@ -5958,6 +5980,48 @@ void CGOpenMPRuntime::emitTaskwaitCall(CodeGenFunction &CGF, SourceLocation Loc, Region->emitUntiedSwitch(CGF); } +void CGOpenMPRuntime::emitTaskgraphCall(CodeGenFunction &CGF, + SourceLocation Loc, + const OMPExecutableDirective &D) { + if (!CGF.HaveInsertPoint()) + return; + + // Building kmp_taskgraph_flags_t flags for kmpc_taskgraph. C.f., kmp.h + enum { + NowaitFlag = 0x1, // Not used yet. + ReRecordFlag = 0x2, + }; + + unsigned Flags = 0; + + CodeGenFunction OutlinedCGF(CGM, /*suppressNewContext=*/true); + + const auto *CS = cast(D.getAssociatedStmt()); + + auto BodyGen = [CS](CodeGenFunction &CGF, PrePostActionTy &) { + CGF.EmitStmt(CS->getCapturedStmt()); + }; + + LValue CapStruct = CGF.InitCapturedStruct(*CS); + CGOpenMPTaskgraphRegionInfo TaskgraphRegion(*CS, BodyGen); + CodeGenFunction::CGCapturedStmtRAII CapInfoRAII(OutlinedCGF, + &TaskgraphRegion); + llvm::Function *FnT = OutlinedCGF.GenerateCapturedStmtFunction(*CS); + + std::array Args{ + emitUpdateLocation(CGF, Loc), + getThreadID(CGF, Loc), + CGF.Builder.getInt32(Flags), + CGF.Builder.getInt32(D.getBeginLoc().getHashValue()), + CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(FnT, CGM.VoidPtrTy), + CGF.Builder.CreatePointerBitCastOrAddrSpaceCast( + CapStruct.getPointer(OutlinedCGF), CGM.VoidPtrTy)}; + + CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction( + CGM.getModule(), OMPRTL___kmpc_taskgraph), + Args); +} + void CGOpenMPRuntime::emitInlinedDirective(CodeGenFunction &CGF, OpenMPDirectiveKind InnerKind, const RegionCodeGenTy &CodeGen, @@ -6393,6 +6457,7 @@ const Expr *CGOpenMPRuntime::getNumTeamsExprForTargetDirective( case OMPD_taskyield: case OMPD_barrier: case OMPD_taskwait: + case OMPD_taskgraph: case OMPD_taskgroup: case OMPD_atomic: case OMPD_flush: @@ -9797,6 +9862,7 @@ getNestedDistributeDirective(ASTContext &Ctx, const OMPExecutableDirective &D) { case OMPD_taskyield: case OMPD_barrier: case OMPD_taskwait: + case OMPD_taskgraph: case OMPD_taskgroup: case OMPD_atomic: case OMPD_flush: @@ -10443,6 +10509,7 @@ void CGOpenMPRuntime::scanForTargetRegionsFunctions(const Stmt *S, case OMPD_taskyield: case OMPD_barrier: case OMPD_taskwait: + case OMPD_taskgraph: case OMPD_taskgroup: case OMPD_atomic: case OMPD_flush: @@ -11010,6 +11077,7 @@ void CGOpenMPRuntime::emitTargetDataStandAloneCall( case OMPD_taskyield: case OMPD_barrier: case OMPD_taskwait: + case OMPD_taskgraph: case OMPD_taskgroup: case OMPD_atomic: case OMPD_flush: @@ -12751,6 +12819,12 @@ void CGOpenMPSIMDRuntime::emitTaskwaitCall(CodeGenFunction &CGF, llvm_unreachable("Not supported in SIMD-only mode"); } +void CGOpenMPSIMDRuntime::emitTaskgraphCall(CodeGenFunction &CGF, + SourceLocation Loc, + const OMPExecutableDirective &D) { + llvm_unreachable("Not supported in SIMD-only mode"); +} + void CGOpenMPSIMDRuntime::emitCancellationPointCall( CodeGenFunction &CGF, SourceLocation Loc, OpenMPDirectiveKind CancelRegion) { diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.h b/clang/lib/CodeGen/CGOpenMPRuntime.h index ba76ba6b5f523..5f101773e049b 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.h +++ b/clang/lib/CodeGen/CGOpenMPRuntime.h @@ -1364,6 +1364,10 @@ class CGOpenMPRuntime { virtual void emitTaskwaitCall(CodeGenFunction &CGF, SourceLocation Loc, const OMPTaskDataTy &Data); + /// Emit code for 'taskgraph' directive. + virtual void emitTaskgraphCall(CodeGenFunction &CGF, SourceLocation Loc, + const OMPExecutableDirective &D); + /// Emit code for 'cancellation point' construct. /// \param CancelRegion Region kind for which the cancellation point must be /// emitted. @@ -2192,6 +2196,10 @@ class CGOpenMPSIMDRuntime final : public CGOpenMPRuntime { void emitTaskwaitCall(CodeGenFunction &CGF, SourceLocation Loc, const OMPTaskDataTy &Data) override; + /// Emit code for 'taskgraph' directive. + void emitTaskgraphCall(CodeGenFunction &CGF, SourceLocation Loc, + const OMPExecutableDirective &D) override; + /// Emit code for 'cancellation point' construct. /// \param CancelRegion Region kind for which the cancellation point must be /// emitted. diff --git a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp index 4272d8b1a1f51..52d5e4fcba927 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp @@ -572,6 +572,7 @@ static bool hasNestedSPMDDirective(ASTContext &Ctx, case OMPD_taskyield: case OMPD_barrier: case OMPD_taskwait: + case OMPD_taskgraph: case OMPD_taskgroup: case OMPD_atomic: case OMPD_flush: @@ -660,6 +661,7 @@ static bool supportsSPMDExecutionMode(ASTContext &Ctx, case OMPD_taskyield: case OMPD_barrier: case OMPD_taskwait: + case OMPD_taskgraph: case OMPD_taskgroup: case OMPD_atomic: case OMPD_flush: diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 92636f27fd4e5..6d1269cb4b6f6 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -285,6 +285,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef Attrs) { case Stmt::OMPTaskwaitDirectiveClass: EmitOMPTaskwaitDirective(cast(*S)); break; + case Stmt::OMPTaskgraphDirectiveClass: + EmitOMPTaskgraphDirective(cast(*S)); + break; case Stmt::OMPTaskgroupDirectiveClass: EmitOMPTaskgroupDirective(cast(*S)); break; diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index efc06a276267a..42973b5b9e6ae 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -1440,6 +1440,7 @@ void CodeGenFunction::EmitOMPReductionClauseInit( case OMPD_error: case OMPD_barrier: case OMPD_taskwait: + case OMPD_taskgraph: case OMPD_taskgroup: case OMPD_flush: case OMPD_depobj: @@ -5630,6 +5631,11 @@ void CodeGenFunction::EmitOMPTaskwaitDirective(const OMPTaskwaitDirective &S) { CGM.getOpenMPRuntime().emitTaskwaitCall(*this, S.getBeginLoc(), Data); } +void CodeGenFunction::EmitOMPTaskgraphDirective( + const OMPTaskgraphDirective &S) { + CGM.getOpenMPRuntime().emitTaskgraphCall(*this, S.getBeginLoc(), S); +} + static bool isSupportedByOpenMPIRBuilder(const OMPTaskgroupDirective &T) { return T.clauses().empty(); } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index f0565c1de04c4..f22f2b60352aa 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3880,6 +3880,7 @@ class CodeGenFunction : public CodeGenTypeCache { void EmitOMPErrorDirective(const OMPErrorDirective &S); void EmitOMPBarrierDirective(const OMPBarrierDirective &S); void EmitOMPTaskwaitDirective(const OMPTaskwaitDirective &S); + void EmitOMPTaskgraphDirective(const OMPTaskgraphDirective &S); void EmitOMPTaskgroupDirective(const OMPTaskgroupDirective &S); void EmitOMPFlushDirective(const OMPFlushDirective &S); void EmitOMPDepobjDirective(const OMPDepobjDirective &S); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index a0483c3027199..b9fd82b489347 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1512,6 +1512,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Stmt::OMPScopeDirectiveClass: case Stmt::OMPTaskDirectiveClass: case Stmt::OMPTaskgroupDirectiveClass: + case Stmt::OMPTaskgraphDirectiveClass: case Stmt::OMPTaskLoopDirectiveClass: case Stmt::OMPTaskLoopSimdDirectiveClass: case Stmt::OMPTaskwaitDirectiveClass: diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 0fa21e89b1236..2e1f5c0b9ab01 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -4468,6 +4468,14 @@ getUnknownRegionParams(Sema &SemaRef) { return Params; } +static SmallVector +getTaskgraphRegionParams(Sema &SemaRef) { + SmallVector Params{ + std::make_pair(StringRef(), QualType()) // __context with shared vars + }; + return Params; +} + static SmallVector getTaskloopRegionParams(Sema &SemaRef) { ASTContext &Context = SemaRef.getASTContext(); @@ -4541,6 +4549,10 @@ static void processCapturedRegions(Sema &SemaRef, OpenMPDirectiveKind DKind, // function directly. MarkAsInlined(SemaRef.getCurCapturedRegion()); break; + case OMPD_taskgraph: + SemaRef.ActOnCapturedRegionStart( + Loc, CurScope, CR_OpenMP, getTaskgraphRegionParams(SemaRef), Level); + break; case OMPD_target: SemaRef.ActOnCapturedRegionStart(Loc, CurScope, CR_OpenMP, getTargetRegionParams(SemaRef), Level); @@ -6502,6 +6514,12 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective( "No associated statement allowed for 'omp taskwait' directive"); Res = ActOnOpenMPTaskwaitDirective(ClausesWithImplicit, StartLoc, EndLoc); break; + case OMPD_taskgraph: + assert(AStmt && + "Associated statement required for 'omp taskgraph' directive"); + Res = ActOnOpenMPTaskgraphDirective(ClausesWithImplicit, AStmt, StartLoc, + EndLoc); + break; case OMPD_taskgroup: Res = ActOnOpenMPTaskgroupDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc); @@ -11339,6 +11357,19 @@ SemaOpenMP::ActOnOpenMPTaskwaitDirective(ArrayRef Clauses, Clauses); } +StmtResult +SemaOpenMP::ActOnOpenMPTaskgraphDirective(ArrayRef Clauses, + Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc) { + if (!AStmt) + return StmtError(); + + assert(isa(AStmt) && "Captured statement expected"); + + return OMPTaskgraphDirective::Create(getASTContext(), StartLoc, EndLoc, + Clauses, AStmt); +} + StmtResult SemaOpenMP::ActOnOpenMPTaskgroupDirective(ArrayRef Clauses, Stmt *AStmt, SourceLocation StartLoc, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 51b55b82f4208..04b718e9141ec 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9822,6 +9822,17 @@ TreeTransform::TransformOMPAssumeDirective(OMPAssumeDirective *D) { return Res; } +template +StmtResult TreeTransform::TransformOMPTaskgraphDirective( + OMPTaskgraphDirective *D) { + DeclarationNameInfo DirName; + getDerived().getSema().OpenMP().StartOpenMPDSABlock( + OMPD_taskgraph, DirName, nullptr, D->getBeginLoc()); + StmtResult Res = getDerived().TransformOMPExecutableDirective(D); + getDerived().getSema().OpenMP().EndOpenMPDSABlock(Res.get()); + return Res; +} + template StmtResult TreeTransform::TransformOMPErrorDirective(OMPErrorDirective *D) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index eef97a8588f0b..d9048f60c6b78 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -2587,6 +2587,11 @@ void ASTStmtReader::VisitOMPAssumeDirective(OMPAssumeDirective *D) { VisitOMPExecutableDirective(D); } +void ASTStmtReader::VisitOMPTaskgraphDirective(OMPTaskgraphDirective *D) { + VisitStmt(D); + VisitOMPExecutableDirective(D); +} + void ASTStmtReader::VisitOMPErrorDirective(OMPErrorDirective *D) { VisitStmt(D); // The NumClauses field was read in ReadStmtFromStream. @@ -3736,6 +3741,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { Context, Record[ASTStmtReader::NumStmtFields], Empty); break; + case STMT_OMP_TASKGRAPH_DIRECTIVE: + S = OMPTaskgraphDirective::CreateEmpty( + Context, Record[ASTStmtReader::NumStmtFields], Empty); + break; + case STMT_OMP_ERROR_DIRECTIVE: S = OMPErrorDirective::CreateEmpty( Context, Record[ASTStmtReader::NumStmtFields], Empty); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index acf345392aa1a..1e008db0df3f1 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -2671,6 +2671,12 @@ void ASTStmtWriter::VisitOMPAssumeDirective(OMPAssumeDirective *D) { Code = serialization::STMT_OMP_ASSUME_DIRECTIVE; } +void ASTStmtWriter::VisitOMPTaskgraphDirective(OMPTaskgraphDirective *D) { + VisitStmt(D); + VisitOMPExecutableDirective(D); + Code = serialization::STMT_OMP_TASKGRAPH_DIRECTIVE; +} + void ASTStmtWriter::VisitOMPErrorDirective(OMPErrorDirective *D) { VisitStmt(D); Record.push_back(D->getNumClauses()); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 4e472b7fc38b0..eae5c2a47ae17 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1767,6 +1767,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPTaskyieldDirectiveClass: case Stmt::OMPBarrierDirectiveClass: case Stmt::OMPTaskwaitDirectiveClass: + case Stmt::OMPTaskgraphDirectiveClass: case Stmt::OMPErrorDirectiveClass: case Stmt::OMPTaskgroupDirectiveClass: case Stmt::OMPFlushDirectiveClass: diff --git a/clang/test/OpenMP/taskgraph_ast_print.cpp b/clang/test/OpenMP/taskgraph_ast_print.cpp new file mode 100644 index 0000000000000..063f734558345 --- /dev/null +++ b/clang/test/OpenMP/taskgraph_ast_print.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -verify -fopenmp -ast-print %s | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -std=c++11 -include-pch %t -verify %s -ast-print | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +int main() { + int x = 0, y = 0; + +#pragma omp taskgraph +// CHECK: #pragma omp taskgraph + { +#pragma omp task depend(in: x) depend(out: y) +// CHECK: #pragma omp task depend(in : x) depend(out : y) + { + y = x; + } +#pragma omp task depend(inout: x, y) +// CHECK: #pragma omp task depend(inout : x,y) + { + x++; + y++; + } + } + + return 0; +} + +#endif diff --git a/clang/test/OpenMP/taskgraph_codegen.cpp b/clang/test/OpenMP/taskgraph_codegen.cpp new file mode 100644 index 0000000000000..1c5d6c73d8890 --- /dev/null +++ b/clang/test/OpenMP/taskgraph_codegen.cpp @@ -0,0 +1,52 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --functions "main" --replace-value-regex "[0-9][0-9][0-9]+" --prefix-filecheck-ir-name _ + +// FIXME: The hash used to identify taskgraph regions (the fourth argument of +// __kmpc_taskgraph) is unstable between the two compiler invocations below, +// and furthermore is a little hard to identify with update_cc_test_checks.py. +// The above works for now, but it's not ideal. + +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-unknown -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[X:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[Y:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_CAPTURED:%.*]] = alloca [[STRUCT_ANON:%.*]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1:[0-9]+]]) +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: store i32 0, ptr [[X]], align 4 +// CHECK-NEXT: store i32 0, ptr [[Y]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON]], ptr [[AGG_CAPTURED]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[X]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_ANON]], ptr [[AGG_CAPTURED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[Y]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @__kmpc_taskgraph(ptr @[[GLOB1]], i32 [[TMP0]], i32 0, i32 {{[0-9][0-9][0-9]+}}, ptr @taskgraph.omp_outlined., ptr [[AGG_CAPTURED]]) +// CHECK-NEXT: ret i32 0 +// +int main() { + int x = 0, y = 0; + +#pragma omp taskgraph + { +#pragma omp task depend(in: x) depend(out: y) + { + y = x; + } +#pragma omp task depend(inout: x, y) + { + x++; + y++; + } + } + + return 0; +} + +#endif diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index c39f337665a40..5eba318dc0e6c 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6325,6 +6325,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPTaskwaitDirective"); case CXCursor_OMPAssumeDirective: return cxstring::createRef("OMPAssumeDirective"); + case CXCursor_OMPTaskgraphDirective: + return cxstring::createRef("OMPTaskgraphDirective"); case CXCursor_OMPErrorDirective: return cxstring::createRef("OMPErrorDirective"); case CXCursor_OMPTaskgroupDirective: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 56f113c1dc309..fc1311cf5fbce 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -741,6 +741,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPTaskwaitDirectiveClass: K = CXCursor_OMPTaskwaitDirective; break; + case Stmt::OMPTaskgraphDirectiveClass: + K = CXCursor_OMPTaskgraphDirective; + break; case Stmt::OMPErrorDirectiveClass: K = CXCursor_OMPErrorDirective; break; diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def b/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def index 01ca8da759ef7..89e37735b7d37 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def +++ b/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def @@ -354,6 +354,7 @@ __OMP_RTL(__kmpc_omp_task_alloc, false, /* kmp_task_t */ VoidPtr, IdentPtr, Int32, Int32, SizeTy, SizeTy, TaskRoutineEntryPtr) __OMP_RTL(__kmpc_omp_task, false, Int32, IdentPtr, Int32, /* kmp_task_t */ VoidPtr) +__OMP_RTL(__kmpc_taskgraph, false, Void, IdentPtr, Int32, Int32, Int32, VoidPtr, VoidPtr) __OMP_RTL(__kmpc_end_taskgroup, false, Void, IdentPtr, Int32) __OMP_RTL(__kmpc_taskgroup, false, Void, IdentPtr, Int32) __OMP_RTL(__kmpc_omp_task_begin_if0, false, Void, IdentPtr, Int32, diff --git a/openmp/runtime/test/tasking/omp_record_replay_print_dot.cpp b/openmp/runtime/test/tasking/omp_record_replay_print_dot.cpp index 7f1f5ccd77d37..e3d2c017c21c7 100644 --- a/openmp/runtime/test/tasking/omp_record_replay_print_dot.cpp +++ b/openmp/runtime/test/tasking/omp_record_replay_print_dot.cpp @@ -1,8 +1,9 @@ // REQUIRES: omp_taskgraph_experimental // RUN: %libomp-cxx-compile-and-run -#include -#include -#include +// RUN: cat tdg_0.dot | FileCheck %s +// RUN: rm -f tdg_0.dot + +#include #include // Compiler-generated code (emulation) @@ -23,29 +24,14 @@ void func(int *num_exec) { (*num_exec)++; } -std::string tdg_string= "digraph TDG {\n" -" compound=true\n" -" subgraph cluster {\n" -" label=TDG_0\n" -" 0[style=bold]\n" -" 1[style=bold]\n" -" 2[style=bold]\n" -" 3[style=bold]\n" -" }\n" -" 0 -> 1 \n" -" 1 -> 2 \n" -" 1 -> 3 \n" -"}"; - int main() { int num_exec = 0; int x, y; - setenv("KMP_TDG_DOT","TRUE",1); - remove("tdg_0.dot"); + setenv("KMP_TDG_DOT", "TRUE", 1); - #pragma omp parallel - #pragma omp single +#pragma omp parallel +#pragma omp single { int gtid = __kmpc_global_thread_num(nullptr); int res = __kmpc_start_record_task(nullptr, gtid, /* kmp_tdg_flags */ 0, /* tdg_id */ 0); @@ -65,16 +51,19 @@ int main() { assert(num_exec == 4); - std::ifstream tdg_file("tdg_0.dot"); - assert(tdg_file.is_open()); - - std::stringstream tdg_file_stream; - tdg_file_stream << tdg_file.rdbuf(); - int equal = tdg_string.compare(tdg_file_stream.str()); - - assert(equal == 0); - - std::cout << "Passed" << std::endl; return 0; } -// CHECK: Passed + +// CHECK: digraph TDG { +// CHECK-NEXT: compound=true +// CHECK-NEXT: subgraph cluster { +// CHECK-NEXT: label=TDG_0 +// CHECK-NEXT: 0[style=bold] +// CHECK-NEXT: 1[style=bold] +// CHECK-NEXT: 2[style=bold] +// CHECK-NEXT: 3[style=bold] +// CHECK-NEXT: } +// CHECK-NEXT: 0 -> 1 +// CHECK-NEXT: 1 -> 2 +// CHECK-NEXT: 1 -> 3 +// CHECK-NEXT: } diff --git a/openmp/runtime/test/tasking/omp_taskgraph.cpp b/openmp/runtime/test/tasking/omp_taskgraph.cpp new file mode 100644 index 0000000000000..363a7da8c145a --- /dev/null +++ b/openmp/runtime/test/tasking/omp_taskgraph.cpp @@ -0,0 +1,35 @@ +// REQUIRES: omp_taskgraph_experimental +// RUN: %libomp-cxx-compile-and-run +#include +#include +#define NT 100 + +// Compiler-generated code (emulation) +typedef struct ident { + void *dummy; +} ident_t; + +void func(int *num_exec) { (*num_exec)++; } + +int main() { + int num_exec = 0; + int num_tasks = 0; + int x = 0; +#pragma omp parallel +#pragma omp single + for (int iter = 0; iter < NT; ++iter) { +#pragma omp taskgraph + { + num_tasks++; +#pragma omp task + func(&num_exec); + } + } + + assert(num_tasks == 1); + assert(num_exec == NT); + + std::cout << "Passed" << std::endl; + return 0; +} +// CHECK: Passed diff --git a/openmp/runtime/test/tasking/omp_taskgraph_deps.cpp b/openmp/runtime/test/tasking/omp_taskgraph_deps.cpp new file mode 100644 index 0000000000000..3341b019a5095 --- /dev/null +++ b/openmp/runtime/test/tasking/omp_taskgraph_deps.cpp @@ -0,0 +1,52 @@ +// REQUIRES: omp_taskgraph_experimental +// RUN: %libomp-cxx-compile-and-run +#include +#include +#define NT 100 +#define MULTIPLIER 100 +#define DECREMENT 5 + +int val; +// Compiler-generated code (emulation) +typedef struct ident { + void *dummy; +} ident_t; + +void sub() { +#pragma omp atomic + val -= DECREMENT; +} + +void add() { +#pragma omp atomic + val += DECREMENT; +} + +void mult() { + // no atomicity needed, can only be executed by 1 thread + // and no concurrency with other tasks possible + val *= MULTIPLIER; +} + +int main() { + val = 0; + int *x, *y; +#pragma omp parallel +#pragma omp single + for (int iter = 0; iter < NT; ++iter) { +#pragma omp taskgraph + { +#pragma omp task depend(out : y) + add(); +#pragma omp task depend(out : x) + sub(); +#pragma omp task depend(in : x, y) + mult(); + } + } + assert(val == 0); + + std::cout << "Passed" << std::endl; + return 0; +} +// CHECK: Passed diff --git a/openmp/runtime/test/tasking/omp_taskgraph_multiTDGs.cpp b/openmp/runtime/test/tasking/omp_taskgraph_multiTDGs.cpp new file mode 100644 index 0000000000000..98a4ee27d0d5b --- /dev/null +++ b/openmp/runtime/test/tasking/omp_taskgraph_multiTDGs.cpp @@ -0,0 +1,66 @@ +// REQUIRES: omp_taskgraph_experimental +// RUN: %libomp-cxx-compile-and-run +#include +#include +#define NT 20 +#define MULTIPLIER 100 +#define DECREMENT 5 + +// Compiler-generated code (emulation) +typedef struct ident { + void *dummy; +} ident_t; + +int val; + +void sub() { +#pragma omp atomic + val -= DECREMENT; +} + +void add() { +#pragma omp atomic + val += DECREMENT; +} + +void mult() { + // no atomicity needed, can only be executed by 1 thread + // and no concurrency with other tasks possible + val *= MULTIPLIER; +} + +int main() { + int num_tasks = 0; + int *x, *y; +#pragma omp parallel +#pragma omp single + for (int iter = 0; iter < NT; ++iter) { +#pragma omp taskgraph + { + num_tasks++; +#pragma omp task depend(out : y) + add(); +#pragma omp task depend(out : x) + sub(); +#pragma omp task depend(in : x, y) + mult(); + } +#pragma omp taskgraph + { + num_tasks++; +#pragma omp task depend(out : y) + add(); +#pragma omp task depend(out : x) + sub(); +#pragma omp task depend(in : x, y) + mult(); + } + } + + assert(num_tasks == 2); + assert(val == 0); + + std::cout << "Passed" << std::endl; + return 0; +} +// CHECK: Passed diff --git a/openmp/runtime/test/tasking/omp_taskgraph_print_dot.cpp b/openmp/runtime/test/tasking/omp_taskgraph_print_dot.cpp new file mode 100644 index 0000000000000..0dc81df32d93a --- /dev/null +++ b/openmp/runtime/test/tasking/omp_taskgraph_print_dot.cpp @@ -0,0 +1,58 @@ +// REQUIRES: omp_taskgraph_experimental +// RUN: %libomp-cxx-compile-and-run +// RUN: cat tdg_17353.dot | FileCheck %s +// RUN: rm -f tdg_17353.dot + +#include +#include + +// Compiler-generated code (emulation) +typedef struct ident { + void *dummy; +} ident_t; + +void func(int *num_exec) { +#pragma omp atomic + (*num_exec)++; +} + +int main() { + int num_exec = 0; + int x, y; + + setenv("KMP_TDG_DOT", "TRUE", 1); + +#pragma omp parallel +#pragma omp single + { +#pragma omp taskgraph + { +#pragma omp task depend(out : x) + func(&num_exec); +#pragma omp task depend(in : x) depend(out : y) + func(&num_exec); +#pragma omp task depend(in : y) + func(&num_exec); +#pragma omp task depend(in : y) + func(&num_exec); + } + } + + assert(num_exec == 4); + + return 0; +} + +// CHECK: digraph TDG { +// CHECK-NEXT: compound=true +// CHECK-NEXT: subgraph cluster { +// CHECK-NEXT: label=TDG_17353 +// CHECK-NEXT: 0[style=bold] +// CHECK-NEXT: 1[style=bold] +// CHECK-NEXT: 2[style=bold] +// CHECK-NEXT: 3[style=bold] +// CHECK-NEXT: } +// CHECK-NEXT: 0 -> 1 +// CHECK-NEXT: 1 -> 2 +// CHECK-NEXT: 1 -> 3 +// CHECK-NEXT: } diff --git a/openmp/runtime/test/tasking/omp_taskgraph_taskloop.cpp b/openmp/runtime/test/tasking/omp_taskgraph_taskloop.cpp new file mode 100644 index 0000000000000..bbea64a2e92af --- /dev/null +++ b/openmp/runtime/test/tasking/omp_taskgraph_taskloop.cpp @@ -0,0 +1,39 @@ +// REQUIRES: omp_taskgraph_experimental +// RUN: %libomp-cxx-compile-and-run +#include +#include + +#define NT 20 +#define N 128 * 128 + +typedef struct ident { + void *dummy; +} ident_t; + +int main() { + int num_tasks = 0; + + int array[N]; + for (int i = 0; i < N; ++i) + array[i] = 1; + + long sum = 0; +#pragma omp parallel +#pragma omp single + for (int iter = 0; iter < NT; ++iter) { +#pragma omp taskgraph + { + num_tasks++; +#pragma omp taskloop reduction(+ : sum) num_tasks(4096) + for (int i = 0; i < N; ++i) { + sum += array[i]; + } + } + } + assert(sum == N * NT); + assert(num_tasks == 1); + + std::cout << "Passed" << std::endl; + return 0; +} +// CHECK: Passed