1 //===---------------interop.cpp - Implementation of interop directive -----===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "interop.h"
10 #include "private.h"
11 
12 namespace {
13 omp_interop_rc_t getPropertyErrorType(omp_interop_property_t Property) {
14   switch (Property) {
15   case omp_ipr_fr_id:
16     return omp_irc_type_int;
17   case omp_ipr_fr_name:
18     return omp_irc_type_str;
19   case omp_ipr_vendor:
20     return omp_irc_type_int;
21   case omp_ipr_vendor_name:
22     return omp_irc_type_str;
23   case omp_ipr_device_num:
24     return omp_irc_type_int;
25   case omp_ipr_platform:
26     return omp_irc_type_int;
27   case omp_ipr_device:
28     return omp_irc_type_ptr;
29   case omp_ipr_device_context:
30     return omp_irc_type_ptr;
31   case omp_ipr_targetsync:
32     return omp_irc_type_ptr;
33   };
34   return omp_irc_no_value;
35 }
36 
37 void getTypeMismatch(omp_interop_property_t Property, int *Err) {
38   if (Err)
39     *Err = getPropertyErrorType(Property);
40 }
41 
42 const char *getVendorIdToStr(const omp_foreign_runtime_ids_t VendorId) {
43   switch (VendorId) {
44   case cuda:
45     return ("cuda");
46   case cuda_driver:
47     return ("cuda_driver");
48   case opencl:
49     return ("opencl");
50   case sycl:
51     return ("sycl");
52   case hip:
53     return ("hip");
54   case level_zero:
55     return ("level_zero");
56   }
57   return ("unknown");
58 }
59 
60 template <typename PropertyTy>
61 PropertyTy getProperty(omp_interop_val_t &InteropVal,
62                        omp_interop_property_t Property, int *Err);
63 
64 template <>
65 intptr_t getProperty<intptr_t>(omp_interop_val_t &interop_val,
66                                omp_interop_property_t property, int *err) {
67   switch (property) {
68   case omp_ipr_fr_id:
69     return interop_val.backend_type_id;
70   case omp_ipr_vendor:
71     return interop_val.vendor_id;
72   case omp_ipr_device_num:
73     return interop_val.device_id;
74   default:;
75   }
76   getTypeMismatch(property, err);
77   return 0;
78 }
79 
80 template <>
81 const char *getProperty<const char *>(omp_interop_val_t &interop_val,
82                                       omp_interop_property_t property,
83                                       int *err) {
84   switch (property) {
85   case omp_ipr_fr_id:
86     return interop_val.interop_type == kmp_interop_type_tasksync
87                ? "tasksync"
88                : "device+context";
89   case omp_ipr_vendor_name:
90     return getVendorIdToStr(interop_val.vendor_id);
91   default:
92     getTypeMismatch(property, err);
93     return nullptr;
94   }
95 }
96 
97 template <>
98 void *getProperty<void *>(omp_interop_val_t &interop_val,
99                           omp_interop_property_t property, int *err) {
100   switch (property) {
101   case omp_ipr_device:
102     if (interop_val.device_info.Device)
103       return interop_val.device_info.Device;
104     *err = omp_irc_no_value;
105     return const_cast<char *>(interop_val.err_str);
106   case omp_ipr_device_context:
107     return interop_val.device_info.Context;
108   case omp_ipr_targetsync:
109     return interop_val.async_info->Queue;
110   default:;
111   }
112   getTypeMismatch(property, err);
113   return nullptr;
114 }
115 
116 bool getPropertyCheck(omp_interop_val_t **interop_ptr,
117                       omp_interop_property_t property, int *err) {
118   if (err)
119     *err = omp_irc_success;
120   if (!interop_ptr) {
121     if (err)
122       *err = omp_irc_empty;
123     return false;
124   }
125   if (property >= 0 || property < omp_ipr_first) {
126     if (err)
127       *err = omp_irc_out_of_range;
128     return false;
129   }
130   if (property == omp_ipr_targetsync &&
131       (*interop_ptr)->interop_type != kmp_interop_type_tasksync) {
132     if (err)
133       *err = omp_irc_other;
134     return false;
135   }
136   if ((property == omp_ipr_device || property == omp_ipr_device_context) &&
137       (*interop_ptr)->interop_type == kmp_interop_type_tasksync) {
138     if (err)
139       *err = omp_irc_other;
140     return false;
141   }
142   return true;
143 }
144 
145 } // namespace
146 
147 #define __OMP_GET_INTEROP_TY(RETURN_TYPE, SUFFIX)                              \
148   RETURN_TYPE omp_get_interop_##SUFFIX(const omp_interop_t interop,            \
149                                        omp_interop_property_t property_id,     \
150                                        int *err) {                             \
151     omp_interop_val_t *interop_val = (omp_interop_val_t *)interop;             \
152     assert((interop_val)->interop_type == kmp_interop_type_tasksync);          \
153     if (!getPropertyCheck(&interop_val, property_id, err)) {                   \
154       return (RETURN_TYPE)(0);                                                 \
155     }                                                                          \
156     return getProperty<RETURN_TYPE>(*interop_val, property_id, err);           \
157   }
158 __OMP_GET_INTEROP_TY(intptr_t, int)
159 __OMP_GET_INTEROP_TY(void *, ptr)
160 __OMP_GET_INTEROP_TY(const char *, str)
161 #undef __OMP_GET_INTEROP_TY
162 
163 #define __OMP_GET_INTEROP_TY3(RETURN_TYPE, SUFFIX)                             \
164   RETURN_TYPE omp_get_interop_##SUFFIX(const omp_interop_t interop,            \
165                                        omp_interop_property_t property_id) {   \
166     int err;                                                                   \
167     omp_interop_val_t *interop_val = (omp_interop_val_t *)interop;             \
168     if (!getPropertyCheck(&interop_val, property_id, &err)) {                  \
169       return (RETURN_TYPE)(0);                                                 \
170     }                                                                          \
171     return nullptr;                                                            \
172     return getProperty<RETURN_TYPE>(*interop_val, property_id, &err);          \
173   }
174 __OMP_GET_INTEROP_TY3(const char *, name)
175 __OMP_GET_INTEROP_TY3(const char *, type_desc)
176 __OMP_GET_INTEROP_TY3(const char *, rc_desc)
177 #undef __OMP_GET_INTEROP_TY3
178 
179 typedef int64_t kmp_int64;
180 
181 #ifdef __cplusplus
182 extern "C" {
183 #endif
184 void __tgt_interop_init(ident_t *loc_ref, kmp_int32 gtid,
185                         omp_interop_val_t *&interop_ptr,
186                         kmp_interop_type_t interop_type, kmp_int32 device_id,
187                         kmp_int64 ndeps, kmp_depend_info_t *dep_list,
188                         kmp_int32 have_nowait) {
189   kmp_int32 ndeps_noalias = 0;
190   kmp_depend_info_t *noalias_dep_list = NULL;
191   assert(interop_type != kmp_interop_type_unknown &&
192          "Cannot initialize with unknown interop_type!");
193   if (device_id == -1) {
194     device_id = omp_get_default_device();
195   }
196 
197   if (interop_type == kmp_interop_type_tasksync) {
198     __kmpc_omp_wait_deps(loc_ref, gtid, ndeps, dep_list, ndeps_noalias,
199                          noalias_dep_list);
200   }
201 
202   interop_ptr = new omp_interop_val_t(device_id, interop_type);
203   if (!device_is_ready(device_id)) {
204     interop_ptr->err_str = "Device not ready!";
205     return;
206   }
207 
208   DeviceTy &Device = *PM->Devices[device_id];
209   if (!Device.RTL || !Device.RTL->init_device_info ||
210       Device.RTL->init_device_info(device_id, &(interop_ptr)->device_info,
211                                    &(interop_ptr)->err_str)) {
212     delete interop_ptr;
213     interop_ptr = omp_interop_none;
214   }
215   if (interop_type == kmp_interop_type_tasksync) {
216     if (!Device.RTL || !Device.RTL->init_async_info ||
217         Device.RTL->init_async_info(device_id, &(interop_ptr)->async_info)) {
218       delete interop_ptr;
219       interop_ptr = omp_interop_none;
220     }
221   }
222 }
223 
224 void __tgt_interop_use(ident_t *loc_ref, kmp_int32 gtid,
225                        omp_interop_val_t *&interop_ptr, kmp_int32 device_id,
226                        kmp_int32 ndeps, kmp_depend_info_t *dep_list,
227                        kmp_int32 have_nowait) {
228   kmp_int32 ndeps_noalias = 0;
229   kmp_depend_info_t *noalias_dep_list = NULL;
230   assert(interop_ptr && "Cannot use nullptr!");
231   omp_interop_val_t *interop_val = interop_ptr;
232   if (device_id == -1) {
233     device_id = omp_get_default_device();
234   }
235   assert(interop_val != omp_interop_none &&
236          "Cannot use uninitialized interop_ptr!");
237   assert((device_id == -1 || interop_val->device_id == device_id) &&
238          "Inconsistent device-id usage!");
239 
240   if (!device_is_ready(device_id)) {
241     interop_ptr->err_str = "Device not ready!";
242     return;
243   }
244 
245   if (interop_val->interop_type == kmp_interop_type_tasksync) {
246     __kmpc_omp_wait_deps(loc_ref, gtid, ndeps, dep_list, ndeps_noalias,
247                          noalias_dep_list);
248   }
249   // TODO Flush the queue associated with the interop through the plugin
250 }
251 
252 void __tgt_interop_destroy(ident_t *loc_ref, kmp_int32 gtid,
253                            omp_interop_val_t *&interop_ptr, kmp_int32 device_id,
254                            kmp_int32 ndeps, kmp_depend_info_t *dep_list,
255                            kmp_int32 have_nowait) {
256   kmp_int32 ndeps_noalias = 0;
257   kmp_depend_info_t *noalias_dep_list = NULL;
258   assert(interop_ptr && "Cannot use nullptr!");
259   omp_interop_val_t *interop_val = interop_ptr;
260   if (device_id == -1) {
261     device_id = omp_get_default_device();
262   }
263 
264   if (interop_val == omp_interop_none)
265     return;
266 
267   assert((device_id == -1 || interop_val->device_id == device_id) &&
268          "Inconsistent device-id usage!");
269   if (!device_is_ready(device_id)) {
270     interop_ptr->err_str = "Device not ready!";
271     return;
272   }
273 
274   if (interop_val->interop_type == kmp_interop_type_tasksync) {
275     __kmpc_omp_wait_deps(loc_ref, gtid, ndeps, dep_list, ndeps_noalias,
276                          noalias_dep_list);
277   }
278   // TODO Flush the queue associated with the interop through the plugin
279   // TODO Signal out dependences
280 
281   delete interop_ptr;
282   interop_ptr = omp_interop_none;
283 }
284 #ifdef __cplusplus
285 } // extern "C"
286 #endif
287