xref: /llvm-project/offload/src/OpenMP/InteropAPI.cpp (revision fa9e90f5d23312587b3a17920941334e0d1a58a1)
1 //===-- InteropAPI.cpp - Implementation of OpenMP interoperability API ----===//
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 "OpenMP/InteropAPI.h"
10 #include "OpenMP/InternalTypes.h"
11 #include "OpenMP/omp.h"
12 
13 #include "PluginManager.h"
14 #include "device.h"
15 #include "omptarget.h"
16 #include "llvm/Support/Error.h"
17 #include <cstdlib>
18 #include <cstring>
19 
20 extern "C" {
21 
22 void __kmpc_omp_wait_deps(ident_t *loc_ref, int32_t gtid, int32_t ndeps,
23                           kmp_depend_info_t *dep_list, int32_t ndeps_noalias,
24                           kmp_depend_info_t *noalias_dep_list)
25     __attribute__((weak));
26 
27 } // extern "C"
28 
29 namespace {
getPropertyErrorType(omp_interop_property_t Property)30 omp_interop_rc_t getPropertyErrorType(omp_interop_property_t Property) {
31   switch (Property) {
32   case omp_ipr_fr_id:
33     return omp_irc_type_int;
34   case omp_ipr_fr_name:
35     return omp_irc_type_str;
36   case omp_ipr_vendor:
37     return omp_irc_type_int;
38   case omp_ipr_vendor_name:
39     return omp_irc_type_str;
40   case omp_ipr_device_num:
41     return omp_irc_type_int;
42   case omp_ipr_platform:
43     return omp_irc_type_int;
44   case omp_ipr_device:
45     return omp_irc_type_ptr;
46   case omp_ipr_device_context:
47     return omp_irc_type_ptr;
48   case omp_ipr_targetsync:
49     return omp_irc_type_ptr;
50   };
51   return omp_irc_no_value;
52 }
53 
getTypeMismatch(omp_interop_property_t Property,int * Err)54 void getTypeMismatch(omp_interop_property_t Property, int *Err) {
55   if (Err)
56     *Err = getPropertyErrorType(Property);
57 }
58 
getVendorIdToStr(const omp_foreign_runtime_ids_t VendorId)59 const char *getVendorIdToStr(const omp_foreign_runtime_ids_t VendorId) {
60   switch (VendorId) {
61   case cuda:
62     return ("cuda");
63   case cuda_driver:
64     return ("cuda_driver");
65   case opencl:
66     return ("opencl");
67   case sycl:
68     return ("sycl");
69   case hip:
70     return ("hip");
71   case level_zero:
72     return ("level_zero");
73   }
74   return ("unknown");
75 }
76 
77 template <typename PropertyTy>
78 PropertyTy getProperty(omp_interop_val_t &InteropVal,
79                        omp_interop_property_t Property, int *Err);
80 
81 template <>
getProperty(omp_interop_val_t & InteropVal,omp_interop_property_t Property,int * Err)82 intptr_t getProperty<intptr_t>(omp_interop_val_t &InteropVal,
83                                omp_interop_property_t Property, int *Err) {
84   switch (Property) {
85   case omp_ipr_fr_id:
86     return InteropVal.backend_type_id;
87   case omp_ipr_vendor:
88     return InteropVal.vendor_id;
89   case omp_ipr_device_num:
90     return InteropVal.device_id;
91   default:;
92   }
93   getTypeMismatch(Property, Err);
94   return 0;
95 }
96 
97 template <>
getProperty(omp_interop_val_t & InteropVal,omp_interop_property_t Property,int * Err)98 const char *getProperty<const char *>(omp_interop_val_t &InteropVal,
99                                       omp_interop_property_t Property,
100                                       int *Err) {
101   switch (Property) {
102   case omp_ipr_fr_id:
103     return InteropVal.interop_type == kmp_interop_type_tasksync
104                ? "tasksync"
105                : "device+context";
106   case omp_ipr_vendor_name:
107     return getVendorIdToStr(InteropVal.vendor_id);
108   default:
109     getTypeMismatch(Property, Err);
110     return nullptr;
111   }
112 }
113 
114 template <>
getProperty(omp_interop_val_t & InteropVal,omp_interop_property_t Property,int * Err)115 void *getProperty<void *>(omp_interop_val_t &InteropVal,
116                           omp_interop_property_t Property, int *Err) {
117   switch (Property) {
118   case omp_ipr_device:
119     if (InteropVal.device_info.Device)
120       return InteropVal.device_info.Device;
121     *Err = omp_irc_no_value;
122     return const_cast<char *>(InteropVal.err_str);
123   case omp_ipr_device_context:
124     return InteropVal.device_info.Context;
125   case omp_ipr_targetsync:
126     return InteropVal.async_info->Queue;
127   default:;
128   }
129   getTypeMismatch(Property, Err);
130   return nullptr;
131 }
132 
getPropertyCheck(omp_interop_val_t ** InteropPtr,omp_interop_property_t Property,int * Err)133 bool getPropertyCheck(omp_interop_val_t **InteropPtr,
134                       omp_interop_property_t Property, int *Err) {
135   if (Err)
136     *Err = omp_irc_success;
137   if (!InteropPtr) {
138     if (Err)
139       *Err = omp_irc_empty;
140     return false;
141   }
142   if (Property >= 0 || Property < omp_ipr_first) {
143     if (Err)
144       *Err = omp_irc_out_of_range;
145     return false;
146   }
147   if (Property == omp_ipr_targetsync &&
148       (*InteropPtr)->interop_type != kmp_interop_type_tasksync) {
149     if (Err)
150       *Err = omp_irc_other;
151     return false;
152   }
153   if ((Property == omp_ipr_device || Property == omp_ipr_device_context) &&
154       (*InteropPtr)->interop_type == kmp_interop_type_tasksync) {
155     if (Err)
156       *Err = omp_irc_other;
157     return false;
158   }
159   return true;
160 }
161 
162 } // namespace
163 
164 #define __OMP_GET_INTEROP_TY(RETURN_TYPE, SUFFIX)                              \
165   RETURN_TYPE omp_get_interop_##SUFFIX(const omp_interop_t interop,            \
166                                        omp_interop_property_t property_id,     \
167                                        int *err) {                             \
168     omp_interop_val_t *interop_val = (omp_interop_val_t *)interop;             \
169     assert((interop_val)->interop_type == kmp_interop_type_tasksync);          \
170     if (!getPropertyCheck(&interop_val, property_id, err)) {                   \
171       return (RETURN_TYPE)(0);                                                 \
172     }                                                                          \
173     return getProperty<RETURN_TYPE>(*interop_val, property_id, err);           \
174   }
__OMP_GET_INTEROP_TY(intptr_t,int)175 __OMP_GET_INTEROP_TY(intptr_t, int)
176 __OMP_GET_INTEROP_TY(void *, ptr)
177 __OMP_GET_INTEROP_TY(const char *, str)
178 #undef __OMP_GET_INTEROP_TY
179 
180 #define __OMP_GET_INTEROP_TY3(RETURN_TYPE, SUFFIX)                             \
181   RETURN_TYPE omp_get_interop_##SUFFIX(const omp_interop_t interop,            \
182                                        omp_interop_property_t property_id) {   \
183     int err;                                                                   \
184     omp_interop_val_t *interop_val = (omp_interop_val_t *)interop;             \
185     if (!getPropertyCheck(&interop_val, property_id, &err)) {                  \
186       return (RETURN_TYPE)(0);                                                 \
187     }                                                                          \
188     return nullptr;                                                            \
189     return getProperty<RETURN_TYPE>(*interop_val, property_id, &err);          \
190   }
191 __OMP_GET_INTEROP_TY3(const char *, name)
192 __OMP_GET_INTEROP_TY3(const char *, type_desc)
193 __OMP_GET_INTEROP_TY3(const char *, rc_desc)
194 #undef __OMP_GET_INTEROP_TY3
195 
196 static const char *copyErrorString(llvm::Error &&Err) {
197   // TODO: Use the error string while avoiding leaks.
198   std::string ErrMsg = llvm::toString(std::move(Err));
199   char *UsrMsg = reinterpret_cast<char *>(malloc(ErrMsg.size() + 1));
200   strcpy(UsrMsg, ErrMsg.c_str());
201   return UsrMsg;
202 }
203 
204 extern "C" {
205 
__tgt_interop_init(ident_t * LocRef,int32_t Gtid,omp_interop_val_t * & InteropPtr,kmp_interop_type_t InteropType,int32_t DeviceId,int32_t Ndeps,kmp_depend_info_t * DepList,int32_t HaveNowait)206 void __tgt_interop_init(ident_t *LocRef, int32_t Gtid,
207                         omp_interop_val_t *&InteropPtr,
208                         kmp_interop_type_t InteropType, int32_t DeviceId,
209                         int32_t Ndeps, kmp_depend_info_t *DepList,
210                         int32_t HaveNowait) {
211   int32_t NdepsNoalias = 0;
212   kmp_depend_info_t *NoaliasDepList = NULL;
213   assert(InteropType != kmp_interop_type_unknown &&
214          "Cannot initialize with unknown interop_type!");
215   if (DeviceId == -1) {
216     DeviceId = omp_get_default_device();
217   }
218 
219   if (InteropType == kmp_interop_type_tasksync) {
220     __kmpc_omp_wait_deps(LocRef, Gtid, Ndeps, DepList, NdepsNoalias,
221                          NoaliasDepList);
222   }
223 
224   InteropPtr = new omp_interop_val_t(DeviceId, InteropType);
225 
226   auto DeviceOrErr = PM->getDevice(DeviceId);
227   if (!DeviceOrErr) {
228     InteropPtr->err_str = copyErrorString(DeviceOrErr.takeError());
229     return;
230   }
231 
232   DeviceTy &Device = *DeviceOrErr;
233   if (!Device.RTL ||
234       Device.RTL->init_device_info(DeviceId, &(InteropPtr)->device_info,
235                                    &(InteropPtr)->err_str)) {
236     delete InteropPtr;
237     InteropPtr = omp_interop_none;
238   }
239   if (InteropType == kmp_interop_type_tasksync) {
240     if (!Device.RTL ||
241         Device.RTL->init_async_info(DeviceId, &(InteropPtr)->async_info)) {
242       delete InteropPtr;
243       InteropPtr = omp_interop_none;
244     }
245   }
246 }
247 
__tgt_interop_use(ident_t * LocRef,int32_t Gtid,omp_interop_val_t * & InteropPtr,int32_t DeviceId,int32_t Ndeps,kmp_depend_info_t * DepList,int32_t HaveNowait)248 void __tgt_interop_use(ident_t *LocRef, int32_t Gtid,
249                        omp_interop_val_t *&InteropPtr, int32_t DeviceId,
250                        int32_t Ndeps, kmp_depend_info_t *DepList,
251                        int32_t HaveNowait) {
252   int32_t NdepsNoalias = 0;
253   kmp_depend_info_t *NoaliasDepList = NULL;
254   assert(InteropPtr && "Cannot use nullptr!");
255   omp_interop_val_t *InteropVal = InteropPtr;
256   if (DeviceId == -1) {
257     DeviceId = omp_get_default_device();
258   }
259   assert(InteropVal != omp_interop_none &&
260          "Cannot use uninitialized interop_ptr!");
261   assert((DeviceId == -1 || InteropVal->device_id == DeviceId) &&
262          "Inconsistent device-id usage!");
263 
264   auto DeviceOrErr = PM->getDevice(DeviceId);
265   if (!DeviceOrErr) {
266     InteropPtr->err_str = copyErrorString(DeviceOrErr.takeError());
267     return;
268   }
269 
270   if (InteropVal->interop_type == kmp_interop_type_tasksync) {
271     __kmpc_omp_wait_deps(LocRef, Gtid, Ndeps, DepList, NdepsNoalias,
272                          NoaliasDepList);
273   }
274   // TODO Flush the queue associated with the interop through the plugin
275 }
276 
__tgt_interop_destroy(ident_t * LocRef,int32_t Gtid,omp_interop_val_t * & InteropPtr,int32_t DeviceId,int32_t Ndeps,kmp_depend_info_t * DepList,int32_t HaveNowait)277 void __tgt_interop_destroy(ident_t *LocRef, int32_t Gtid,
278                            omp_interop_val_t *&InteropPtr, int32_t DeviceId,
279                            int32_t Ndeps, kmp_depend_info_t *DepList,
280                            int32_t HaveNowait) {
281   int32_t NdepsNoalias = 0;
282   kmp_depend_info_t *NoaliasDepList = NULL;
283   assert(InteropPtr && "Cannot use nullptr!");
284   omp_interop_val_t *InteropVal = InteropPtr;
285   if (DeviceId == -1) {
286     DeviceId = omp_get_default_device();
287   }
288 
289   if (InteropVal == omp_interop_none)
290     return;
291 
292   assert((DeviceId == -1 || InteropVal->device_id == DeviceId) &&
293          "Inconsistent device-id usage!");
294   auto DeviceOrErr = PM->getDevice(DeviceId);
295   if (!DeviceOrErr) {
296     InteropPtr->err_str = copyErrorString(DeviceOrErr.takeError());
297     return;
298   }
299 
300   if (InteropVal->interop_type == kmp_interop_type_tasksync) {
301     __kmpc_omp_wait_deps(LocRef, Gtid, Ndeps, DepList, NdepsNoalias,
302                          NoaliasDepList);
303   }
304   // TODO Flush the queue associated with the interop through the plugin
305   // TODO Signal out dependences
306 
307   delete InteropPtr;
308   InteropPtr = omp_interop_none;
309 }
310 
311 } // extern "C"
312