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 { |
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 | |
54 | void getTypeMismatch(omp_interop_property_t Property, int *Err) { |
55 | if (Err) |
56 | *Err = getPropertyErrorType(Property); |
57 | } |
58 | |
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 <> |
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 <> |
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 <> |
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 | |
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 | } |
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(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 | |
204 | extern "C" { |
205 | |
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 || !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 | |
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 | |
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 | |