(root)/
gcc-13.2.0/
libgomp/
sections.c
       1  /* Copyright (C) 2005-2023 Free Software Foundation, Inc.
       2     Contributed by Richard Henderson <rth@redhat.com>.
       3  
       4     This file is part of the GNU Offloading and Multi Processing Library
       5     (libgomp).
       6  
       7     Libgomp is free software; you can redistribute it and/or modify it
       8     under the terms of the GNU General Public License as published by
       9     the Free Software Foundation; either version 3, or (at your option)
      10     any later version.
      11  
      12     Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
      13     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      14     FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      15     more details.
      16  
      17     Under Section 7 of GPL version 3, you are granted additional
      18     permissions described in the GCC Runtime Library Exception, version
      19     3.1, as published by the Free Software Foundation.
      20  
      21     You should have received a copy of the GNU General Public License and
      22     a copy of the GCC Runtime Library Exception along with this program;
      23     see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      24     <http://www.gnu.org/licenses/>.  */
      25  
      26  /* This file handles the SECTIONS construct.  */
      27  
      28  #include "libgomp.h"
      29  #include <string.h>
      30  
      31  
      32  ialias_redirect (GOMP_taskgroup_reduction_register)
      33  
      34  /* Initialize the given work share construct from the given arguments.  */
      35  
      36  static inline void
      37  gomp_sections_init (struct gomp_work_share *ws, unsigned count)
      38  {
      39    ws->sched = GFS_DYNAMIC;
      40    ws->chunk_size = 1;
      41    ws->end = count + 1L;
      42    ws->incr = 1;
      43    ws->next = 1;
      44  #ifdef HAVE_SYNC_BUILTINS
      45    /* Prepare things to make each iteration faster.  */
      46    if (sizeof (long) > sizeof (unsigned))
      47      ws->mode = 1;
      48    else
      49      {
      50        struct gomp_thread *thr = gomp_thread ();
      51        struct gomp_team *team = thr->ts.team;
      52        long nthreads = team ? team->nthreads : 1;
      53  
      54        ws->mode = ((nthreads | ws->end)
      55  		  < 1UL << (sizeof (long) * __CHAR_BIT__ / 2 - 1));
      56      }
      57  #else
      58    ws->mode = 0;
      59  #endif
      60  }
      61  
      62  /* This routine is called when first encountering a sections construct
      63     that is not bound directly to a parallel construct.  The first thread 
      64     that arrives will create the work-share construct; subsequent threads
      65     will see the construct exists and allocate work from it.
      66  
      67     COUNT is the number of sections in this construct.
      68  
      69     Returns the 1-based section number for this thread to perform, or 0 if
      70     all work was assigned to other threads prior to this thread's arrival.  */
      71  
      72  unsigned
      73  GOMP_sections_start (unsigned count)
      74  {
      75    struct gomp_thread *thr = gomp_thread ();
      76    long s, e, ret;
      77  
      78    if (gomp_work_share_start (0))
      79      {
      80        gomp_sections_init (thr->ts.work_share, count);
      81        gomp_work_share_init_done ();
      82      }
      83  
      84  #ifdef HAVE_SYNC_BUILTINS
      85    if (gomp_iter_dynamic_next (&s, &e))
      86      ret = s;
      87    else
      88      ret = 0;
      89  #else
      90    gomp_mutex_lock (&thr->ts.work_share->lock);
      91    if (gomp_iter_dynamic_next_locked (&s, &e))
      92      ret = s;
      93    else
      94      ret = 0;
      95    gomp_mutex_unlock (&thr->ts.work_share->lock);
      96  #endif
      97  
      98    return ret;
      99  }
     100  
     101  unsigned
     102  GOMP_sections2_start (unsigned count, uintptr_t *reductions, void **mem)
     103  {
     104    struct gomp_thread *thr = gomp_thread ();
     105    long s, e, ret;
     106  
     107    if (reductions)
     108      gomp_workshare_taskgroup_start ();
     109    if (gomp_work_share_start (0))
     110      {
     111        gomp_sections_init (thr->ts.work_share, count);
     112        if (reductions)
     113  	{
     114  	  GOMP_taskgroup_reduction_register (reductions);
     115  	  thr->task->taskgroup->workshare = true;
     116  	  thr->ts.work_share->task_reductions = reductions;
     117  	}
     118        if (mem)
     119  	{
     120  	  uintptr_t size = (uintptr_t) *mem;
     121  #define INLINE_ORDERED_TEAM_IDS_OFF \
     122    ((offsetof (struct gomp_work_share, inline_ordered_team_ids)		\
     123      + __alignof__ (long long) - 1) & ~(__alignof__ (long long) - 1))
     124  	  if (sizeof (struct gomp_work_share)
     125  	      <= INLINE_ORDERED_TEAM_IDS_OFF
     126  	      || __alignof__ (struct gomp_work_share) < __alignof__ (long long)
     127  	      || size > (sizeof (struct gomp_work_share)
     128  			- INLINE_ORDERED_TEAM_IDS_OFF))
     129  	    *mem
     130  	      = (void *) (thr->ts.work_share->ordered_team_ids
     131  			  = gomp_malloc_cleared (size));
     132  	  else
     133  	    *mem = memset (((char *) thr->ts.work_share)
     134  			   + INLINE_ORDERED_TEAM_IDS_OFF, '\0', size);
     135  	}
     136        gomp_work_share_init_done ();
     137      }
     138    else
     139      {
     140        if (reductions)
     141  	{
     142  	  uintptr_t *first_reductions = thr->ts.work_share->task_reductions;
     143  	  gomp_workshare_task_reduction_register (reductions,
     144  						  first_reductions);
     145  	}
     146        if (mem)
     147  	{
     148  	  if ((offsetof (struct gomp_work_share, inline_ordered_team_ids)
     149  	       & (__alignof__ (long long) - 1)) == 0)
     150  	    *mem = (void *) thr->ts.work_share->ordered_team_ids;
     151  	  else
     152  	    {
     153  	      uintptr_t p = (uintptr_t) thr->ts.work_share->ordered_team_ids;
     154  	      p += __alignof__ (long long) - 1;
     155  	      p &= ~(__alignof__ (long long) - 1);
     156  	      *mem = (void *) p;
     157  	    }
     158  	}
     159      }
     160  
     161  #ifdef HAVE_SYNC_BUILTINS
     162    if (gomp_iter_dynamic_next (&s, &e))
     163      ret = s;
     164    else
     165      ret = 0;
     166  #else
     167    gomp_mutex_lock (&thr->ts.work_share->lock);
     168    if (gomp_iter_dynamic_next_locked (&s, &e))
     169      ret = s;
     170    else
     171      ret = 0;
     172    gomp_mutex_unlock (&thr->ts.work_share->lock);
     173  #endif
     174  
     175    return ret;
     176  }
     177  
     178  /* This routine is called when the thread completes processing of the
     179     section currently assigned to it.  If the work-share construct is
     180     bound directly to a parallel construct, then the construct may have
     181     been set up before the parallel.  In which case, this may be the
     182     first iteration for the thread.
     183  
     184     Returns the 1-based section number for this thread to perform, or 0 if
     185     all work was assigned to other threads prior to this thread's arrival.  */
     186  
     187  unsigned
     188  GOMP_sections_next (void)
     189  {
     190    long s, e, ret;
     191  
     192  #ifdef HAVE_SYNC_BUILTINS
     193    if (gomp_iter_dynamic_next (&s, &e))
     194      ret = s;
     195    else
     196      ret = 0;
     197  #else
     198    struct gomp_thread *thr = gomp_thread ();
     199  
     200    gomp_mutex_lock (&thr->ts.work_share->lock);
     201    if (gomp_iter_dynamic_next_locked (&s, &e))
     202      ret = s;
     203    else
     204      ret = 0;
     205    gomp_mutex_unlock (&thr->ts.work_share->lock);
     206  #endif
     207  
     208    return ret;
     209  }
     210  
     211  /* This routine pre-initializes a work-share construct to avoid one
     212     synchronization once we get into the loop.  */
     213  
     214  void
     215  GOMP_parallel_sections_start (void (*fn) (void *), void *data,
     216  			      unsigned num_threads, unsigned count)
     217  {
     218    struct gomp_team *team;
     219  
     220    num_threads = gomp_resolve_num_threads (num_threads, count);
     221    team = gomp_new_team (num_threads);
     222    gomp_sections_init (&team->work_shares[0], count);
     223    gomp_team_start (fn, data, num_threads, 0, team, NULL);
     224  }
     225  
     226  ialias_redirect (GOMP_parallel_end)
     227  
     228  void
     229  GOMP_parallel_sections (void (*fn) (void *), void *data,
     230  			unsigned num_threads, unsigned count, unsigned flags)
     231  {
     232    struct gomp_team *team;
     233  
     234    num_threads = gomp_resolve_num_threads (num_threads, count);
     235    team = gomp_new_team (num_threads);
     236    gomp_sections_init (&team->work_shares[0], count);
     237    gomp_team_start (fn, data, num_threads, flags, team, NULL);
     238    fn (data);
     239    GOMP_parallel_end ();
     240  }
     241  
     242  /* The GOMP_section_end* routines are called after the thread is told
     243     that all sections are complete.  The first two versions synchronize
     244     all threads; the nowait version does not.  */
     245  
     246  void
     247  GOMP_sections_end (void)
     248  {
     249    gomp_work_share_end ();
     250  }
     251  
     252  bool
     253  GOMP_sections_end_cancel (void)
     254  {
     255    return gomp_work_share_end_cancel ();
     256  }
     257  
     258  void
     259  GOMP_sections_end_nowait (void)
     260  {
     261    gomp_work_share_end_nowait ();
     262  }