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 {
getPropertyErrorType(omp_interop_property_t Property)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 
getTypeMismatch(omp_interop_property_t Property,int * Err)37 void getTypeMismatch(omp_interop_property_t Property, int *Err) {
38   if (Err)
39     *Err = getPropertyErrorType(Property);
40 }
41 
getVendorIdToStr(const omp_foreign_runtime_ids_t VendorId)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 <>
getProperty(omp_interop_val_t & InteropVal,omp_interop_property_t Property,int * Err)65 intptr_t getProperty<intptr_t>(omp_interop_val_t &InteropVal,
66                                omp_interop_property_t Property, int *Err) {
67   switch (Property) {
68   case omp_ipr_fr_id:
69     return InteropVal.backend_type_id;
70   case omp_ipr_vendor:
71     return InteropVal.vendor_id;
72   case omp_ipr_device_num:
73     return InteropVal.device_id;
74   default:;
75   }
76   getTypeMismatch(Property, Err);
77   return 0;
78 }
79 
80 template <>
getProperty(omp_interop_val_t & InteropVal,omp_interop_property_t Property,int * Err)81 const char *getProperty<const char *>(omp_interop_val_t &InteropVal,
82                                       omp_interop_property_t Property,
83                                       int *Err) {
84   switch (Property) {
85   case omp_ipr_fr_id:
86     return InteropVal.interop_type == kmp_interop_type_tasksync
87                ? "tasksync"
88                : "device+context";
89   case omp_ipr_vendor_name:
90     return getVendorIdToStr(InteropVal.vendor_id);
91   default:
92     getTypeMismatch(Property, Err);
93     return nullptr;
94   }
95 }
96 
97 template <>
getProperty(omp_interop_val_t & InteropVal,omp_interop_property_t Property,int * Err)98 void *getProperty<void *>(omp_interop_val_t &InteropVal,
99                           omp_interop_property_t Property, int *Err) {
100   switch (Property) {
101   case omp_ipr_device:
102     if (InteropVal.device_info.Device)
103       return InteropVal.device_info.Device;
104     *Err = omp_irc_no_value;
105     return const_cast<char *>(InteropVal.err_str);
106   case omp_ipr_device_context:
107     return InteropVal.device_info.Context;
108   case omp_ipr_targetsync:
109     return InteropVal.async_info->Queue;
110   default:;
111   }
112   getTypeMismatch(Property, Err);
113   return nullptr;
114 }
115 
getPropertyCheck(omp_interop_val_t ** InteropPtr,omp_interop_property_t Property,int * Err)116 bool getPropertyCheck(omp_interop_val_t **InteropPtr,
117                       omp_interop_property_t Property, int *Err) {
118   if (Err)
119     *Err = omp_irc_success;
120   if (!InteropPtr) {
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       (*InteropPtr)->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       (*InteropPtr)->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
__tgt_interop_init(ident_t * LocRef,kmp_int32 Gtid,omp_interop_val_t * & InteropPtr,kmp_interop_type_t InteropType,kmp_int32 DeviceId,kmp_int64 Ndeps,kmp_depend_info_t * DepList,kmp_int32 HaveNowait)184 void __tgt_interop_init(ident_t *LocRef, kmp_int32 Gtid,
185                         omp_interop_val_t *&InteropPtr,
186                         kmp_interop_type_t InteropType, kmp_int32 DeviceId,
187                         kmp_int64 Ndeps, kmp_depend_info_t *DepList,
188                         kmp_int32 HaveNowait) {
189   kmp_int32 NdepsNoalias = 0;
190   kmp_depend_info_t *NoaliasDepList = NULL;
191   assert(InteropType != kmp_interop_type_unknown &&
192          "Cannot initialize with unknown interop_type!");
193   if (DeviceId == -1) {
194     DeviceId = omp_get_default_device();
195   }
196 
197   if (InteropType == kmp_interop_type_tasksync) {
198     __kmpc_omp_wait_deps(LocRef, Gtid, Ndeps, DepList, NdepsNoalias,
199                          NoaliasDepList);
200   }
201 
202   InteropPtr = new omp_interop_val_t(DeviceId, InteropType);
203   if (!deviceIsReady(DeviceId)) {
204     InteropPtr->err_str = "Device not ready!";
205     return;
206   }
207 
208   DeviceTy &Device = *PM->Devices[DeviceId];
209   if (!Device.RTL || !Device.RTL->init_device_info ||
210       Device.RTL->init_device_info(DeviceId, &(InteropPtr)->device_info,
211                                    &(InteropPtr)->err_str)) {
212     delete InteropPtr;
213     InteropPtr = omp_interop_none;
214   }
215   if (InteropType == kmp_interop_type_tasksync) {
216     if (!Device.RTL || !Device.RTL->init_async_info ||
217         Device.RTL->init_async_info(DeviceId, &(InteropPtr)->async_info)) {
218       delete InteropPtr;
219       InteropPtr = omp_interop_none;
220     }
221   }
222 }
223 
__tgt_interop_use(ident_t * LocRef,kmp_int32 Gtid,omp_interop_val_t * & InteropPtr,kmp_int32 DeviceId,kmp_int32 Ndeps,kmp_depend_info_t * DepList,kmp_int32 HaveNowait)224 void __tgt_interop_use(ident_t *LocRef, kmp_int32 Gtid,
225                        omp_interop_val_t *&InteropPtr, kmp_int32 DeviceId,
226                        kmp_int32 Ndeps, kmp_depend_info_t *DepList,
227                        kmp_int32 HaveNowait) {
228   kmp_int32 NdepsNoalias = 0;
229   kmp_depend_info_t *NoaliasDepList = NULL;
230   assert(InteropPtr && "Cannot use nullptr!");
231   omp_interop_val_t *InteropVal = InteropPtr;
232   if (DeviceId == -1) {
233     DeviceId = omp_get_default_device();
234   }
235   assert(InteropVal != omp_interop_none &&
236          "Cannot use uninitialized interop_ptr!");
237   assert((DeviceId == -1 || InteropVal->device_id == DeviceId) &&
238          "Inconsistent device-id usage!");
239 
240   if (!deviceIsReady(DeviceId)) {
241     InteropPtr->err_str = "Device not ready!";
242     return;
243   }
244 
245   if (InteropVal->interop_type == kmp_interop_type_tasksync) {
246     __kmpc_omp_wait_deps(LocRef, Gtid, Ndeps, DepList, NdepsNoalias,
247                          NoaliasDepList);
248   }
249   // TODO Flush the queue associated with the interop through the plugin
250 }
251 
__tgt_interop_destroy(ident_t * LocRef,kmp_int32 Gtid,omp_interop_val_t * & InteropPtr,kmp_int32 DeviceId,kmp_int32 Ndeps,kmp_depend_info_t * DepList,kmp_int32 HaveNowait)252 void __tgt_interop_destroy(ident_t *LocRef, kmp_int32 Gtid,
253                            omp_interop_val_t *&InteropPtr, kmp_int32 DeviceId,
254                            kmp_int32 Ndeps, kmp_depend_info_t *DepList,
255                            kmp_int32 HaveNowait) {
256   kmp_int32 NdepsNoalias = 0;
257   kmp_depend_info_t *NoaliasDepList = NULL;
258   assert(InteropPtr && "Cannot use nullptr!");
259   omp_interop_val_t *InteropVal = InteropPtr;
260   if (DeviceId == -1) {
261     DeviceId = omp_get_default_device();
262   }
263 
264   if (InteropVal == omp_interop_none)
265     return;
266 
267   assert((DeviceId == -1 || InteropVal->device_id == DeviceId) &&
268          "Inconsistent device-id usage!");
269   if (!deviceIsReady(DeviceId)) {
270     InteropPtr->err_str = "Device not ready!";
271     return;
272   }
273 
274   if (InteropVal->interop_type == kmp_interop_type_tasksync) {
275     __kmpc_omp_wait_deps(LocRef, Gtid, Ndeps, DepList, NdepsNoalias,
276                          NoaliasDepList);
277   }
278   // TODO Flush the queue associated with the interop through the plugin
279   // TODO Signal out dependences
280 
281   delete InteropPtr;
282   InteropPtr = omp_interop_none;
283 }
284 #ifdef __cplusplus
285 } // extern "C"
286 #endif
287