1 // RUN: %libomp-cxx-compile-and-run
2 // RUN: %libomp-cxx-compile -DFLG=1 && %libomp-run
3 // REQUIRES: openmp-5.0
4 // GCC-5 is needed for OpenMP 4.0 support (taskgroup)
5 // XFAIL: gcc-4
6 #include <cstdio>
7 #include <cmath>
8 #include <cassert>
9 #include <omp.h>
10 
11 // Total number of loop iterations, should be multiple of T for this test
12 #define N 10000
13 
14 // Flag to request lazy (1) or eager (0) allocation of reduction objects
15 #ifndef FLG
16 #define FLG 0
17 #endif
18 
19 /*
20   // initial user's code that corresponds to pseudo code of the test
21   #pragma omp taskgroup task_reduction(+:i,j) task_reduction(*:x)
22   {
23     for( int l = 0; l < N; ++l ) {
24       #pragma omp task firstprivate(l) in_reduction(+:i) in_reduction(*:x)
25       {
26         i += l;
27         if( l%2 )
28           x *= 1.0 / (l + 1);
29         else
30           x *= (l + 1);
31       }
32     }
33 
34     #pragma omp taskgroup task_reduction(-:i,k) task_reduction(+:y)
35     {
36       for( int l = 0; l < N; ++l ) {
37         #pragma omp task firstprivate(l) in_reduction(+:j,y) \
38             in_reduction(*:x) in_reduction(-:k)
39         {
40           j += l;
41           k -= l;
42           y += (double)l;
43           if( l%2 )
44             x *= 1.0 / (l + 1);
45           else
46             x *= (l + 1);
47         }
48         #pragma omp task firstprivate(l) in_reduction(+:y) in_reduction(-:i,k)
49         {
50           i -= l;
51           k -= l;
52           y += (double)l;
53         }
54         #pragma omp task firstprivate(l) in_reduction(+:j) in_reduction(*:x)
55         {
56           j += l;
57           if( l%2 )
58             x *= 1.0 / (l + 1);
59           else
60             x *= (l + 1);
61         }
62       }
63     } // inner reduction
64 
65     for( int l = 0; l < N; ++l ) {
66       #pragma omp task firstprivate(l) in_reduction(+:j)
67         j += l;
68     }
69   } // outer reduction
70 */
71 
72 //------------------------------------------------
73 // OpenMP runtime library routines
74 #ifdef __cplusplus
75 extern "C" {
76 #endif
77 extern void* __kmpc_task_reduction_get_th_data(int gtid, void* tg, void* item);
78 extern void* __kmpc_task_reduction_init(int gtid, int num, void* data);
79 extern int __kmpc_global_thread_num(void*);
80 #ifdef __cplusplus
81 }
82 #endif
83 
84 //------------------------------------------------
85 // Compiler-generated code
86 
87 typedef struct _task_red_item {
88     void       *shar; // shared reduction item
89     size_t      size; // size of data item
90     void       *f_init; // data initialization routine
91     void       *f_fini; // data finalization routine
92     void       *f_comb; // data combiner routine
93     unsigned    flags;
94 } _task_red_item_t;
95 
96 // int:+   no need in init/fini callbacks, valid for subtraction
97 void __red_int_add_comb(void *lhs, void *rhs) // combiner
98 { *(int*)lhs += *(int*)rhs; }
99 
100 // long long:+   no need in init/fini callbacks, valid for subtraction
101 void __red_llong_add_comb(void *lhs, void *rhs) // combiner
102 { *(long long*)lhs += *(long long*)rhs; }
103 
104 // double:*   no need in fini callback
105 void __red_dbl_mul_init(void *data) // initializer
106 { *(double*)data = 1.0; }
107 void __red_dbl_mul_comb(void *lhs, void *rhs) // combiner
108 { *(double*)lhs *= *(double*)rhs; }
109 
110 // double:+   no need in init/fini callbacks
111 void __red_dbl_add_comb(void *lhs, void *rhs) // combiner
112 { *(double*)lhs += *(double*)rhs; }
113 
114 // ==============================
115 
116 void calc_serial(int *pi, long long *pj, double *px, long long *pk, double *py)
117 {
118     for( int l = 0; l < N; ++l ) {
119         *pi += l;
120         if( l%2 )
121           *px *= 1.0 / (l + 1);
122         else
123           *px *= (l + 1);
124     }
125     for( int l = 0; l < N; ++l ) {
126         *pj += l;
127         *pk -= l;
128         *py += (double)l;
129         if( l%2 )
130             *px *= 1.0 / (l + 1);
131         else
132             *px *= (l + 1);
133 
134         *pi -= l;
135         *pk -= l;
136         *py += (double)l;
137 
138         *pj += l;
139         if( l%2 )
140             *px *= 1.0 / (l + 1);
141         else
142             *px *= (l + 1);
143     }
144     for( int l = 0; l < N; ++l ) {
145         *pj += l;
146     }
147 }
148 
149 //------------------------------------------------
150 // Test case
151 int main()
152 {
153   int nthreads = omp_get_max_threads();
154   int err = 0;
155   void** ptrs = (void**)malloc(nthreads*sizeof(void*));
156 
157   // user's code ======================================
158   // variables for serial calculations:
159   int is = 3;
160   long long js = -9999999;
161   double xs = 99999.0;
162   long long ks = 99999999;
163   double ys = -99999999.0;
164   // variables for parallel calculations:
165   int ip = 3;
166   long long jp = -9999999;
167   double xp = 99999.0;
168   long long kp = 99999999;
169   double yp = -99999999.0;
170 
171   calc_serial(&is, &js, &xs, &ks, &ys);
172   // ==================================================
173   for (int i = 0; i < nthreads; ++i)
174     ptrs[i] = NULL;
175   #pragma omp parallel
176   {
177     #pragma omp single nowait
178     {
179       // outer taskgroup reduces (i,j,x)
180       #pragma omp taskgroup // task_reduction(+:i,j) task_reduction(*:x)
181       {
182         _task_red_item_t red_data[3];
183         red_data[0].shar = &ip;
184         red_data[0].size = sizeof(ip);
185         red_data[0].f_init = NULL; // RTL will zero thread-specific objects
186         red_data[0].f_fini = NULL; // no destructors needed
187         red_data[0].f_comb = (void*)&__red_int_add_comb;
188         red_data[0].flags = FLG;
189         red_data[1].shar = &jp;
190         red_data[1].size = sizeof(jp);
191         red_data[1].f_init = NULL; // RTL will zero thread-specific objects
192         red_data[1].f_fini = NULL; // no destructors needed
193         red_data[1].f_comb = (void*)&__red_llong_add_comb;
194         red_data[1].flags = FLG;
195         red_data[2].shar = &xp;
196         red_data[2].size = sizeof(xp);
197         red_data[2].f_init = (void*)&__red_dbl_mul_init;
198         red_data[2].f_fini = NULL; // no destructors needed
199         red_data[2].f_comb = (void*)&__red_dbl_mul_comb;
200         red_data[2].flags = FLG;
201         int gtid = __kmpc_global_thread_num(NULL);
202         void* tg1 = __kmpc_task_reduction_init(gtid, 3, red_data);
203 
204         for( int l = 0; l < N; l += 2 ) {
205           // 2 iterations per task to get correct x value; actually any even
206           // number of iters per task will work, otherwise x looses precision
207           #pragma omp task firstprivate(l) //in_reduction(+:i) in_reduction(*:x)
208           {
209             int gtid = __kmpc_global_thread_num(NULL);
210             int *p_ip = (int*)__kmpc_task_reduction_get_th_data(gtid, tg1, &ip);
211             double *p_xp = (double*)__kmpc_task_reduction_get_th_data(
212                                         gtid, tg1, &xp);
213             if (!ptrs[gtid]) ptrs[gtid] = p_xp;
214 
215             // user's pseudo-code ==============================
216             *p_ip += l;
217             *p_xp *= (l + 1);
218 
219             *p_ip += l + 1;
220             *p_xp *= 1.0 / (l + 2);
221             // ==================================================
222           }
223         }
224         // inner taskgroup reduces (i,k,y), i is same object as in outer one
225         #pragma omp taskgroup // task_reduction(-:i,k) task_reduction(+:y)
226         {
227           _task_red_item_t red_data[3];
228           red_data[0].shar = &ip;
229           red_data[0].size = sizeof(ip);
230           red_data[0].f_init = NULL; // RTL will zero thread-specific objects
231           red_data[0].f_fini = NULL; // no destructors needed
232           red_data[0].f_comb = (void*)&__red_int_add_comb;
233           red_data[0].flags = FLG;
234           red_data[1].shar = &kp;
235           red_data[1].size = sizeof(kp);
236           red_data[1].f_init = NULL; // RTL will zero thread-specific objects
237           red_data[1].f_fini = NULL; // no destructors needed
238           red_data[1].f_comb = (void*)&__red_llong_add_comb; // same for + and -
239           red_data[1].flags = FLG;
240           red_data[2].shar = &yp;
241           red_data[2].size = sizeof(yp);
242           red_data[2].f_init = NULL; // RTL will zero thread-specific objects
243           red_data[2].f_fini = NULL; // no destructors needed
244           red_data[2].f_comb = (void*)&__red_dbl_add_comb;
245           red_data[2].flags = FLG;
246           int gtid = __kmpc_global_thread_num(NULL);
247           void* tg2 = __kmpc_task_reduction_init(gtid, 3, red_data);
248 
249           for( int l = 0; l < N; l += 2 ) {
250             #pragma omp task firstprivate(l)
251             // in_reduction(+:j,y) in_reduction(*:x) in_reduction(-:k)
252             {
253               int gtid = __kmpc_global_thread_num(NULL);
254               long long *p_jp = (long long*)__kmpc_task_reduction_get_th_data(
255                                                 gtid, tg1, &jp);
256               long long *p_kp = (long long*)__kmpc_task_reduction_get_th_data(
257                                                 gtid, tg2, &kp);
258               double *p_xp = (double*)__kmpc_task_reduction_get_th_data(
259                                           gtid, tg1, &xp);
260               double *p_yp = (double*)__kmpc_task_reduction_get_th_data(
261                                           gtid, tg2, &yp);
262               // user's pseudo-code ==============================
263               *p_jp += l;
264               *p_kp -= l;
265               *p_yp += (double)l;
266               *p_xp *= (l + 1);
267 
268               *p_jp += l + 1;
269               *p_kp -= l + 1;
270               *p_yp += (double)(l + 1);
271               *p_xp *= 1.0 / (l + 2);
272               // =================================================
273 {
274   // the following code is here just to check __kmpc_task_reduction_get_th_data:
275   int tid = omp_get_thread_num();
276   void *addr1;
277   void *addr2;
278   addr1 = __kmpc_task_reduction_get_th_data(gtid, tg1, &xp); // from shared
279   addr2 = __kmpc_task_reduction_get_th_data(gtid, tg1, addr1); // from private
280   if (addr1 != addr2) {
281     #pragma omp atomic
282       ++err;
283     printf("Wrong thread-specific addresses %d s:%p p:%p\n", tid, addr1, addr2);
284   }
285   // from neighbour w/o taskgroup (should start lookup from current tg2)
286   if (tid > 0) {
287     if (ptrs[tid-1]) {
288       addr2 = __kmpc_task_reduction_get_th_data(gtid, NULL, ptrs[tid-1]);
289       if (addr1 != addr2) {
290         #pragma omp atomic
291           ++err;
292         printf("Wrong thread-specific addresses %d s:%p n:%p\n",
293                tid, addr1, addr2);
294       }
295     }
296   } else {
297     if (ptrs[nthreads-1]) {
298       addr2 = __kmpc_task_reduction_get_th_data(gtid, NULL, ptrs[nthreads-1]);
299       if (addr1 != addr2) {
300         #pragma omp atomic
301           ++err;
302         printf("Wrong thread-specific addresses %d s:%p n:%p\n",
303                tid, addr1, addr2);
304       }
305     }
306   }
307   // ----------------------------------------------
308 }
309             }
310             #pragma omp task firstprivate(l)
311             // in_reduction(+:y) in_reduction(-:i,k)
312             {
313               int gtid = __kmpc_global_thread_num(NULL);
314               int *p_ip = (int*)__kmpc_task_reduction_get_th_data(
315                                     gtid, tg2, &ip);
316               long long *p_kp = (long long*)__kmpc_task_reduction_get_th_data(
317                                                 gtid, tg2, &kp);
318               double *p_yp = (double*)__kmpc_task_reduction_get_th_data(
319                                           gtid, tg2, &yp);
320 
321               // user's pseudo-code ==============================
322               *p_ip -= l;
323               *p_kp -= l;
324               *p_yp += (double)l;
325 
326               *p_ip -= l + 1;
327               *p_kp -= l + 1;
328               *p_yp += (double)(l + 1);
329               // =================================================
330             }
331             #pragma omp task firstprivate(l)
332             // in_reduction(+:j) in_reduction(*:x)
333             {
334               int gtid = __kmpc_global_thread_num(NULL);
335               long long *p_jp = (long long*)__kmpc_task_reduction_get_th_data(
336                                                 gtid, tg1, &jp);
337               double *p_xp = (double*)__kmpc_task_reduction_get_th_data(
338                                           gtid, tg1, &xp);
339               // user's pseudo-code ==============================
340               *p_jp += l;
341               *p_xp *= (l + 1);
342 
343               *p_jp += l + 1;
344               *p_xp *= 1.0 / (l + 2);
345               // =================================================
346             }
347           }
348         } // inner reduction
349 
350         for( int l = 0; l < N; l += 2 ) {
351           #pragma omp task firstprivate(l) // in_reduction(+:j)
352           {
353             int gtid = __kmpc_global_thread_num(NULL);
354             long long *p_jp = (long long*)__kmpc_task_reduction_get_th_data(
355                                               gtid, tg1, &jp);
356             // user's pseudo-code ==============================
357             *p_jp += l;
358             *p_jp += l + 1;
359             // =================================================
360           }
361         }
362       } // outer reduction
363     } // end single
364   } // end parallel
365   // check results
366 #if _DEBUG
367   printf("reduction flags = %u\n", FLG);
368 #endif
369   if (ip == is && jp == js && ks == kp &&
370       fabs(xp - xs) < 0.01 && fabs(yp - ys) < 0.01)
371     printf("passed\n");
372   else
373     printf("failed,\n ser:(%d %lld %f %lld %f)\n par:(%d %lld %f %lld %f)\n",
374       is, js, xs, ks, ys,
375       ip, jp, xp, kp, yp);
376   return 0;
377 }
378