1 // RUN: %libomp-cxx-compile-and-run
2 
3 /*
4  * This test aims to check whether hidden helper task can work with regular task
5  * in terms of dependences. It is equivalent to the following code:
6  *
7  * #pragma omp parallel
8  * for (int i = 0; i < N; ++i) {
9  *   int data = -1;
10  * #pragma omp task shared(data) depend(out: data)
11  *   {
12  *     data = 1;
13  *   }
14  * #pragma omp hidden helper task shared(data) depend(inout: data)
15  *   {
16  *     data += 2;
17  *   }
18  * #pragma omp hidden helper task shared(data) depend(inout: data)
19  *   {
20  *     data += 4;
21  *   }
22  * #pragma omp task shared(data) depend(inout: data)
23  *   {
24  *     data += 8;
25  *   }
26  * #pragma omp taskwait
27  *   assert(data == 15);
28  * }
29  */
30 
31 #include "common.h"
32 
33 extern "C" {
34 struct kmp_task_t_with_privates {
35   kmp_task_t task;
36 };
37 
38 struct anon {
39   int32_t *data;
40 };
41 }
42 
43 template <int I>
44 kmp_int32 omp_task_entry(kmp_int32 gtid, kmp_task_t_with_privates *task) {
45   auto shareds = reinterpret_cast<anon *>(task->task.shareds);
46   auto p = shareds->data;
47   *p += I;
48   return 0;
49 }
50 
51 int main(int argc, char *argv[]) {
52   constexpr const int N = 1024;
53 #pragma omp parallel for
54   for (int i = 0; i < N; ++i) {
55     int32_t gtid = __kmpc_global_thread_num(nullptr);
56     int32_t data = 0;
57 
58     // Task 1
59     auto task1 = __kmpc_omp_task_alloc(
60         nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
61         reinterpret_cast<kmp_routine_entry_t>(omp_task_entry<1>));
62 
63     auto shareds = reinterpret_cast<anon *>(task1->shareds);
64     shareds->data = &data;
65 
66     kmp_depend_info_t depinfo1;
67     depinfo1.base_addr = reinterpret_cast<intptr_t>(&data);
68     depinfo1.flag = 0;
69     depinfo1.flags.out = 1;
70     depinfo1.len = 4;
71 
72     __kmpc_omp_task_with_deps(nullptr, gtid, task1, 1, &depinfo1, 0, nullptr);
73 
74     // Task 2
75     auto task2 = __kmpc_omp_target_task_alloc(
76         nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
77         reinterpret_cast<kmp_routine_entry_t>(omp_task_entry<2>), -1);
78 
79     shareds = reinterpret_cast<anon *>(task2->shareds);
80     shareds->data = &data;
81 
82     kmp_depend_info_t depinfo2;
83     depinfo2.base_addr = reinterpret_cast<intptr_t>(&data);
84     depinfo2.flag = 0;
85     depinfo2.flags.in = 1;
86     depinfo2.flags.out = 1;
87     depinfo2.len = 4;
88 
89     __kmpc_omp_task_with_deps(nullptr, gtid, task2, 1, &depinfo2, 0, nullptr);
90 
91     // Task 3
92     auto task3 = __kmpc_omp_target_task_alloc(
93         nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
94         reinterpret_cast<kmp_routine_entry_t>(omp_task_entry<4>), -1);
95 
96     shareds = reinterpret_cast<anon *>(task3->shareds);
97     shareds->data = &data;
98 
99     kmp_depend_info_t depinfo3;
100     depinfo3.base_addr = reinterpret_cast<intptr_t>(&data);
101     depinfo3.flag = 0;
102     depinfo3.flags.in = 1;
103     depinfo3.flags.out = 1;
104     depinfo3.len = 4;
105 
106     __kmpc_omp_task_with_deps(nullptr, gtid, task3, 1, &depinfo3, 0, nullptr);
107 
108     // Task 4
109     auto task4 = __kmpc_omp_task_alloc(
110         nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
111         reinterpret_cast<kmp_routine_entry_t>(omp_task_entry<8>));
112 
113     shareds = reinterpret_cast<anon *>(task4->shareds);
114     shareds->data = &data;
115 
116     kmp_depend_info_t depinfo4;
117     depinfo4.base_addr = reinterpret_cast<intptr_t>(&data);
118     depinfo4.flag = 0;
119     depinfo4.flags.in = 1;
120     depinfo4.flags.out = 1;
121     depinfo4.len = 4;
122 
123     __kmpc_omp_task_with_deps(nullptr, gtid, task4, 1, &depinfo4, 0, nullptr);
124 
125     // Wait for all tasks
126     __kmpc_omp_taskwait(nullptr, gtid);
127 
128     assert(data == 15);
129   }
130 
131   std::cout << "PASS\n";
132   return 0;
133 }
134 
135 // CHECK: PASS
136