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
20extern "C" {
21
22void __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
29namespace {
30omp_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
54void getTypeMismatch(omp_interop_property_t Property, int *Err) {
55 if (Err)
56 *Err = getPropertyErrorType(Property);
57}
58
59const 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
77template <typename PropertyTy>
78PropertyTy getProperty(omp_interop_val_t &InteropVal,
79 omp_interop_property_t Property, int *Err);
80
81template <>
82intptr_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
97template <>
98const 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
114template <>
115void *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
133bool 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 }
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
196static const char *copyErrorString(llvm::Error &&Err) {
197 // TODO: Use the error string while avoiding leaks.
198 std::string ErrMsg = llvm::toString(E: std::move(Err));
199 char *UsrMsg = reinterpret_cast<char *>(malloc(size: ErrMsg.size() + 1));
200 strcpy(dest: UsrMsg, src: ErrMsg.c_str());
201 return UsrMsg;
202}
203
204extern "C" {
205
206void __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 || !Device.RTL->init_device_info ||
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 || !Device.RTL->init_async_info ||
241 Device.RTL->init_async_info(DeviceId, &(InteropPtr)->async_info)) {
242 delete InteropPtr;
243 InteropPtr = omp_interop_none;
244 }
245 }
246}
247
248void __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
277void __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

source code of offload/src/OpenMP/InteropAPI.cpp