1 1.1.1.3 mrg /* Copyright (C) 2015-2024 Free Software Foundation, Inc. 2 1.1 mrg Contributed by Mentor Embedded. 3 1.1 mrg 4 1.1 mrg This file is part of the GNU Offloading and Multi Processing Library 5 1.1 mrg (libgomp). 6 1.1 mrg 7 1.1 mrg Libgomp is free software; you can redistribute it and/or modify it 8 1.1 mrg under the terms of the GNU General Public License as published by 9 1.1 mrg the Free Software Foundation; either version 3, or (at your option) 10 1.1 mrg any later version. 11 1.1 mrg 12 1.1 mrg Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY 13 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 1.1 mrg FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 1.1 mrg more details. 16 1.1 mrg 17 1.1 mrg Under Section 7 of GPL version 3, you are granted additional 18 1.1 mrg permissions described in the GCC Runtime Library Exception, version 19 1.1 mrg 3.1, as published by the Free Software Foundation. 20 1.1 mrg 21 1.1 mrg You should have received a copy of the GNU General Public License and 22 1.1 mrg a copy of the GCC Runtime Library Exception along with this program; 23 1.1 mrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 24 1.1 mrg <http://www.gnu.org/licenses/>. */ 25 1.1 mrg 26 1.1 mrg /* This is an AMD GCN specific implementation of a barrier synchronization 27 1.1 mrg mechanism for libgomp. This type is private to the library. This 28 1.1 mrg implementation uses atomic instructions and s_barrier instruction. It 29 1.1 mrg uses MEMMODEL_RELAXED here because barriers are within workgroups and 30 1.1 mrg therefore don't need to flush caches. */ 31 1.1 mrg 32 1.1 mrg #include <limits.h> 33 1.1 mrg #include "libgomp.h" 34 1.1 mrg 35 1.1 mrg 36 1.1 mrg void 37 1.1 mrg gomp_barrier_wait_end (gomp_barrier_t *bar, gomp_barrier_state_t state) 38 1.1 mrg { 39 1.1 mrg if (__builtin_expect (state & BAR_WAS_LAST, 0)) 40 1.1 mrg { 41 1.1 mrg /* Next time we'll be awaiting TOTAL threads again. */ 42 1.1 mrg bar->awaited = bar->total; 43 1.1 mrg __atomic_store_n (&bar->generation, bar->generation + BAR_INCR, 44 1.1 mrg MEMMODEL_RELAXED); 45 1.1 mrg } 46 1.1.1.2 mrg if (bar->total > 1) 47 1.1.1.2 mrg asm ("s_barrier" ::: "memory"); 48 1.1 mrg } 49 1.1 mrg 50 1.1 mrg void 51 1.1 mrg gomp_barrier_wait (gomp_barrier_t *bar) 52 1.1 mrg { 53 1.1 mrg gomp_barrier_wait_end (bar, gomp_barrier_wait_start (bar)); 54 1.1 mrg } 55 1.1 mrg 56 1.1 mrg /* Like gomp_barrier_wait, except that if the encountering thread 57 1.1 mrg is not the last one to hit the barrier, it returns immediately. 58 1.1 mrg The intended usage is that a thread which intends to gomp_barrier_destroy 59 1.1 mrg this barrier calls gomp_barrier_wait, while all other threads 60 1.1 mrg call gomp_barrier_wait_last. When gomp_barrier_wait returns, 61 1.1 mrg the barrier can be safely destroyed. */ 62 1.1 mrg 63 1.1 mrg void 64 1.1 mrg gomp_barrier_wait_last (gomp_barrier_t *bar) 65 1.1 mrg { 66 1.1 mrg /* Deferring to gomp_barrier_wait does not use the optimization opportunity 67 1.1 mrg allowed by the interface contract for all-but-last participants. The 68 1.1 mrg original implementation in config/linux/bar.c handles this better. */ 69 1.1 mrg gomp_barrier_wait (bar); 70 1.1 mrg } 71 1.1 mrg 72 1.1 mrg void 73 1.1 mrg gomp_team_barrier_wake (gomp_barrier_t *bar, int count) 74 1.1 mrg { 75 1.1.1.2 mrg if (bar->total > 1) 76 1.1.1.2 mrg asm ("s_barrier" ::: "memory"); 77 1.1 mrg } 78 1.1 mrg 79 1.1 mrg void 80 1.1 mrg gomp_team_barrier_wait_end (gomp_barrier_t *bar, gomp_barrier_state_t state) 81 1.1 mrg { 82 1.1 mrg unsigned int generation, gen; 83 1.1 mrg 84 1.1 mrg if (__builtin_expect (state & BAR_WAS_LAST, 0)) 85 1.1 mrg { 86 1.1 mrg /* Next time we'll be awaiting TOTAL threads again. */ 87 1.1 mrg struct gomp_thread *thr = gomp_thread (); 88 1.1 mrg struct gomp_team *team = thr->ts.team; 89 1.1 mrg 90 1.1 mrg bar->awaited = bar->total; 91 1.1 mrg team->work_share_cancelled = 0; 92 1.1 mrg if (__builtin_expect (team->task_count, 0)) 93 1.1 mrg { 94 1.1 mrg gomp_barrier_handle_tasks (state); 95 1.1 mrg state &= ~BAR_WAS_LAST; 96 1.1 mrg } 97 1.1 mrg else 98 1.1 mrg { 99 1.1 mrg state &= ~BAR_CANCELLED; 100 1.1 mrg state += BAR_INCR - BAR_WAS_LAST; 101 1.1 mrg __atomic_store_n (&bar->generation, state, MEMMODEL_RELAXED); 102 1.1.1.2 mrg if (bar->total > 1) 103 1.1.1.2 mrg asm ("s_barrier" ::: "memory"); 104 1.1 mrg return; 105 1.1 mrg } 106 1.1 mrg } 107 1.1 mrg 108 1.1 mrg generation = state; 109 1.1 mrg state &= ~BAR_CANCELLED; 110 1.1 mrg int retry = 100; 111 1.1 mrg do 112 1.1 mrg { 113 1.1 mrg if (retry-- == 0) 114 1.1 mrg { 115 1.1 mrg /* It really shouldn't happen that barriers get out of sync, but 116 1.1 mrg if they do then this will loop until they realign, so we need 117 1.1 mrg to avoid an infinite loop where the thread just isn't there. */ 118 1.1 mrg const char msg[] = ("Barrier sync failed (another thread died?);" 119 1.1 mrg " aborting."); 120 1.1 mrg write (2, msg, sizeof (msg)-1); 121 1.1 mrg abort(); 122 1.1 mrg } 123 1.1 mrg 124 1.1 mrg asm ("s_barrier" ::: "memory"); 125 1.1 mrg gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE); 126 1.1 mrg if (__builtin_expect (gen & BAR_TASK_PENDING, 0)) 127 1.1 mrg { 128 1.1 mrg gomp_barrier_handle_tasks (state); 129 1.1 mrg gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE); 130 1.1 mrg } 131 1.1 mrg generation |= gen & BAR_WAITING_FOR_TASK; 132 1.1 mrg } 133 1.1 mrg while (gen != state + BAR_INCR); 134 1.1 mrg } 135 1.1 mrg 136 1.1 mrg void 137 1.1 mrg gomp_team_barrier_wait (gomp_barrier_t *bar) 138 1.1 mrg { 139 1.1 mrg gomp_team_barrier_wait_end (bar, gomp_barrier_wait_start (bar)); 140 1.1 mrg } 141 1.1 mrg 142 1.1 mrg void 143 1.1 mrg gomp_team_barrier_wait_final (gomp_barrier_t *bar) 144 1.1 mrg { 145 1.1 mrg gomp_barrier_state_t state = gomp_barrier_wait_final_start (bar); 146 1.1 mrg if (__builtin_expect (state & BAR_WAS_LAST, 0)) 147 1.1 mrg bar->awaited_final = bar->total; 148 1.1 mrg gomp_team_barrier_wait_end (bar, state); 149 1.1 mrg } 150 1.1 mrg 151 1.1 mrg bool 152 1.1 mrg gomp_team_barrier_wait_cancel_end (gomp_barrier_t *bar, 153 1.1 mrg gomp_barrier_state_t state) 154 1.1 mrg { 155 1.1 mrg unsigned int generation, gen; 156 1.1 mrg 157 1.1 mrg if (__builtin_expect (state & BAR_WAS_LAST, 0)) 158 1.1 mrg { 159 1.1 mrg /* Next time we'll be awaiting TOTAL threads again. */ 160 1.1 mrg /* BAR_CANCELLED should never be set in state here, because 161 1.1 mrg cancellation means that at least one of the threads has been 162 1.1 mrg cancelled, thus on a cancellable barrier we should never see 163 1.1 mrg all threads to arrive. */ 164 1.1 mrg struct gomp_thread *thr = gomp_thread (); 165 1.1 mrg struct gomp_team *team = thr->ts.team; 166 1.1 mrg 167 1.1 mrg bar->awaited = bar->total; 168 1.1 mrg team->work_share_cancelled = 0; 169 1.1 mrg if (__builtin_expect (team->task_count, 0)) 170 1.1 mrg { 171 1.1 mrg gomp_barrier_handle_tasks (state); 172 1.1 mrg state &= ~BAR_WAS_LAST; 173 1.1 mrg } 174 1.1 mrg else 175 1.1 mrg { 176 1.1 mrg state += BAR_INCR - BAR_WAS_LAST; 177 1.1 mrg __atomic_store_n (&bar->generation, state, MEMMODEL_RELAXED); 178 1.1.1.2 mrg if (bar->total > 1) 179 1.1.1.2 mrg asm ("s_barrier" ::: "memory"); 180 1.1 mrg return false; 181 1.1 mrg } 182 1.1 mrg } 183 1.1 mrg 184 1.1 mrg if (__builtin_expect (state & BAR_CANCELLED, 0)) 185 1.1 mrg return true; 186 1.1 mrg 187 1.1 mrg generation = state; 188 1.1 mrg int retry = 100; 189 1.1 mrg do 190 1.1 mrg { 191 1.1 mrg if (retry-- == 0) 192 1.1 mrg { 193 1.1 mrg /* It really shouldn't happen that barriers get out of sync, but 194 1.1 mrg if they do then this will loop until they realign, so we need 195 1.1 mrg to avoid an infinite loop where the thread just isn't there. */ 196 1.1 mrg const char msg[] = ("Barrier sync failed (another thread died?);" 197 1.1 mrg " aborting."); 198 1.1 mrg write (2, msg, sizeof (msg)-1); 199 1.1 mrg abort(); 200 1.1 mrg } 201 1.1 mrg 202 1.1.1.2 mrg if (bar->total > 1) 203 1.1.1.2 mrg asm ("s_barrier" ::: "memory"); 204 1.1 mrg gen = __atomic_load_n (&bar->generation, MEMMODEL_RELAXED); 205 1.1 mrg if (__builtin_expect (gen & BAR_CANCELLED, 0)) 206 1.1 mrg return true; 207 1.1 mrg if (__builtin_expect (gen & BAR_TASK_PENDING, 0)) 208 1.1 mrg { 209 1.1 mrg gomp_barrier_handle_tasks (state); 210 1.1 mrg gen = __atomic_load_n (&bar->generation, MEMMODEL_RELAXED); 211 1.1 mrg } 212 1.1 mrg generation |= gen & BAR_WAITING_FOR_TASK; 213 1.1 mrg } 214 1.1 mrg while (gen != state + BAR_INCR); 215 1.1 mrg 216 1.1 mrg return false; 217 1.1 mrg } 218 1.1 mrg 219 1.1 mrg bool 220 1.1 mrg gomp_team_barrier_wait_cancel (gomp_barrier_t *bar) 221 1.1 mrg { 222 1.1 mrg return gomp_team_barrier_wait_cancel_end (bar, gomp_barrier_wait_start (bar)); 223 1.1 mrg } 224 1.1 mrg 225 1.1 mrg void 226 1.1 mrg gomp_team_barrier_cancel (struct gomp_team *team) 227 1.1 mrg { 228 1.1 mrg gomp_mutex_lock (&team->task_lock); 229 1.1 mrg if (team->barrier.generation & BAR_CANCELLED) 230 1.1 mrg { 231 1.1 mrg gomp_mutex_unlock (&team->task_lock); 232 1.1 mrg return; 233 1.1 mrg } 234 1.1 mrg team->barrier.generation |= BAR_CANCELLED; 235 1.1 mrg gomp_mutex_unlock (&team->task_lock); 236 1.1 mrg gomp_team_barrier_wake (&team->barrier, INT_MAX); 237 1.1 mrg } 238