Hi Thomas,

Thanks for the review. See the updated patch in attachment and my comments below.

On 07/07/2026 08:58, Thomas Schwinge wrote:
Hi Paul-Antoine!

On 2026-07-01T16:57:43+0200, Paul-Antoine Arras <[email protected]> wrote:
While working on a follow-up patch, I realised that I did not include
target-side versions of GOMP_distribute_static_worksharing_start et al.
The attached fixup patch does so and adds testcases.

ACK.

On 30/06/2026 14:56, Paul-Antoine Arras wrote:
--- gcc/common.opt
+++ gcc/common.opt
@@ -2503,6 +2503,13 @@ fomit-frame-pointer
   Common Var(flag_omit_frame_pointer) Optimization
   When possible do not generate stack frames.
+fopenmp-ompt
+Common Var(flag_openmp_ompt) EnabledBy(fopenmp-ompt-detailed)
+
+
+fopenmp-ompt-detailed
+Common Var(flag_openmp_ompt_detailed)
+
   fopenmp-target-simd-clone
   Common Alias(fopenmp-target-simd-clone=,any,none)

Some short "help text" for the new options, please.

Also, need documenting in 'gcc/doc/invoke.texi', in the "Option Summary"
as well as "OpenMP and OpenACC Options".  Probably don't need anything
additional in 'gcc/fortran/gfortran.texi'.

Added documentation as needed.

We have '-fopenmp', '-fopenmp-simd' defined in 'gcc/c-family/c.opt',
'gcc/fortran/lang.opt', as, from the user perspective, they only apply to
those respective front ends.  I did wonder whether we shouldn't handle
'-fopenmp-ompt', '-fopenmp-ompt-detailed' similarly?  But on the other
head, those are 'Common' infrastructure, in the sense that OpenMP/OMPT
functionality also applies if OpenMP is used "as an implementation
detail" in GCC, such as when 'Common' '-ftree-parallelize-loops'
synthesizes OpenMP constructs (and therefore implicitly enables OpenMP
support) to parallelize Algol 68 code, for example.  In that case, it's
then certainly valid to expect to see OpenMP/OMPT events reported, and
'-fopenmp-ompt', '-fopenmp-ompt-detailed' than likewise apply.
Therefore, it appears correct to have those 'Common' options.  (We shall
add a corresponding Algol 68 test case (or other non-C, C++, Fortran, of
course), once that's feasible to do.)

Indeed, my reasoning was that, contrary to '-fopenmp', '-fopenmp-ompt' has no effect on either front end and does not affect user-written code, only code generation.

--- gcc/omp-builtins.def
+++ gcc/omp-builtins.def
@@ -445,6 +445,11 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SECTIONS_END_NOWAIT,
                  BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
   DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_START, "GOMP_single_start",
                  BT_FN_BOOL, ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_START_WITH_END,
+                 "GOMP_single_start_with_end", BT_FN_BOOL,
+                 ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_END, "GOMP_single_end", BT_FN_VOID,
+                 ATTR_NOTHROW_LEAF_LIST)
   DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_COPY_START, "GOMP_single_copy_start",
                  BT_FN_PTR, ATTR_NOTHROW_LEAF_LIST)
   DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_COPY_END, "GOMP_single_copy_end",
@@ -503,9 +508,29 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ERROR, "GOMP_error",
   DEF_GOMP_BUILTIN (BUILT_IN_GOMP_HAS_MASKED_THREAD_NUM,
                  "GOMP_has_masked_thread_num", BT_FN_BOOL_INT,
                  ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_MASKED_END, "GOMP_masked_end", BT_FN_VOID,
+                 ATTR_NOTHROW_LEAF_LIST)
   DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING,
                  "GOMP_loop_static_worksharing", BT_FN_COMPLEX_INT,
                  ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_START,
+                 "GOMP_loop_static_worksharing_start", BT_FN_COMPLEX_INT,
+                 ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_END,
+                 "GOMP_loop_static_worksharing_end", BT_FN_VOID,
+                 ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_DISPATCH,
+                 "GOMP_loop_static_worksharing_dispatch", BT_FN_VOID,
+                 ATTR_NOTHROW_LEAF_LIST)
   DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING,
                  "GOMP_distribute_static_worksharing", BT_FN_COMPLEX_INT,
                  ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_START,
+                 "GOMP_distribute_static_worksharing_start", BT_FN_COMPLEX_INT,
+                 ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_END,
+                 "GOMP_distribute_static_worksharing_end", BT_FN_VOID,
+                 ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_DISPATCH,
+                 "GOMP_distribute_static_worksharing_dispatch", BT_FN_VOID,
+                 ATTR_NOTHROW_LEAF_LIST)

All these need prototypes in 'libgomp/libgomp_g.h', too.  (This is
especially important for my libgomp/C++ work.)  In 'libgomp/libgomp_g.h',
please sort them in the right place: in sections corresponding to their
libgomp implementation file, in order of definition therein.

Added prototypes and ensured proper sorting.

'GOMP_loop_static_worksharing_dispatch',
'GOMP_distribute_static_worksharing_dispatch' are really just a signal,
but don't transmit any further data into libgomp/OMPT?

Yes, that is the requirement.

--- gcc/omp-expand.cc
+++ gcc/omp-expand.cc

@@ -5201,10 +5201,14 @@ expand_omp_for_static_nochunk (struct omp_region 
*region,

@@ -5969,10 +6019,14 @@ expand_omp_for_static_chunk (struct omp_region *region,

I've not verified the positions of the individual 'GOMP_[...]' calls that
you're synthesizing.

Let me know if anything needs more in-depth review.

--- gcc/omp-low.cc
+++ gcc/omp-low.cc

@@ -8786,7 +8786,9 @@ lower_omp_single_simple (gomp_single *single_stmt, 
gimple_seq *pre_p)

@@ -8923,6 +8925,15 @@ lower_omp_single (gimple_stmt_iterator *gsi_p, 
omp_context *ctx)

@@ -9099,6 +9110,14 @@ lower_omp_master (gimple_stmt_iterator *gsi_p, 
omp_context *ctx)

Likewise.

--- gcc/opts.cc
+++ gcc/opts.cc
@@ -1538,6 +1538,12 @@ finish_options (struct gcc_options *opts, struct 
gcc_options *opts_set,
                    " %<-fstrict-flex-arrays%> is not present");
         }
+ if ((opts->x_flag_openmp_ompt || opts->x_flag_openmp_ompt_detailed)
+      && !opts->x_flag_openmp)
+    error_at (
+      loc,
+      "%<-fopenmp-ompt%> and %<-fopenmp-ompt-detailed%> require %<-fopenmp%>");
+
     diagnose_options (opts, opts_set, loc);
   }

Looks like the appropriate place for these diagnostics.

--- libgomp/loop.c
+++ libgomp/loop.c
@@ -1186,3 +1186,53 @@ GOMP_loop_ordered_guided_next (long *istart, long *iend)
     return gomp_loop_ordered_guided_next (istart, iend);
   }
   #endif
+
+/* For a worksharing-loop construct with static schedule, return the thread ID
+   and number of threads packed into a single complex value.  */
+
+_Complex int
+GOMP_loop_static_worksharing (void)
+{
+  struct gomp_team *team = gomp_thread ()->ts.team;
+  unsigned tid = gomp_thread ()->ts.team_id;
+  unsigned nthreads = team ? team->nthreads : 1;
+  return nthreads + tid * 1I;
+}
+
+/* OMPT variant enabled by -fopenmp-ompt.  */
+
+_Complex int
+GOMP_loop_static_worksharing_start (void)
+{
+  struct gomp_team *team = gomp_thread ()->ts.team;
+  unsigned tid = gomp_thread ()->ts.team_id;
+  unsigned nthreads = team ? team->nthreads : 1;
+  return nthreads + tid * 1I;
+}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt-detailed.  */
+
+void
+GOMP_loop_static_worksharing_dispatch (void)
+{}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt.  */
+
+void
+GOMP_loop_static_worksharing_end (void)
+{}
+
+/* Return true if the current thread number equals TID.
+   Used to implement the masked construct's filter clause.  */
+
+bool
+GOMP_has_masked_thread_num (int tid)
+{
+  return tid == gomp_thread ()->ts.team_id;
+}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt.  */
+
+void
+GOMP_masked_end (void)
+{}

Please update 'libgomp/libgomp_g.h' also for the functions you're moving
into 'libgomp/loop.c' from:

--- libgomp/parallel.c
+++ libgomp/parallel.c
@@ -271,27 +271,6 @@ GOMP_cancel (int which, bool do_cancel)
     return true;
   }
-/* For a worksharing-loop construct with static schedule, return the thread ID
-   and number of threads packed into a single complex value.  */
-
-_Complex int
-GOMP_loop_static_worksharing (void)
-{
-  struct gomp_team *team = gomp_thread ()->ts.team;
-  unsigned tid = gomp_thread ()->ts.team_id;
-  unsigned nthreads = team ? team->nthreads : 1;
-  return nthreads + tid * 1I;
-}
-
-/* Return true if the current thread number equals TID.
-   Used to implement the masked construct's filter clause.  */
-
-bool
-GOMP_has_masked_thread_num (int tid)
-{
-  return tid == gomp_thread ()->ts.team_id;
-}

... here.

Done.

(I'm not disagreeing -- but was there any specific reason for moving
them?)

The initial reason to put GOMP_loop_static_worksharing in parallel.c was that its implementation was based on omp_get_num_threads and omp_get_thread_num which lived there. However, it is not used in parallel constructs, just in loops. It was probably a mistake though to move GOMP_has_masked_thread_num along. It is now reverted.

-

Keep the page break, I guess?

   /* The public OpenMP API for thread and team related inquiries.  */
int

Restored form feed.

Best,
--
PA
commit 52792ed16b33dead397ecfb2f06805d91f87c6cd
Author: Paul-Antoine Arras <[email protected]>
Date:   Tue Jun 30 11:06:44 2026 +0200

    openmp: Add OMPT entry points for worksharing-loop, single, and masked constructs
    
    Add two new compiler flags, -fopenmp-ompt and -fopenmp-ompt-detailed, that
    cause the compiler to emit OMPT-aware runtime entry points around OpenMP
    worksharing constructs.  With -fopenmp-ompt, the _start and _end variants
    are called instead of the plain worksharing functions; with
    -fopenmp-ompt-detailed, a _dispatch stub is additionally called after the
    chunk bounds are computed.  New stubs are added to libgomp as placeholders
    for future OMPT callback invocations.
    
    gcc/ChangeLog:
    
            * common.opt (fopenmp-ompt): New option.
            (fopenmp-ompt-detailed): New option.
            * doc/invoke.texi (Option Summary): Add -fopenmp-ompt and
            -fopenmp-ompt-detailed.
            (OpenMP and OpenACC Options): Document -fopenmp-ompt and
            -fopenmp-ompt-detailed.
            * omp-builtins.def (BUILT_IN_GOMP_SINGLE_START_WITH_END): New builtin.
            (BUILT_IN_GOMP_SINGLE_END): New builtin.
            (BUILT_IN_GOMP_MASKED_END): New builtin.
            (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_START): New builtin.
            (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_END): New builtin.
            (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_DISPATCH): New builtin.
            (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_START): New builtin.
            (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_END): New builtin.
            (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_DISPATCH): New builtin.
            * omp-expand.cc (expand_omp_for_static_nochunk): With -fopenmp-ompt,
            emit _start and _end variants instead of the plain worksharing call;
            with -fopenmp-ompt-detailed, also emit _dispatch.
            (expand_omp_for_static_chunk): Likewise.
            * omp-low.cc (lower_omp_single_simple): With -fopenmp-ompt, use
            GOMP_single_start_with_end instead of GOMP_single_start.
            (lower_omp_single): With -fopenmp-ompt, emit GOMP_single_end.
            (lower_omp_master): With -fopenmp-ompt, emit GOMP_masked_end.
            * opts.cc (finish_options): Diagnose -fopenmp-ompt and
            -fopenmp-ompt-detailed used without -fopenmp.
    
    libgomp/ChangeLog:
    
            * config/gcn/teams.c (GOMP_distribute_static_worksharing_start,
            GOMP_distribute_static_worksharing_dispatch,
            GOMP_distribute_static_worksharing_end): New functions.
            * config/nvptx/teams.c (GOMP_distribute_static_worksharing_start,
            GOMP_distribute_static_worksharing_dispatch,
            GOMP_distribute_static_worksharing_end): Likewise.
            * libgomp.map (GOMP_6.0.2): Export new entry points.
            * libgomp_g.h: Add prototypes for GOMP_loop_static_worksharing,
            GOMP_loop_static_worksharing_start, GOMP_loop_static_worksharing_dispatch,
            GOMP_loop_static_worksharing_end, GOMP_has_masked_thread_num,
            GOMP_masked_end, GOMP_single_start_with_end, GOMP_single_end,
            GOMP_distribute_static_worksharing_start,
            GOMP_distribute_static_worksharing_dispatch, and
            GOMP_distribute_static_worksharing_end.
            * loop.c (GOMP_loop_static_worksharing): Move here from parallel.c.
            (GOMP_loop_static_worksharing_start): New function.
            (GOMP_loop_static_worksharing_dispatch): New stub.
            (GOMP_loop_static_worksharing_end): New stub.
            (GOMP_has_masked_thread_num): Move here from parallel.c.
            (GOMP_masked_end): New stub.
            * parallel.c (GOMP_loop_static_worksharing): Move to loop.c.
            (GOMP_has_masked_thread_num): Move to loop.c.
            * single.c (GOMP_single_start_with_end): New function.
            (GOMP_single_end): New stub.
            (GOMP_single_copy_start): Fix trailing whitespace.
            * teams.c (GOMP_distribute_static_worksharing_start): New function.
            (GOMP_distribute_static_worksharing_dispatch): New stub.
            (GOMP_distribute_static_worksharing_end): New stub.
            * testsuite/libgomp.c-c++-common/for-static-1.c: New test.
            * testsuite/libgomp.c-c++-common/for-static-2.c: New test.
            * testsuite/libgomp.c-c++-common/for-static-3.c: New test.
            * testsuite/libgomp.c-c++-common/for-static.h: New test.
    
    gcc/testsuite/ChangeLog:
    
            * c-c++-common/gomp/masked-1.c: Add scan-tree-dump-not check that
            GOMP_masked_end is not emitted without -fopenmp-ompt.
            * c-c++-common/gomp/for-8.c: Remove; superseded by for-static-*.c.
            * c-c++-common/gomp/for-static-1.c: New test; checks that the plain
            worksharing builtins are emitted without -fopenmp-ompt.
            * c-c++-common/gomp/for-static-2.c: New test; checks that _start and
            _end variants are emitted with -fopenmp-ompt.
            * c-c++-common/gomp/for-static-3.c: New test; checks that _dispatch is
            also emitted with -fopenmp-ompt-detailed.
            * c-c++-common/gomp/for-static.h: New shared test header.
            * c-c++-common/gomp/masked-3.c: New test for GOMP_masked_end emission
            with -fopenmp-ompt.
            * c-c++-common/gomp/openmp-ompt-1.c: New test; checks that
            -fopenmp-ompt without -fopenmp is diagnosed.
            * c-c++-common/gomp/openmp-ompt-2.c: New test; checks that
            -fopenmp-ompt-detailed without -fopenmp is diagnosed.
            * c-c++-common/gomp/single2.c: New test for GOMP_single_start_with_end
            and GOMP_single_end emission with -fopenmp-ompt.

diff --git gcc/common.opt gcc/common.opt
index 218dddf5dfe..4d1e1d0f928 100644
--- gcc/common.opt
+++ gcc/common.opt
@@ -2503,6 +2503,14 @@ fomit-frame-pointer
 Common Var(flag_omit_frame_pointer) Optimization
 When possible do not generate stack frames.
 
+fopenmp-ompt
+Common Var(flag_openmp_ompt) EnabledBy(fopenmp-ompt-detailed)
+Emit additional entry points for OMPT before and after OpenMP worksharing constructs.
+
+fopenmp-ompt-detailed
+Common Var(flag_openmp_ompt_detailed)
+Like -fopenmp-ompt, and also emit OMPT dispatch entry points.
+
 fopenmp-target-simd-clone
 Common Alias(fopenmp-target-simd-clone=,any,none)
 
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index 4fbd25e49dd..5cbc22e1e76 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -323,7 +323,8 @@ Objective-C and Objective-C++ Dialects}.
 @xref{OpenMP and OpenACC Options,,Options Controlling OpenMP and OpenACC}.
 @gccoptlist{-foffload=@var{arg}  -foffload-options=@var{arg}
 -fopenacc  -fopenacc-dim=@var{geom}
--fopenmp  -fopenmp-simd  -fopenmp-target-simd-clone@r{[}=@var{device-type}@r{]}}
+-fopenmp  -fopenmp-simd  -fopenmp-ompt  -fopenmp-ompt-detailed
+-fopenmp-target-simd-clone@r{[}=@var{device-type}@r{]}}
 
 @item Diagnostic Message Formatting Options
 @xref{Diagnostic Message Formatting Options,,Options to Control Diagnostic Messages Formatting}.
@@ -5600,6 +5601,20 @@ Fortran.  In fixed source form Fortran, the sentinels can also start with
 @option{-fopenmp} is additionally specified, the @code{loop} region binds
 to the current task region, independent of the specified @code{bind} clause.
 
+@opindex fopenmp-ompt
+@opindex fno-openmp-ompt
+@cindex OpenMP OMPT
+@item -fopenmp-ompt
+Emit OMPT entry points around OpenMP worksharing constructs, so that an
+OMPT tool can observe their start and end.  Requires @option{-fopenmp}.
+
+@opindex fopenmp-ompt-detailed
+@opindex fno-openmp-ompt-detailed
+@item -fopenmp-ompt-detailed
+Like @option{-fopenmp-ompt}, and additionally emit an OMPT dispatch
+entry point that gets called at the beginning of each workshare chunk.
+Requires @option{-fopenmp}.
+
 @opindex fopenmp-target-simd-clone
 @opindex fno-openmp-target-simd-clone
 @cindex OpenMP target SIMD clone
diff --git gcc/omp-builtins.def gcc/omp-builtins.def
index d7440a42761..465aeebefdb 100644
--- gcc/omp-builtins.def
+++ gcc/omp-builtins.def
@@ -445,6 +445,11 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SECTIONS_END_NOWAIT,
 		  BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_START, "GOMP_single_start",
 		  BT_FN_BOOL, ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_START_WITH_END,
+		  "GOMP_single_start_with_end", BT_FN_BOOL,
+		  ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_END, "GOMP_single_end", BT_FN_VOID,
+		  ATTR_NOTHROW_LEAF_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_COPY_START, "GOMP_single_copy_start",
 		  BT_FN_PTR, ATTR_NOTHROW_LEAF_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SINGLE_COPY_END, "GOMP_single_copy_end",
@@ -503,9 +508,29 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_ERROR, "GOMP_error",
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_HAS_MASKED_THREAD_NUM,
 		  "GOMP_has_masked_thread_num", BT_FN_BOOL_INT,
 		  ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_MASKED_END, "GOMP_masked_end", BT_FN_VOID,
+		  ATTR_NOTHROW_LEAF_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING,
 		  "GOMP_loop_static_worksharing", BT_FN_COMPLEX_INT,
 		  ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_START,
+		  "GOMP_loop_static_worksharing_start", BT_FN_COMPLEX_INT,
+		  ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_END,
+		  "GOMP_loop_static_worksharing_end", BT_FN_VOID,
+		  ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_DISPATCH,
+		  "GOMP_loop_static_worksharing_dispatch", BT_FN_VOID,
+		  ATTR_NOTHROW_LEAF_LIST)
 DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING,
 		  "GOMP_distribute_static_worksharing", BT_FN_COMPLEX_INT,
 		  ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_START,
+		  "GOMP_distribute_static_worksharing_start", BT_FN_COMPLEX_INT,
+		  ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_END,
+		  "GOMP_distribute_static_worksharing_end", BT_FN_VOID,
+		  ATTR_NOTHROW_LEAF_LIST)
+DEF_GOMP_BUILTIN (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_DISPATCH,
+		  "GOMP_distribute_static_worksharing_dispatch", BT_FN_VOID,
+		  ATTR_NOTHROW_LEAF_LIST)
diff --git gcc/omp-expand.cc gcc/omp-expand.cc
index 3d799d28032..ad380b27d06 100644
--- gcc/omp-expand.cc
+++ gcc/omp-expand.cc
@@ -5201,10 +5201,14 @@ expand_omp_for_static_nochunk (struct omp_region *region,
   switch (gimple_omp_for_kind (fd->for_stmt))
     {
     case GF_OMP_FOR_KIND_FOR:
-      decl = builtin_decl_explicit (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING);
+      decl = builtin_decl_explicit (
+	flag_openmp_ompt ? BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_START
+			 : BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING);
       break;
     case GF_OMP_FOR_KIND_DISTRIBUTE:
-      decl = builtin_decl_explicit (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING);
+      decl = builtin_decl_explicit (
+	flag_openmp_ompt ? BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_START
+			 : BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING);
       break;
     default:
       gcc_unreachable ();
@@ -5576,8 +5580,53 @@ expand_omp_for_static_nochunk (struct omp_region *region,
 						   cont_bb, body_bb);
     }
 
-  /* Replace the GIMPLE_OMP_RETURN with a barrier, or nothing.  */
+  if (flag_openmp_ompt_detailed)
+    {
+      /* Insert call to GOMP_*_static_worksharing_dispatch at the end of
+	 seq_start_bb.  */
+      gsi = gsi_last_nondebug_bb (seq_start_bb);
+      tree decl;
+      switch (gimple_omp_for_kind (fd->for_stmt))
+	{
+	case GF_OMP_FOR_KIND_FOR:
+	  decl = builtin_decl_explicit (
+	    BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_DISPATCH);
+	  break;
+	case GF_OMP_FOR_KIND_DISTRIBUTE:
+	  decl = builtin_decl_explicit (
+	    BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_DISPATCH);
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      gcall *g = gimple_build_call (decl, 0);
+      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+    }
+
   gsi = gsi_last_nondebug_bb (exit_bb);
+  if (flag_openmp_ompt)
+    {
+      /* Insert call to GOMP_*_static_worksharing_end at the end of exit_bb.
+       */
+      tree decl;
+      switch (gimple_omp_for_kind (fd->for_stmt))
+	{
+	case GF_OMP_FOR_KIND_FOR:
+	  decl
+	    = builtin_decl_explicit (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_END);
+	  break;
+	case GF_OMP_FOR_KIND_DISTRIBUTE:
+	  decl = builtin_decl_explicit (
+	    BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_END);
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      gcall *g = gimple_build_call (decl, 0);
+      gsi_insert_after (&gsi, g, GSI_SAME_STMT);
+    }
+
+  /* Replace the GIMPLE_OMP_RETURN with a barrier, or nothing.  */
   if (!gimple_omp_return_nowait_p (gsi_stmt (gsi)))
     {
       t = gimple_omp_return_lhs (gsi_stmt (gsi));
@@ -5646,6 +5695,7 @@ expand_omp_for_static_nochunk (struct omp_region *region,
       exit3_bb = split_block (exit2_bb, g)->dest;
       gsi = gsi_after_labels (exit3_bb);
     }
+
   gsi_remove (&gsi, true);
 
   /* Connect all the blocks.  */
@@ -5969,10 +6019,14 @@ expand_omp_for_static_chunk (struct omp_region *region,
   switch (gimple_omp_for_kind (fd->for_stmt))
     {
     case GF_OMP_FOR_KIND_FOR:
-      decl = builtin_decl_explicit (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING);
+      decl = builtin_decl_explicit (
+	flag_openmp_ompt ? BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_START
+			 : BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING);
       break;
     case GF_OMP_FOR_KIND_DISTRIBUTE:
-      decl = builtin_decl_explicit (BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING);
+      decl = builtin_decl_explicit (
+	flag_openmp_ompt ? BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_START
+			 : BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING);
       break;
     default:
       gcc_unreachable ();
@@ -6303,8 +6357,30 @@ expand_omp_for_static_chunk (struct omp_region *region,
       gsi_insert_after (&gsi, assign_stmt, GSI_CONTINUE_LINKING);
     }
 
-  /* Replace the GIMPLE_OMP_RETURN with a barrier, or nothing.  */
   gsi = gsi_last_nondebug_bb (exit_bb);
+  if (flag_openmp_ompt)
+    {
+      /* Insert call to GOMP_*_static_worksharing_end at the end of exit_bb.
+       */
+      tree decl;
+      switch (gimple_omp_for_kind (fd->for_stmt))
+	{
+	case GF_OMP_FOR_KIND_FOR:
+	  decl
+	    = builtin_decl_explicit (BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_END);
+	  break;
+	case GF_OMP_FOR_KIND_DISTRIBUTE:
+	  decl = builtin_decl_explicit (
+	    BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_END);
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      gcall *g = gimple_build_call (decl, 0);
+      gsi_insert_after (&gsi, g, GSI_SAME_STMT);
+    }
+
+  /* Replace the GIMPLE_OMP_RETURN with a barrier, or nothing.  */
   if (!gimple_omp_return_nowait_p (gsi_stmt (gsi)))
     {
       t = gimple_omp_return_lhs (gsi_stmt (gsi));
@@ -6340,6 +6416,29 @@ expand_omp_for_static_chunk (struct omp_region *region,
     }
   gsi_remove (&gsi, true);
 
+  if (flag_openmp_ompt_detailed)
+    {
+      /* Insert call to GOMP_*_static_worksharing_dispatch at the end of
+	 seq_start_bb.  */
+      gsi = gsi_last_nondebug_bb (seq_start_bb);
+      tree decl;
+      switch (gimple_omp_for_kind (fd->for_stmt))
+	{
+	case GF_OMP_FOR_KIND_FOR:
+	  decl = builtin_decl_explicit (
+	    BUILT_IN_GOMP_LOOP_STATIC_WORKSHARING_DISPATCH);
+	  break;
+	case GF_OMP_FOR_KIND_DISTRIBUTE:
+	  decl = builtin_decl_explicit (
+	    BUILT_IN_GOMP_DISTRIBUTE_STATIC_WORKSHARING_DISPATCH);
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      gcall *g = gimple_build_call (decl, 0);
+      gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+    }
+
   /* Connect the new blocks.  */
   find_edge (iter_part_bb, seq_start_bb)->flags = EDGE_TRUE_VALUE;
   find_edge (iter_part_bb, fin_bb)->flags = EDGE_FALSE_VALUE;
diff --git gcc/omp-low.cc gcc/omp-low.cc
index dbdbc4079f4..50da8581a8d 100644
--- gcc/omp-low.cc
+++ gcc/omp-low.cc
@@ -8786,7 +8786,9 @@ lower_omp_single_simple (gomp_single *single_stmt, gimple_seq *pre_p)
   gimple *call, *cond;
   tree lhs, decl;
 
-  decl = builtin_decl_explicit (BUILT_IN_GOMP_SINGLE_START);
+  decl = builtin_decl_explicit (flag_openmp_ompt
+				  ? BUILT_IN_GOMP_SINGLE_START_WITH_END
+				  : BUILT_IN_GOMP_SINGLE_START);
   lhs = create_tmp_var (TREE_TYPE (TREE_TYPE (decl)));
   call = gimple_build_call (decl, 0);
   gimple_call_set_lhs (call, lhs);
@@ -8923,6 +8925,15 @@ lower_omp_single (gimple_stmt_iterator *gsi_p, omp_context *ctx)
   gimple *g = gimple_build_omp_return (nowait);
   gimple_seq_add_stmt (&bind_body_tail, g);
   maybe_add_implicit_barrier_cancel (ctx, g, &bind_body_tail);
+
+  if (flag_openmp_ompt && !ctx->record_type)
+    {
+      /* Insert call to GOMP_single_end.  */
+      tree decl = builtin_decl_explicit (BUILT_IN_GOMP_SINGLE_END);
+      gimple *stmt = gimple_build_call (decl, 0);
+      gimple_seq_add_stmt (&bind_body_tail, stmt);
+    }
+
   if (ctx->record_type)
     {
       gimple_stmt_iterator gsi = gsi_start (bind_body_tail);
@@ -9099,6 +9110,14 @@ lower_omp_master (gimple_stmt_iterator *gsi_p, omp_context *ctx)
   gimple_bind_add_seq (bind, gimple_omp_body (stmt));
   gimple_omp_set_body (stmt, NULL);
 
+  if (flag_openmp_ompt)
+    {
+      /* Insert call to GOMP_masked_end at the end of the body.  */
+      tree decl = builtin_decl_explicit (BUILT_IN_GOMP_MASKED_END);
+      gcall *g = gimple_build_call (decl, 0);
+      gimple_bind_add_stmt (bind, g);
+    }
+
   gimple_bind_add_stmt (bind, gimple_build_label (lab));
 
   gimple_bind_add_stmt (bind, gimple_build_omp_return (true));
diff --git gcc/opts.cc gcc/opts.cc
index 342517528e8..3dae57af0b4 100644
--- gcc/opts.cc
+++ gcc/opts.cc
@@ -1538,6 +1538,12 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
 		    " %<-fstrict-flex-arrays%> is not present");
       }
 
+  if ((opts->x_flag_openmp_ompt || opts->x_flag_openmp_ompt_detailed)
+      && !opts->x_flag_openmp)
+    error_at (
+      loc,
+      "%<-fopenmp-ompt%> and %<-fopenmp-ompt-detailed%> require %<-fopenmp%>");
+
   diagnose_options (opts, opts_set, loc);
 }
 
diff --git gcc/testsuite/c-c++-common/gomp/for-8.c gcc/testsuite/c-c++-common/gomp/for-8.c
deleted file mode 100644
index a894aaaaed1..00000000000
--- gcc/testsuite/c-c++-common/gomp/for-8.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* { dg-do compile } */
-/* { dg-additional-options "-fdump-tree-ompexp" } */
-
-/* Check that the static loop and distribute expanders fetch the thread/team
-   id and count through a single GOMP_loop_static_worksharing /
-   GOMP_distribute_static_worksharing call rather than separate
-   omp_get_thread_num/omp_get_num_threads or omp_get_team_num/omp_get_num_teams
-   calls.  */
-
-void bar (int);
-
-/* Static schedule without a chunk size goes through
-   expand_omp_for_static_nochunk.  */
-
-void
-f1 (int n)
-{
-  int i;
-  #pragma omp for schedule(static)
-  for (i = 0; i < n; ++i)
-    bar (i);
-}
-
-/* Static schedule with a chunk size goes through
-   expand_omp_for_static_chunk.  */
-
-void
-f2 (int n)
-{
-  int i;
-  #pragma omp for schedule(static, 4)
-  for (i = 0; i < n; ++i)
-    bar (i);
-}
-
-/* Distribute without a chunk size goes through
-   expand_omp_for_static_nochunk.  */
-
-void
-f3 (int n)
-{
-  int i;
-#pragma omp teams
-#pragma omp distribute
-  for (i = 0; i < n; ++i)
-    bar (i);
-}
-
-/* Distribute with a chunk size goes through
-   expand_omp_for_static_chunk.  */
-
-void
-f4 (int n)
-{
-  int i;
-#pragma omp teams
-#pragma omp distribute dist_schedule(static, 4)
-  for (i = 0; i < n; ++i)
-    bar (i);
-}
-
-/* { dg-final { scan-tree-dump "GOMP_loop_static_worksharing" "ompexp" } } */
-/* { dg-final { scan-tree-dump "GOMP_distribute_static_worksharing" "ompexp" } } */
-/* { dg-final { scan-tree-dump-not "omp_get_num_threads" "ompexp" } } */
-/* { dg-final { scan-tree-dump-not "omp_get_thread_num" "ompexp" } } */
-/* { dg-final { scan-tree-dump-not "omp_get_num_teams" "ompexp" } } */
-/* { dg-final { scan-tree-dump-not "omp_get_team_num" "ompexp" } } */
diff --git gcc/testsuite/c-c++-common/gomp/for-static-1.c gcc/testsuite/c-c++-common/gomp/for-static-1.c
new file mode 100644
index 00000000000..50d4e176f3d
--- /dev/null
+++ gcc/testsuite/c-c++-common/gomp/for-static-1.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-ompexp" } */
+
+/* Check that the static loop and distribute expanders fetch the thread/team
+   id and count through a single GOMP_loop_static_worksharing /
+   GOMP_distribute_static_worksharing call rather than separate
+   omp_get_thread_num/omp_get_num_threads or omp_get_team_num/omp_get_num_teams
+   calls.  */
+
+#include "for-static.h"
+
+/* { dg-final { scan-tree-dump-times "GOMP_loop_static_worksharing \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_distribute_static_worksharing \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "omp_get_num_threads" "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "omp_get_thread_num" "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "omp_get_num_teams" "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "omp_get_team_num" "ompexp" } } */
diff --git gcc/testsuite/c-c++-common/gomp/for-static-2.c gcc/testsuite/c-c++-common/gomp/for-static-2.c
new file mode 100644
index 00000000000..c3d25b97bed
--- /dev/null
+++ gcc/testsuite/c-c++-common/gomp/for-static-2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fopenmp-ompt -fdump-tree-ompexp" } */
+
+/* Check that, with -fopenmp-ompt, the _start and _end variants are called.  */
+
+#include "for-static.h"
+
+/* { dg-final { scan-tree-dump-times "GOMP_loop_static_worksharing_start \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_loop_static_worksharing_end \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_distribute_static_worksharing_start \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_distribute_static_worksharing_end \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "GOMP_loop_static_worksharing \\(" "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "GOMP_loop_static_worksharing_dispatch" "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "GOMP_distribute_static_worksharing \\(" "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "GOMP_distribute_static_worksharing_dispatch" "ompexp" } } */
diff --git gcc/testsuite/c-c++-common/gomp/for-static-3.c gcc/testsuite/c-c++-common/gomp/for-static-3.c
new file mode 100644
index 00000000000..6fd1da6c407
--- /dev/null
+++ gcc/testsuite/c-c++-common/gomp/for-static-3.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fopenmp-ompt-detailed -fdump-tree-ompexp" } */
+
+/* Check that, with -fopenmp-ompt-detailed, _dispatch builtins are called along
+   with the _start and _end variants.  */
+
+#include "for-static.h"
+
+/* { dg-final { scan-tree-dump-times "GOMP_loop_static_worksharing_start \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_loop_static_worksharing_dispatch \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_loop_static_worksharing_end \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_distribute_static_worksharing_start \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_distribute_static_worksharing_dispatch \\(" 2 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_distribute_static_worksharing_end \\(" 2 "ompexp" } } */
diff --git gcc/testsuite/c-c++-common/gomp/for-static.h gcc/testsuite/c-c++-common/gomp/for-static.h
new file mode 100644
index 00000000000..6504c75a13e
--- /dev/null
+++ gcc/testsuite/c-c++-common/gomp/for-static.h
@@ -0,0 +1,51 @@
+void bar (int);
+
+/* Static schedule without a chunk size goes through
+   expand_omp_for_static_nochunk.  */
+
+void
+f1 (int n)
+{
+  int i;
+  #pragma omp for schedule(static)
+  for (i = 0; i < n; ++i)
+    bar (i);
+}
+
+/* Static schedule with a chunk size goes through
+   expand_omp_for_static_chunk.  */
+
+void
+f2 (int n)
+{
+  int i;
+  #pragma omp for schedule(static, 4)
+  for (i = 0; i < n; ++i)
+    bar (i);
+}
+
+/* Distribute without a chunk size goes through
+   expand_omp_for_static_nochunk.  */
+
+void
+f3 (int n)
+{
+  int i;
+#pragma omp teams
+#pragma omp distribute
+  for (i = 0; i < n; ++i)
+    bar (i);
+}
+
+/* Distribute with a chunk size goes through
+   expand_omp_for_static_chunk.  */
+
+void
+f4 (int n)
+{
+  int i;
+#pragma omp teams
+#pragma omp distribute dist_schedule(static, 4)
+  for (i = 0; i < n; ++i)
+    bar (i);
+}
diff --git gcc/testsuite/c-c++-common/gomp/masked-1.c gcc/testsuite/c-c++-common/gomp/masked-1.c
index 8de87b551e3..28d2735d96e 100644
--- gcc/testsuite/c-c++-common/gomp/masked-1.c
+++ gcc/testsuite/c-c++-common/gomp/masked-1.c
@@ -26,4 +26,5 @@ foo (int x, int *a)
 }
 
 /* { dg-final { scan-tree-dump "GOMP_has_masked_thread_num" "omplower" } } */
+/* { dg-final { scan-tree-dump-not "GOMP_masked_end" "omplower" } } */
 /* { dg-final { scan-tree-dump-not "omp_get_thread_num" "omplower" } } */
diff --git gcc/testsuite/c-c++-common/gomp/masked-3.c gcc/testsuite/c-c++-common/gomp/masked-3.c
new file mode 100644
index 00000000000..adea59fdb69
--- /dev/null
+++ gcc/testsuite/c-c++-common/gomp/masked-3.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fopenmp-ompt -fdump-tree-omplower" } */
+
+void bar (void);
+
+void
+foo (void)
+{
+  #pragma omp masked
+  bar ();
+}
+
+/* { dg-final { scan-tree-dump-times "GOMP_has_masked_thread_num" 1 "omplower" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_masked_end" 1 "omplower" } } */
+/* { dg-final { scan-tree-dump-not "omp_get_thread_num" "omplower" } } */
diff --git gcc/testsuite/c-c++-common/gomp/openmp-ompt-1.c gcc/testsuite/c-c++-common/gomp/openmp-ompt-1.c
new file mode 100644
index 00000000000..0a45c2bd52c
--- /dev/null
+++ gcc/testsuite/c-c++-common/gomp/openmp-ompt-1.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp-ompt" } */
+
+/* { dg-error ".-fopenmp-ompt. and .-fopenmp-ompt-detailed. require .-fopenmp." "" { target *-*-* } 0 } */
diff --git gcc/testsuite/c-c++-common/gomp/openmp-ompt-2.c gcc/testsuite/c-c++-common/gomp/openmp-ompt-2.c
new file mode 100644
index 00000000000..f93f6920327
--- /dev/null
+++ gcc/testsuite/c-c++-common/gomp/openmp-ompt-2.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp-ompt-detailed" } */
+
+/* { dg-error ".-fopenmp-ompt. and .-fopenmp-ompt-detailed. require .-fopenmp." "" { target *-*-* } 0 } */
diff --git gcc/testsuite/c-c++-common/gomp/single2.c gcc/testsuite/c-c++-common/gomp/single2.c
new file mode 100644
index 00000000000..215f0368ce4
--- /dev/null
+++ gcc/testsuite/c-c++-common/gomp/single2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fopenmp-ompt -fdump-tree-ompexp" } */
+
+void
+foo (void)
+{
+  #pragma omp single
+    ;
+}
+
+/* { dg-final { scan-tree-dump-times "GOMP_single_start_with_end" 1 "ompexp" } } */
+/* { dg-final { scan-tree-dump-times "GOMP_single_end" 1 "ompexp" } } */
+/* { dg-final { scan-tree-dump-not "GOMP_single_start \\(" "ompexp" } } */
diff --git libgomp/config/gcn/teams.c libgomp/config/gcn/teams.c
index 5aa8b15502a..99f807ac876 100644
--- libgomp/config/gcn/teams.c
+++ libgomp/config/gcn/teams.c
@@ -50,6 +50,29 @@ GOMP_distribute_static_worksharing (void)
   return nteams + tid * 1I;
 }
 
+/* OMPT variant enabled by -fopenmp-ompt.  */
+
+_Complex int
+GOMP_distribute_static_worksharing_start (void)
+{
+  int __lds *gomp_team_num = (int __lds *) GOMP_TEAM_NUM;
+  unsigned tid = *gomp_team_num;
+  unsigned nteams = gomp_num_teams_var + 1;
+  return nteams + tid * 1I;
+}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt-detailed.  */
+
+void
+GOMP_distribute_static_worksharing_dispatch (void)
+{}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt.  */
+
+void
+GOMP_distribute_static_worksharing_end (void)
+{}
+
 int
 omp_get_num_teams (void)
 {
diff --git libgomp/config/nvptx/teams.c libgomp/config/nvptx/teams.c
index 9763f6f4108..3e6ea287276 100644
--- libgomp/config/nvptx/teams.c
+++ libgomp/config/nvptx/teams.c
@@ -52,6 +52,27 @@ GOMP_distribute_static_worksharing (void)
   return nteams + tid * 1I;
 }
 
+/* OMPT variant enabled by -fopenmp-ompt.  */
+
+_Complex int
+GOMP_distribute_static_worksharing_start (void)
+{
+  unsigned tid = __gomp_team_num;
+  unsigned nteams = gomp_num_teams_var + 1;
+  return nteams + tid * 1I;
+}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt-detailed.  */
+
+void
+GOMP_distribute_static_worksharing_dispatch (void)
+{}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt.  */
+
+void
+GOMP_distribute_static_worksharing_end (void)
+{}
 int
 omp_get_num_teams (void)
 {
diff --git libgomp/libgomp.map libgomp/libgomp.map
index e2985347bf7..3f465cc1491 100644
--- libgomp/libgomp.map
+++ libgomp/libgomp.map
@@ -485,12 +485,21 @@ GOMP_6.0.1 {
 GOMP_6.0.2 {
   global:
 	GOMP_has_masked_thread_num;
+	GOMP_masked_end;
 	GOMP_loop_static_worksharing;
+	GOMP_loop_static_worksharing_start;
+	GOMP_loop_static_worksharing_dispatch;
+	GOMP_loop_static_worksharing_end;
 	GOMP_distribute_static_worksharing;
+	GOMP_distribute_static_worksharing_start;
+	GOMP_distribute_static_worksharing_dispatch;
+	GOMP_distribute_static_worksharing_end;
 	GOMP_barrier_ext;
 	GOMP_barrier_cancel_ext;
 	GOMP_reduction_start;
 	GOMP_reduction_end;
+	GOMP_single_start_with_end;
+	GOMP_single_end;
 } GOMP_6.0.1;
 
 OACC_2.0 {
diff --git libgomp/libgomp_g.h libgomp/libgomp_g.h
index a3097d5b093..4a3565684cc 100644
--- libgomp/libgomp_g.h
+++ libgomp/libgomp_g.h
@@ -144,6 +144,11 @@ extern void GOMP_loop_end (void);
 extern void GOMP_loop_end_nowait (void);
 extern bool GOMP_loop_end_cancel (void);
 
+extern __complex__ int GOMP_loop_static_worksharing (void);
+extern __complex__ int GOMP_loop_static_worksharing_start (void);
+extern void GOMP_loop_static_worksharing_dispatch (void);
+extern void GOMP_loop_static_worksharing_end (void);
+
 /* loop_ull.c */
 
 extern bool GOMP_loop_ull_static_start (bool, unsigned long long,
@@ -298,7 +303,7 @@ extern unsigned GOMP_parallel_reductions (void (*) (void *), void *, unsigned,
 extern bool GOMP_cancel (int, bool);
 extern bool GOMP_cancellation_point (int);
 extern bool GOMP_has_masked_thread_num (int);
-extern __complex__ int GOMP_loop_static_worksharing (void);
+extern void GOMP_masked_end (void);
 
 /* task.c */
 
@@ -339,6 +344,8 @@ extern bool GOMP_sections_end_cancel (void);
 /* single.c */
 
 extern bool GOMP_single_start (void);
+extern bool GOMP_single_start_with_end (void);
+extern void GOMP_single_end (void);
 extern void *GOMP_single_copy_start (void);
 extern void GOMP_single_copy_end (void *);
 
@@ -380,6 +387,9 @@ extern void *GOMP_target_map_indirect_ptr (void *);
 extern void GOMP_teams_reg (void (*) (void *), void *, unsigned, unsigned,
 			    unsigned);
 extern __complex__ int GOMP_distribute_static_worksharing (void);
+extern __complex__ int GOMP_distribute_static_worksharing_start (void);
+extern void GOMP_distribute_static_worksharing_dispatch (void);
+extern void GOMP_distribute_static_worksharing_end (void);
 
 /* allocator.c */
 
diff --git libgomp/loop.c libgomp/loop.c
index e3fbeb1c4de..f286ce6b6db 100644
--- libgomp/loop.c
+++ libgomp/loop.c
@@ -1186,3 +1186,38 @@ GOMP_loop_ordered_guided_next (long *istart, long *iend)
   return gomp_loop_ordered_guided_next (istart, iend);
 }
 #endif
+
+/* For a worksharing-loop construct with static schedule, return the thread ID
+   and number of threads packed into a single complex value.  */
+
+_Complex int
+GOMP_loop_static_worksharing (void)
+{
+  struct gomp_team *team = gomp_thread ()->ts.team;
+  unsigned tid = gomp_thread ()->ts.team_id;
+  unsigned nthreads = team ? team->nthreads : 1;
+  return nthreads + tid * 1I;
+}
+
+/* OMPT variant enabled by -fopenmp-ompt.  */
+
+_Complex int
+GOMP_loop_static_worksharing_start (void)
+{
+  struct gomp_team *team = gomp_thread ()->ts.team;
+  unsigned tid = gomp_thread ()->ts.team_id;
+  unsigned nthreads = team ? team->nthreads : 1;
+  return nthreads + tid * 1I;
+}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt-detailed.  */
+
+void
+GOMP_loop_static_worksharing_dispatch (void)
+{}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt.  */
+
+void
+GOMP_loop_static_worksharing_end (void)
+{}
diff --git libgomp/parallel.c libgomp/parallel.c
index 92472ecb4f0..bb522b39ecb 100644
--- libgomp/parallel.c
+++ libgomp/parallel.c
@@ -271,18 +271,6 @@ GOMP_cancel (int which, bool do_cancel)
   return true;
 }
 
-/* For a worksharing-loop construct with static schedule, return the thread ID
-   and number of threads packed into a single complex value.  */
-
-_Complex int
-GOMP_loop_static_worksharing (void)
-{
-  struct gomp_team *team = gomp_thread ()->ts.team;
-  unsigned tid = gomp_thread ()->ts.team_id;
-  unsigned nthreads = team ? team->nthreads : 1;
-  return nthreads + tid * 1I;
-}
-
 /* Return true if the current thread number equals TID.
    Used to implement the masked construct's filter clause.  */
 
@@ -291,6 +279,12 @@ GOMP_has_masked_thread_num (int tid)
 {
   return tid == gomp_thread ()->ts.team_id;
 }
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt.  */
+
+void
+GOMP_masked_end (void)
+{}
 
 /* The public OpenMP API for thread and team related inquiries.  */
 
diff --git libgomp/single.c libgomp/single.c
index 65126000ea9..00a84e1c9c1 100644
--- libgomp/single.c
+++ libgomp/single.c
@@ -55,6 +55,37 @@ GOMP_single_start (void)
 #endif
 }
 
+/* OMPT variant enabled by -fopenmp-ompt.  */
+
+bool
+GOMP_single_start_with_end (void)
+{
+#ifdef HAVE_SYNC_BUILTINS
+  struct gomp_thread *thr = gomp_thread ();
+  struct gomp_team *team = thr->ts.team;
+  unsigned long single_count;
+
+  if (__builtin_expect (team == NULL, 0))
+    return true;
+
+  single_count = thr->ts.single_count++;
+  return __sync_bool_compare_and_swap (&team->single_count, single_count,
+				       single_count + 1L);
+#else
+  bool ret = gomp_work_share_start (0);
+  if (ret)
+    gomp_work_share_init_done ();
+  gomp_work_share_end_nowait ();
+  return ret;
+#endif
+}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt.  */
+
+void
+GOMP_single_end (void)
+{}
+
 /* This routine is called when first encountering a SINGLE construct that
    does have a COPYPRIVATE clause.  Returns NULL if this is the thread
    that should execute the clause; otherwise the return value is pointer
@@ -69,7 +100,7 @@ GOMP_single_copy_start (void)
   void *ret;
 
   first = gomp_work_share_start (0);
-  
+
   if (first)
     {
       gomp_work_share_init_done ();
diff --git libgomp/teams.c libgomp/teams.c
index 7038623b2df..f501123d647 100644
--- libgomp/teams.c
+++ libgomp/teams.c
@@ -70,6 +70,29 @@ GOMP_distribute_static_worksharing (void)
   return nteams + tid * 1I;
 }
 
+/* OMPT variant enabled by -fopenmp-ompt.  */
+
+_Complex int
+GOMP_distribute_static_worksharing_start (void)
+{
+  struct gomp_thread *thr = gomp_thread ();
+  unsigned tid = thr->team_num;
+  unsigned nteams = thr->num_teams + 1;
+  return nteams + tid * 1I;
+}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt-detailed.  */
+
+void
+GOMP_distribute_static_worksharing_dispatch (void)
+{}
+
+/* Stub for OMPT callback enabled by -fopenmp-ompt.  */
+
+void
+GOMP_distribute_static_worksharing_end (void)
+{}
+
 int
 omp_get_num_teams (void)
 {
diff --git libgomp/testsuite/libgomp.c-c++-common/for-static-1.c libgomp/testsuite/libgomp.c-c++-common/for-static-1.c
new file mode 100644
index 00000000000..b304c8c10ec
--- /dev/null
+++ libgomp/testsuite/libgomp.c-c++-common/for-static-1.c
@@ -0,0 +1,13 @@
+/* { dg-do link { target offload_target_any } } */
+/* { dg-additional-options "-O0 -foffload=-fdump-tree-optimized" } */
+
+/* Check that the static distribute expanders fetch the team id and count
+   through a single GOMP_distribute_static_worksharing call rather than separate
+   omp_get_team_num and omp_get_num_teams calls, and that this function exists
+   on the target side.  */
+
+#include "for-static.h"
+
+/* { dg-final { scan-offload-tree-dump-times "GOMP_distribute_static_worksharing \\(" 2 "optimized" } } */
+/* { dg-final { scan-offload-tree-dump-not "omp_get_num_teams" "optimized" } } */
+/* { dg-final { scan-offload-tree-dump-not "omp_get_team_num" "optimized" } } */
diff --git libgomp/testsuite/libgomp.c-c++-common/for-static-2.c libgomp/testsuite/libgomp.c-c++-common/for-static-2.c
new file mode 100644
index 00000000000..83e577a3298
--- /dev/null
+++ libgomp/testsuite/libgomp.c-c++-common/for-static-2.c
@@ -0,0 +1,11 @@
+/* { dg-do link { target offload_target_any } } */
+/* { dg-additional-options "-O0 -foffload=-fdump-tree-optimized -fopenmp-ompt" } */
+
+/* Check that, with -fopenmp-ompt, the _start and _end variants are called.  */
+
+#include "for-static.h"
+
+/* { dg-final { scan-offload-tree-dump-times "GOMP_distribute_static_worksharing_start \\(" 2 "optimized" } } */
+/* { dg-final { scan-offload-tree-dump-times "GOMP_distribute_static_worksharing_end \\(" 2 "optimized" } } */
+/* { dg-final { scan-offload-tree-dump-not "GOMP_distribute_static_worksharing \\(" "optimized" } } */
+/* { dg-final { scan-offload-tree-dump-not "GOMP_distribute_static_worksharing_dispatch" "optimized" } } */
diff --git libgomp/testsuite/libgomp.c-c++-common/for-static-3.c libgomp/testsuite/libgomp.c-c++-common/for-static-3.c
new file mode 100644
index 00000000000..e7b904eedc3
--- /dev/null
+++ libgomp/testsuite/libgomp.c-c++-common/for-static-3.c
@@ -0,0 +1,11 @@
+/* { dg-do link { target offload_target_any } } */
+/* { dg-additional-options "-O0 -foffload=-fdump-tree-optimized -fopenmp-ompt-detailed" } */
+
+/* Check that, with -fopenmp-ompt-detailed, _dispatch builtins are called along
+   with the _start and _end variants.  */
+
+#include "for-static.h"
+
+/* { dg-final { scan-offload-tree-dump-times "GOMP_distribute_static_worksharing_start \\(" 2 "optimized" } } */
+/* { dg-final { scan-offload-tree-dump-times "GOMP_distribute_static_worksharing_dispatch \\(" 2 "optimized" } } */
+/* { dg-final { scan-offload-tree-dump-times "GOMP_distribute_static_worksharing_end \\(" 2 "optimized" } } */
diff --git libgomp/testsuite/libgomp.c-c++-common/for-static.h libgomp/testsuite/libgomp.c-c++-common/for-static.h
new file mode 100644
index 00000000000..a6c0e055b83
--- /dev/null
+++ libgomp/testsuite/libgomp.c-c++-common/for-static.h
@@ -0,0 +1,37 @@
+void bar (int a)
+{}
+
+/* Distribute without a chunk size goes through
+   expand_omp_for_static_nochunk.  */
+
+void
+f3 (int n)
+{
+  int i;
+#pragma omp target
+#pragma omp teams
+#pragma omp distribute
+  for (i = 0; i < n; ++i)
+    bar (i);
+}
+
+/* Distribute with a chunk size goes through
+   expand_omp_for_static_chunk.  */
+
+void
+f4 (int n)
+{
+  int i;
+#pragma omp target
+#pragma omp teams
+#pragma omp distribute dist_schedule(static, 4)
+  for (i = 0; i < n; ++i)
+    bar (i);
+}
+
+int main (void)
+{
+  f3(0);
+  f4(1);
+  return 0;
+}

Reply via email to