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.flags.out = 1;
69     depinfo1.len = 4;
70 
71     __kmpc_omp_task_with_deps(nullptr, gtid, task1, 1, &depinfo1, 0, nullptr);
72 
73     // Task 2
74     auto task2 = __kmpc_omp_target_task_alloc(
75         nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
76         reinterpret_cast<kmp_routine_entry_t>(omp_task_entry<2>), -1);
77 
78     shareds = reinterpret_cast<anon *>(task2->shareds);
79     shareds->data = &data;
80 
81     kmp_depend_info_t depinfo2;
82     depinfo2.base_addr = reinterpret_cast<intptr_t>(&data);
83     depinfo2.flags.in = 1;
84     depinfo2.flags.out = 1;
85     depinfo2.len = 4;
86 
87     __kmpc_omp_task_with_deps(nullptr, gtid, task2, 1, &depinfo2, 0, nullptr);
88 
89     // Task 3
90     auto task3 = __kmpc_omp_target_task_alloc(
91         nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
92         reinterpret_cast<kmp_routine_entry_t>(omp_task_entry<4>), -1);
93 
94     shareds = reinterpret_cast<anon *>(task3->shareds);
95     shareds->data = &data;
96 
97     kmp_depend_info_t depinfo3;
98     depinfo3.base_addr = reinterpret_cast<intptr_t>(&data);
99     depinfo3.flags.in = 1;
100     depinfo3.flags.out = 1;
101     depinfo3.len = 4;
102 
103     __kmpc_omp_task_with_deps(nullptr, gtid, task3, 1, &depinfo3, 0, nullptr);
104 
105     // Task 4
106     auto task4 = __kmpc_omp_task_alloc(
107         nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
108         reinterpret_cast<kmp_routine_entry_t>(omp_task_entry<8>));
109 
110     shareds = reinterpret_cast<anon *>(task4->shareds);
111     shareds->data = &data;
112 
113     kmp_depend_info_t depinfo4;
114     depinfo4.base_addr = reinterpret_cast<intptr_t>(&data);
115     depinfo4.flags.in = 1;
116     depinfo4.flags.out = 1;
117     depinfo4.len = 4;
118 
119     __kmpc_omp_task_with_deps(nullptr, gtid, task4, 1, &depinfo4, 0, nullptr);
120 
121     // Wait for all tasks
122     __kmpc_omp_taskwait(nullptr, gtid);
123 
124     assert(data == 15);
125   }
126 
127   std::cout << "PASS\n";
128   return 0;
129 }
130 
131 // CHECK: PASS
132