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