https://github.com/jtb20 updated https://github.com/llvm/llvm-project/pull/159774
>From 1837237122980978081711645ae4b07f41291d37 Mon Sep 17 00:00:00 2001 From: Julian Brown <[email protected]> Date: Mon, 15 Sep 2025 05:25:55 -0500 Subject: [PATCH 1/4] [OpenMP] Taskgraph frontend support This is a version of the 'ompx taskgraph' support posted in PR66919, adapted to the official OpenMP 6.0 spelling of 'omp taskgraph', and with the 'ompx' extension parts removed. Co-authored-by: Adrian Munera <[email protected]> Co-authored-by: Jose M Monsalve Diaz <[email protected]> --- clang/bindings/python/clang/cindex.py | 3 + clang/include/clang-c/Index.h | 4 + clang/include/clang/AST/RecursiveASTVisitor.h | 3 + clang/include/clang/AST/StmtOpenMP.h | 49 ++++++++++++ clang/include/clang/Basic/StmtNodes.td | 1 + clang/include/clang/Sema/SemaOpenMP.h | 4 + .../include/clang/Serialization/ASTBitCodes.h | 1 + clang/lib/AST/StmtOpenMP.cpp | 15 ++++ clang/lib/AST/StmtPrinter.cpp | 5 ++ clang/lib/AST/StmtProfile.cpp | 5 ++ clang/lib/Basic/OpenMPKinds.cpp | 3 + clang/lib/CodeGen/CGOpenMPRuntime.cpp | 74 +++++++++++++++++++ clang/lib/CodeGen/CGOpenMPRuntime.h | 8 ++ clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp | 2 + clang/lib/CodeGen/CGStmt.cpp | 3 + clang/lib/CodeGen/CGStmtOpenMP.cpp | 6 ++ clang/lib/CodeGen/CodeGenFunction.h | 1 + clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/Sema/SemaOpenMP.cpp | 31 ++++++++ clang/lib/Sema/TreeTransform.h | 11 +++ clang/lib/Serialization/ASTReaderStmt.cpp | 10 +++ clang/lib/Serialization/ASTWriterStmt.cpp | 6 ++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + clang/tools/libclang/CIndex.cpp | 2 + clang/tools/libclang/CXCursor.cpp | 3 + .../include/llvm/Frontend/OpenMP/OMPKinds.def | 1 + 26 files changed, 253 insertions(+) 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/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<OMPClause *> 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<OMPExecutableDirective>; def OMPTaskyieldDirective : StmtNode<OMPExecutableDirective>; def OMPBarrierDirective : StmtNode<OMPExecutableDirective>; def OMPTaskwaitDirective : StmtNode<OMPExecutableDirective>; +def OMPTaskgraphDirective : StmtNode<OMPExecutableDirective>; def OMPTaskgroupDirective : StmtNode<OMPExecutableDirective>; def OMPFlushDirective : StmtNode<OMPExecutableDirective>; def OMPDepobjDirective : StmtNode<OMPExecutableDirective>; 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<OMPClause *> Clauses, + Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc); /// Called on well-formed '\#pragma omp taskwait'. StmtResult ActOnOpenMPTaskwaitDirective(ArrayRef<OMPClause *> 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<OMPTaskwaitDirective>(C, NumClauses); } +OMPTaskgraphDirective *OMPTaskgraphDirective::Create( + const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt) { + auto *Dir = createDirective<OMPTaskgraphDirective>( + C, Clauses, AssociatedStmt, /*NumChildren=*/1, StartLoc, EndLoc); + return Dir; +} + +OMPTaskgraphDirective *OMPTaskgraphDirective::CreateEmpty(const ASTContext &C, + unsigned NumClauses, + EmptyShell) { + return createEmptyDirective<OMPTaskgraphDirective>( + C, NumClauses, /*HasAssociatedStmt=*/true, /*NumChildren=*/1); +} + OMPTaskgroupDirective *OMPTaskgroupDirective::Create( const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, ArrayRef<OMPClause *> 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<CGOpenMPRegionInfo>(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<CapturedStmt>(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<llvm::Value *, 6> 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<const Attr *> Attrs) { case Stmt::OMPTaskwaitDirectiveClass: EmitOMPTaskwaitDirective(cast<OMPTaskwaitDirective>(*S)); break; + case Stmt::OMPTaskgraphDirectiveClass: + EmitOMPTaskgraphDirective(cast<OMPTaskgraphDirective>(*S)); + break; case Stmt::OMPTaskgroupDirectiveClass: EmitOMPTaskgroupDirective(cast<OMPTaskgroupDirective>(*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<SemaOpenMP::CapturedParamNameType> +getTaskgraphRegionParams(Sema &SemaRef) { + SmallVector<SemaOpenMP::CapturedParamNameType> Params{ + std::make_pair(StringRef(), QualType()) // __context with shared vars + }; + return Params; +} + static SmallVector<SemaOpenMP::CapturedParamNameType> 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<OMPClause *> Clauses, Clauses); } +StmtResult +SemaOpenMP::ActOnOpenMPTaskgraphDirective(ArrayRef<OMPClause *> Clauses, + Stmt *AStmt, SourceLocation StartLoc, + SourceLocation EndLoc) { + if (!AStmt) + return StmtError(); + + assert(isa<CapturedStmt>(AStmt) && "Captured statement expected"); + + return OMPTaskgraphDirective::Create(getASTContext(), StartLoc, EndLoc, + Clauses, AStmt); +} + StmtResult SemaOpenMP::ActOnOpenMPTaskgroupDirective(ArrayRef<OMPClause *> 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<Derived>::TransformOMPAssumeDirective(OMPAssumeDirective *D) { return Res; } +template <typename Derived> +StmtResult TreeTransform<Derived>::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 <typename Derived> StmtResult TreeTransform<Derived>::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/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, >From 0e8d4d58024b0e87c7849bc33e83ad041033322d Mon Sep 17 00:00:00 2001 From: Julian Brown <[email protected]> Date: Fri, 26 Sep 2025 07:09:42 -0500 Subject: [PATCH 2/4] [OpenMP] New Clang tests for 'taskgraph' directive This patch adds two simple tests for parsing/serialization/deserialization and codegen for the OpenMP 'taskgraph' directive (for the current record-and-replay based implementation). --- clang/test/OpenMP/taskgraph_ast_print.cpp | 31 ++++++++++++++ clang/test/OpenMP/taskgraph_codegen.cpp | 52 +++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 clang/test/OpenMP/taskgraph_ast_print.cpp create mode 100644 clang/test/OpenMP/taskgraph_codegen.cpp 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 >From 505a07e9349ba754c997c1a054a4a949d3b4c3df Mon Sep 17 00:00:00 2001 From: Julian Brown <[email protected]> Date: Mon, 15 Sep 2025 05:54:11 -0500 Subject: [PATCH 3/4] [OpenMP] New/derived taskgraph tests This patch adds new tests for 'omp taskgraph' functionality, but (unlike the patches posted in PR66919) leave the existing tests using the internal runtime API for record and replay as-is. I have changed the 'print_dot' tests to use FileCheck instead of their own internal checking, though. Co-authored-by: Adrian Munera <[email protected]> --- .../tasking/omp_record_replay_print_dot.cpp | 53 ++++++--------- openmp/runtime/test/tasking/omp_taskgraph.cpp | 35 ++++++++++ .../test/tasking/omp_taskgraph_deps.cpp | 52 +++++++++++++++ .../test/tasking/omp_taskgraph_multiTDGs.cpp | 66 +++++++++++++++++++ .../test/tasking/omp_taskgraph_print_dot.cpp | 58 ++++++++++++++++ .../test/tasking/omp_taskgraph_taskloop.cpp | 39 +++++++++++ 6 files changed, 271 insertions(+), 32 deletions(-) create mode 100644 openmp/runtime/test/tasking/omp_taskgraph.cpp create mode 100644 openmp/runtime/test/tasking/omp_taskgraph_deps.cpp create mode 100644 openmp/runtime/test/tasking/omp_taskgraph_multiTDGs.cpp create mode 100644 openmp/runtime/test/tasking/omp_taskgraph_print_dot.cpp create mode 100644 openmp/runtime/test/tasking/omp_taskgraph_taskloop.cpp 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 <iostream> -#include <fstream> -#include <sstream> +// RUN: cat tdg_0.dot | FileCheck %s +// RUN: rm -f tdg_0.dot + +#include <cstdlib> #include <cassert> // 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 <iostream> +#include <cassert> +#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 <iostream> +#include <cassert> +#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 <iostream> +#include <cassert> +#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 <cstdlib> +#include <cassert> + +// 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 <iostream> +#include <cassert> + +#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 >From 740d7b10b4a1194d6b4cac01fe24a3f49400352f Mon Sep 17 00:00:00 2001 From: Julian Brown <[email protected]> Date: Fri, 3 Oct 2025 03:52:04 -0500 Subject: [PATCH 4/4] [OpenMP] Mention partial taskgraph support in Clang release notes NFC. --- clang/docs/ReleaseNotes.rst | 1 + 1 file changed, 1 insertion(+) 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 ^^^^^^^^^^^^ _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
