//===-- InteropAPI.cpp - Implementation of OpenMP interoperability API ----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "OpenMP/InteropAPI.h" #include "OpenMP/InternalTypes.h" #include "OpenMP/omp.h" #include "PluginManager.h" #include "device.h" #include "omptarget.h" #include "llvm/Support/Error.h" #include #include extern "C" { void __kmpc_omp_wait_deps(ident_t *loc_ref, int32_t gtid, int32_t ndeps, kmp_depend_info_t *dep_list, int32_t ndeps_noalias, kmp_depend_info_t *noalias_dep_list) __attribute__((weak)); } // extern "C" namespace { omp_interop_rc_t getPropertyErrorType(omp_interop_property_t Property) { switch (Property) { case omp_ipr_fr_id: return omp_irc_type_int; case omp_ipr_fr_name: return omp_irc_type_str; case omp_ipr_vendor: return omp_irc_type_int; case omp_ipr_vendor_name: return omp_irc_type_str; case omp_ipr_device_num: return omp_irc_type_int; case omp_ipr_platform: return omp_irc_type_int; case omp_ipr_device: return omp_irc_type_ptr; case omp_ipr_device_context: return omp_irc_type_ptr; case omp_ipr_targetsync: return omp_irc_type_ptr; }; return omp_irc_no_value; } void getTypeMismatch(omp_interop_property_t Property, int *Err) { if (Err) *Err = getPropertyErrorType(Property); } const char *getVendorIdToStr(const omp_foreign_runtime_ids_t VendorId) { switch (VendorId) { case cuda: return ("cuda"); case cuda_driver: return ("cuda_driver"); case opencl: return ("opencl"); case sycl: return ("sycl"); case hip: return ("hip"); case level_zero: return ("level_zero"); } return ("unknown"); } template PropertyTy getProperty(omp_interop_val_t &InteropVal, omp_interop_property_t Property, int *Err); template <> intptr_t getProperty(omp_interop_val_t &InteropVal, omp_interop_property_t Property, int *Err) { switch (Property) { case omp_ipr_fr_id: return InteropVal.backend_type_id; case omp_ipr_vendor: return InteropVal.vendor_id; case omp_ipr_device_num: return InteropVal.device_id; default:; } getTypeMismatch(Property, Err); return 0; } template <> const char *getProperty(omp_interop_val_t &InteropVal, omp_interop_property_t Property, int *Err) { switch (Property) { case omp_ipr_fr_id: return InteropVal.interop_type == kmp_interop_type_tasksync ? "tasksync" : "device+context"; case omp_ipr_vendor_name: return getVendorIdToStr(InteropVal.vendor_id); default: getTypeMismatch(Property, Err); return nullptr; } } template <> void *getProperty(omp_interop_val_t &InteropVal, omp_interop_property_t Property, int *Err) { switch (Property) { case omp_ipr_device: if (InteropVal.device_info.Device) return InteropVal.device_info.Device; *Err = omp_irc_no_value; return const_cast(InteropVal.err_str); case omp_ipr_device_context: return InteropVal.device_info.Context; case omp_ipr_targetsync: return InteropVal.async_info->Queue; default:; } getTypeMismatch(Property, Err); return nullptr; } bool getPropertyCheck(omp_interop_val_t **InteropPtr, omp_interop_property_t Property, int *Err) { if (Err) *Err = omp_irc_success; if (!InteropPtr) { if (Err) *Err = omp_irc_empty; return false; } if (Property >= 0 || Property < omp_ipr_first) { if (Err) *Err = omp_irc_out_of_range; return false; } if (Property == omp_ipr_targetsync && (*InteropPtr)->interop_type != kmp_interop_type_tasksync) { if (Err) *Err = omp_irc_other; return false; } if ((Property == omp_ipr_device || Property == omp_ipr_device_context) && (*InteropPtr)->interop_type == kmp_interop_type_tasksync) { if (Err) *Err = omp_irc_other; return false; } return true; } } // namespace #define __OMP_GET_INTEROP_TY(RETURN_TYPE, SUFFIX) \ RETURN_TYPE omp_get_interop_##SUFFIX(const omp_interop_t interop, \ omp_interop_property_t property_id, \ int *err) { \ omp_interop_val_t *interop_val = (omp_interop_val_t *)interop; \ assert((interop_val)->interop_type == kmp_interop_type_tasksync); \ if (!getPropertyCheck(&interop_val, property_id, err)) { \ return (RETURN_TYPE)(0); \ } \ return getProperty(*interop_val, property_id, err); \ } __OMP_GET_INTEROP_TY(intptr_t, int) __OMP_GET_INTEROP_TY(void *, ptr) __OMP_GET_INTEROP_TY(const char *, str) #undef __OMP_GET_INTEROP_TY #define __OMP_GET_INTEROP_TY3(RETURN_TYPE, SUFFIX) \ RETURN_TYPE omp_get_interop_##SUFFIX(const omp_interop_t interop, \ omp_interop_property_t property_id) { \ int err; \ omp_interop_val_t *interop_val = (omp_interop_val_t *)interop; \ if (!getPropertyCheck(&interop_val, property_id, &err)) { \ return (RETURN_TYPE)(0); \ } \ return nullptr; \ return getProperty(*interop_val, property_id, &err); \ } __OMP_GET_INTEROP_TY3(const char *, name) __OMP_GET_INTEROP_TY3(const char *, type_desc) __OMP_GET_INTEROP_TY3(const char *, rc_desc) #undef __OMP_GET_INTEROP_TY3 static const char *copyErrorString(llvm::Error &&Err) { // TODO: Use the error string while avoiding leaks. std::string ErrMsg = llvm::toString(std::move(Err)); char *UsrMsg = reinterpret_cast(malloc(ErrMsg.size() + 1)); strcpy(UsrMsg, ErrMsg.c_str()); return UsrMsg; } extern "C" { void __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) { int32_t NdepsNoalias = 0; kmp_depend_info_t *NoaliasDepList = NULL; assert(InteropType != kmp_interop_type_unknown && "Cannot initialize with unknown interop_type!"); if (DeviceId == -1) { DeviceId = omp_get_default_device(); } if (InteropType == kmp_interop_type_tasksync) { __kmpc_omp_wait_deps(LocRef, Gtid, Ndeps, DepList, NdepsNoalias, NoaliasDepList); } InteropPtr = new omp_interop_val_t(DeviceId, InteropType); auto DeviceOrErr = PM->getDevice(DeviceId); if (!DeviceOrErr) { InteropPtr->err_str = copyErrorString(DeviceOrErr.takeError()); return; } DeviceTy &Device = *DeviceOrErr; if (!Device.RTL || Device.RTL->init_device_info(DeviceId, &(InteropPtr)->device_info, &(InteropPtr)->err_str)) { delete InteropPtr; InteropPtr = omp_interop_none; } if (InteropType == kmp_interop_type_tasksync) { if (!Device.RTL || Device.RTL->init_async_info(DeviceId, &(InteropPtr)->async_info)) { delete InteropPtr; InteropPtr = omp_interop_none; } } } void __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) { int32_t NdepsNoalias = 0; kmp_depend_info_t *NoaliasDepList = NULL; assert(InteropPtr && "Cannot use nullptr!"); omp_interop_val_t *InteropVal = InteropPtr; if (DeviceId == -1) { DeviceId = omp_get_default_device(); } assert(InteropVal != omp_interop_none && "Cannot use uninitialized interop_ptr!"); assert((DeviceId == -1 || InteropVal->device_id == DeviceId) && "Inconsistent device-id usage!"); auto DeviceOrErr = PM->getDevice(DeviceId); if (!DeviceOrErr) { InteropPtr->err_str = copyErrorString(DeviceOrErr.takeError()); return; } if (InteropVal->interop_type == kmp_interop_type_tasksync) { __kmpc_omp_wait_deps(LocRef, Gtid, Ndeps, DepList, NdepsNoalias, NoaliasDepList); } // TODO Flush the queue associated with the interop through the plugin } void __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) { int32_t NdepsNoalias = 0; kmp_depend_info_t *NoaliasDepList = NULL; assert(InteropPtr && "Cannot use nullptr!"); omp_interop_val_t *InteropVal = InteropPtr; if (DeviceId == -1) { DeviceId = omp_get_default_device(); } if (InteropVal == omp_interop_none) return; assert((DeviceId == -1 || InteropVal->device_id == DeviceId) && "Inconsistent device-id usage!"); auto DeviceOrErr = PM->getDevice(DeviceId); if (!DeviceOrErr) { InteropPtr->err_str = copyErrorString(DeviceOrErr.takeError()); return; } if (InteropVal->interop_type == kmp_interop_type_tasksync) { __kmpc_omp_wait_deps(LocRef, Gtid, Ndeps, DepList, NdepsNoalias, NoaliasDepList); } // TODO Flush the queue associated with the interop through the plugin // TODO Signal out dependences delete InteropPtr; InteropPtr = omp_interop_none; } } // extern "C"