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