1 //===------ omptarget.cpp - Target independent OpenMP target RTL -- C++ -*-===// 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 // Implementation of the interface to be used by Clang during the codegen of a 10 // target region. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "device.h" 15 #include "private.h" 16 #include "rtl.h" 17 18 #include <cassert> 19 #include <vector> 20 21 /* All begin addresses for partially mapped structs must be 8-aligned in order 22 * to ensure proper alignment of members. E.g. 23 * 24 * struct S { 25 * int a; // 4-aligned 26 * int b; // 4-aligned 27 * int *p; // 8-aligned 28 * } s1; 29 * ... 30 * #pragma omp target map(tofrom: s1.b, s1.p[0:N]) 31 * { 32 * s1.b = 5; 33 * for (int i...) s1.p[i] = ...; 34 * } 35 * 36 * Here we are mapping s1 starting from member b, so BaseAddress=&s1=&s1.a and 37 * BeginAddress=&s1.b. Let's assume that the struct begins at address 0x100, 38 * then &s1.a=0x100, &s1.b=0x104, &s1.p=0x108. Each member obeys the alignment 39 * requirements for its type. Now, when we allocate memory on the device, in 40 * CUDA's case cuMemAlloc() returns an address which is at least 256-aligned. 41 * This means that the chunk of the struct on the device will start at a 42 * 256-aligned address, let's say 0x200. Then the address of b will be 0x200 and 43 * address of p will be a misaligned 0x204 (on the host there was no need to add 44 * padding between b and p, so p comes exactly 4 bytes after b). If the device 45 * kernel tries to access s1.p, a misaligned address error occurs (as reported 46 * by the CUDA plugin). By padding the begin address down to a multiple of 8 and 47 * extending the size of the allocated chuck accordingly, the chuck on the 48 * device will start at 0x200 with the padding (4 bytes), then &s1.b=0x204 and 49 * &s1.p=0x208, as they should be to satisfy the alignment requirements. 50 */ 51 static const int64_t Alignment = 8; 52 53 /// Map global data and execute pending ctors 54 static int InitLibrary(DeviceTy &Device) { 55 /* 56 * Map global data 57 */ 58 int32_t device_id = Device.DeviceID; 59 int rc = OFFLOAD_SUCCESS; 60 61 Device.PendingGlobalsMtx.lock(); 62 PM->TrlTblMtx.lock(); 63 for (HostEntriesBeginToTransTableTy::iterator entry_it = 64 PM->HostEntriesBeginToTransTable.begin(); 65 entry_it != PM->HostEntriesBeginToTransTable.end(); ++entry_it) { 66 TranslationTable *TransTable = &entry_it->second; 67 if (TransTable->HostTable.EntriesBegin == 68 TransTable->HostTable.EntriesEnd) { 69 // No host entry so no need to proceed 70 continue; 71 } 72 if (TransTable->TargetsTable[device_id] != 0) { 73 // Library entries have already been processed 74 continue; 75 } 76 77 // 1) get image. 78 assert(TransTable->TargetsImages.size() > (size_t)device_id && 79 "Not expecting a device ID outside the table's bounds!"); 80 __tgt_device_image *img = TransTable->TargetsImages[device_id]; 81 if (!img) { 82 REPORT("No image loaded for device id %d.\n", device_id); 83 rc = OFFLOAD_FAIL; 84 break; 85 } 86 // 2) load image into the target table. 87 __tgt_target_table *TargetTable = TransTable->TargetsTable[device_id] = 88 Device.load_binary(img); 89 // Unable to get table for this image: invalidate image and fail. 90 if (!TargetTable) { 91 REPORT("Unable to generate entries table for device id %d.\n", device_id); 92 TransTable->TargetsImages[device_id] = 0; 93 rc = OFFLOAD_FAIL; 94 break; 95 } 96 97 // Verify whether the two table sizes match. 98 size_t hsize = 99 TransTable->HostTable.EntriesEnd - TransTable->HostTable.EntriesBegin; 100 size_t tsize = TargetTable->EntriesEnd - TargetTable->EntriesBegin; 101 102 // Invalid image for these host entries! 103 if (hsize != tsize) { 104 REPORT("Host and Target tables mismatch for device id %d [%zx != %zx].\n", 105 device_id, hsize, tsize); 106 TransTable->TargetsImages[device_id] = 0; 107 TransTable->TargetsTable[device_id] = 0; 108 rc = OFFLOAD_FAIL; 109 break; 110 } 111 112 // process global data that needs to be mapped. 113 Device.DataMapMtx.lock(); 114 __tgt_target_table *HostTable = &TransTable->HostTable; 115 for (__tgt_offload_entry *CurrDeviceEntry = TargetTable->EntriesBegin, 116 *CurrHostEntry = HostTable->EntriesBegin, 117 *EntryDeviceEnd = TargetTable->EntriesEnd; 118 CurrDeviceEntry != EntryDeviceEnd; 119 CurrDeviceEntry++, CurrHostEntry++) { 120 if (CurrDeviceEntry->size != 0) { 121 // has data. 122 assert(CurrDeviceEntry->size == CurrHostEntry->size && 123 "data size mismatch"); 124 125 // Fortran may use multiple weak declarations for the same symbol, 126 // therefore we must allow for multiple weak symbols to be loaded from 127 // the fat binary. Treat these mappings as any other "regular" mapping. 128 // Add entry to map. 129 if (Device.getTgtPtrBegin(CurrHostEntry->addr, CurrHostEntry->size)) 130 continue; 131 DP("Add mapping from host " DPxMOD " to device " DPxMOD " with size %zu" 132 "\n", 133 DPxPTR(CurrHostEntry->addr), DPxPTR(CurrDeviceEntry->addr), 134 CurrDeviceEntry->size); 135 Device.HostDataToTargetMap.emplace( 136 (uintptr_t)CurrHostEntry->addr /*HstPtrBase*/, 137 (uintptr_t)CurrHostEntry->addr /*HstPtrBegin*/, 138 (uintptr_t)CurrHostEntry->addr + CurrHostEntry->size /*HstPtrEnd*/, 139 (uintptr_t)CurrDeviceEntry->addr /*TgtPtrBegin*/, nullptr, 140 true /*IsRefCountINF*/); 141 } 142 } 143 Device.DataMapMtx.unlock(); 144 } 145 PM->TrlTblMtx.unlock(); 146 147 if (rc != OFFLOAD_SUCCESS) { 148 Device.PendingGlobalsMtx.unlock(); 149 return rc; 150 } 151 152 /* 153 * Run ctors for static objects 154 */ 155 if (!Device.PendingCtorsDtors.empty()) { 156 // Call all ctors for all libraries registered so far 157 for (auto &lib : Device.PendingCtorsDtors) { 158 if (!lib.second.PendingCtors.empty()) { 159 DP("Has pending ctors... call now\n"); 160 for (auto &entry : lib.second.PendingCtors) { 161 void *ctor = entry; 162 int rc = 163 target(nullptr, device_id, ctor, 0, nullptr, nullptr, nullptr, 164 nullptr, nullptr, nullptr, 1, 1, true /*team*/); 165 if (rc != OFFLOAD_SUCCESS) { 166 REPORT("Running ctor " DPxMOD " failed.\n", DPxPTR(ctor)); 167 Device.PendingGlobalsMtx.unlock(); 168 return OFFLOAD_FAIL; 169 } 170 } 171 // Clear the list to indicate that this device has been used 172 lib.second.PendingCtors.clear(); 173 DP("Done with pending ctors for lib " DPxMOD "\n", DPxPTR(lib.first)); 174 } 175 } 176 } 177 Device.HasPendingGlobals = false; 178 Device.PendingGlobalsMtx.unlock(); 179 180 return OFFLOAD_SUCCESS; 181 } 182 183 // Check whether a device has been initialized, global ctors have been 184 // executed and global data has been mapped; do so if not already done. 185 int CheckDeviceAndCtors(int64_t device_id) { 186 // Is device ready? 187 if (!device_is_ready(device_id)) { 188 REPORT("Device %" PRId64 " is not ready.\n", device_id); 189 return OFFLOAD_FAIL; 190 } 191 192 // Get device info. 193 DeviceTy &Device = PM->Devices[device_id]; 194 195 // Check whether global data has been mapped for this device 196 Device.PendingGlobalsMtx.lock(); 197 bool hasPendingGlobals = Device.HasPendingGlobals; 198 Device.PendingGlobalsMtx.unlock(); 199 if (hasPendingGlobals && InitLibrary(Device) != OFFLOAD_SUCCESS) { 200 REPORT("Failed to init globals on device %" PRId64 "\n", device_id); 201 return OFFLOAD_FAIL; 202 } 203 204 return OFFLOAD_SUCCESS; 205 } 206 207 static int32_t getParentIndex(int64_t type) { 208 return ((type & OMP_TGT_MAPTYPE_MEMBER_OF) >> 48) - 1; 209 } 210 211 /// Call the user-defined mapper function followed by the appropriate 212 // targetData* function (targetData{Begin,End,Update}). 213 int targetDataMapper(ident_t *loc, DeviceTy &Device, void *arg_base, void *arg, 214 int64_t arg_size, int64_t arg_type, 215 map_var_info_t arg_names, void *arg_mapper, 216 TargetDataFuncPtrTy target_data_function) { 217 TIMESCOPE_WITH_IDENT(loc); 218 DP("Calling the mapper function " DPxMOD "\n", DPxPTR(arg_mapper)); 219 220 // The mapper function fills up Components. 221 MapperComponentsTy MapperComponents; 222 MapperFuncPtrTy MapperFuncPtr = (MapperFuncPtrTy)(arg_mapper); 223 (*MapperFuncPtr)((void *)&MapperComponents, arg_base, arg, arg_size, arg_type, 224 arg_names); 225 226 // Construct new arrays for args_base, args, arg_sizes and arg_types 227 // using the information in MapperComponents and call the corresponding 228 // targetData* function using these new arrays. 229 std::vector<void *> MapperArgsBase(MapperComponents.Components.size()); 230 std::vector<void *> MapperArgs(MapperComponents.Components.size()); 231 std::vector<int64_t> MapperArgSizes(MapperComponents.Components.size()); 232 std::vector<int64_t> MapperArgTypes(MapperComponents.Components.size()); 233 std::vector<void *> MapperArgNames(MapperComponents.Components.size()); 234 235 for (unsigned I = 0, E = MapperComponents.Components.size(); I < E; ++I) { 236 auto &C = 237 MapperComponents 238 .Components[target_data_function == targetDataEnd ? I : E - I - 1]; 239 MapperArgsBase[I] = C.Base; 240 MapperArgs[I] = C.Begin; 241 MapperArgSizes[I] = C.Size; 242 MapperArgTypes[I] = C.Type; 243 MapperArgNames[I] = C.Name; 244 } 245 246 int rc = target_data_function(loc, Device, MapperComponents.Components.size(), 247 MapperArgsBase.data(), MapperArgs.data(), 248 MapperArgSizes.data(), MapperArgTypes.data(), 249 MapperArgNames.data(), /*arg_mappers*/ nullptr, 250 /*__tgt_async_info*/ nullptr); 251 252 return rc; 253 } 254 255 /// Internal function to do the mapping and transfer the data to the device 256 int targetDataBegin(ident_t *loc, DeviceTy &Device, int32_t arg_num, 257 void **args_base, void **args, int64_t *arg_sizes, 258 int64_t *arg_types, map_var_info_t *arg_names, 259 void **arg_mappers, __tgt_async_info *async_info_ptr) { 260 // process each input. 261 for (int32_t i = 0; i < arg_num; ++i) { 262 // Ignore private variables and arrays - there is no mapping for them. 263 if ((arg_types[i] & OMP_TGT_MAPTYPE_LITERAL) || 264 (arg_types[i] & OMP_TGT_MAPTYPE_PRIVATE)) 265 continue; 266 267 if (arg_mappers && arg_mappers[i]) { 268 // Instead of executing the regular path of targetDataBegin, call the 269 // targetDataMapper variant which will call targetDataBegin again 270 // with new arguments. 271 DP("Calling targetDataMapper for the %dth argument\n", i); 272 273 map_var_info_t arg_name = (!arg_names) ? nullptr : arg_names[i]; 274 int rc = targetDataMapper(loc, Device, args_base[i], args[i], arg_sizes[i], 275 arg_types[i], arg_name, arg_mappers[i], 276 targetDataBegin); 277 278 if (rc != OFFLOAD_SUCCESS) { 279 REPORT("Call to targetDataBegin via targetDataMapper for custom mapper" 280 " failed.\n"); 281 return OFFLOAD_FAIL; 282 } 283 284 // Skip the rest of this function, continue to the next argument. 285 continue; 286 } 287 288 void *HstPtrBegin = args[i]; 289 void *HstPtrBase = args_base[i]; 290 int64_t data_size = arg_sizes[i]; 291 map_var_info_t HstPtrName = (!arg_names) ? nullptr : arg_names[i]; 292 293 // Adjust for proper alignment if this is a combined entry (for structs). 294 // Look at the next argument - if that is MEMBER_OF this one, then this one 295 // is a combined entry. 296 int64_t padding = 0; 297 const int next_i = i + 1; 298 if (getParentIndex(arg_types[i]) < 0 && next_i < arg_num && 299 getParentIndex(arg_types[next_i]) == i) { 300 padding = (int64_t)HstPtrBegin % Alignment; 301 if (padding) { 302 DP("Using a padding of %" PRId64 " bytes for begin address " DPxMOD 303 "\n", 304 padding, DPxPTR(HstPtrBegin)); 305 HstPtrBegin = (char *)HstPtrBegin - padding; 306 data_size += padding; 307 } 308 } 309 310 // Address of pointer on the host and device, respectively. 311 void *Pointer_HstPtrBegin, *PointerTgtPtrBegin; 312 bool IsNew, Pointer_IsNew; 313 bool IsHostPtr = false; 314 bool IsImplicit = arg_types[i] & OMP_TGT_MAPTYPE_IMPLICIT; 315 // Force the creation of a device side copy of the data when: 316 // a close map modifier was associated with a map that contained a to. 317 bool HasCloseModifier = arg_types[i] & OMP_TGT_MAPTYPE_CLOSE; 318 bool HasPresentModifier = arg_types[i] & OMP_TGT_MAPTYPE_PRESENT; 319 // UpdateRef is based on MEMBER_OF instead of TARGET_PARAM because if we 320 // have reached this point via __tgt_target_data_begin and not __tgt_target 321 // then no argument is marked as TARGET_PARAM ("omp target data map" is not 322 // associated with a target region, so there are no target parameters). This 323 // may be considered a hack, we could revise the scheme in the future. 324 bool UpdateRef = !(arg_types[i] & OMP_TGT_MAPTYPE_MEMBER_OF); 325 if (arg_types[i] & OMP_TGT_MAPTYPE_PTR_AND_OBJ) { 326 DP("Has a pointer entry: \n"); 327 // Base is address of pointer. 328 // 329 // Usually, the pointer is already allocated by this time. For example: 330 // 331 // #pragma omp target map(s.p[0:N]) 332 // 333 // The map entry for s comes first, and the PTR_AND_OBJ entry comes 334 // afterward, so the pointer is already allocated by the time the 335 // PTR_AND_OBJ entry is handled below, and PointerTgtPtrBegin is thus 336 // non-null. However, "declare target link" can produce a PTR_AND_OBJ 337 // entry for a global that might not already be allocated by the time the 338 // PTR_AND_OBJ entry is handled below, and so the allocation might fail 339 // when HasPresentModifier. 340 PointerTgtPtrBegin = Device.getOrAllocTgtPtr( 341 HstPtrBase, HstPtrBase, sizeof(void *), nullptr, Pointer_IsNew, 342 IsHostPtr, IsImplicit, UpdateRef, HasCloseModifier, 343 HasPresentModifier); 344 if (!PointerTgtPtrBegin) { 345 REPORT("Call to getOrAllocTgtPtr returned null pointer (%s).\n", 346 HasPresentModifier ? "'present' map type modifier" 347 : "device failure or illegal mapping"); 348 return OFFLOAD_FAIL; 349 } 350 DP("There are %zu bytes allocated at target address " DPxMOD " - is%s new" 351 "\n", 352 sizeof(void *), DPxPTR(PointerTgtPtrBegin), 353 (Pointer_IsNew ? "" : " not")); 354 Pointer_HstPtrBegin = HstPtrBase; 355 // modify current entry. 356 HstPtrBase = *(void **)HstPtrBase; 357 UpdateRef = true; // subsequently update ref count of pointee 358 } 359 360 void *TgtPtrBegin = Device.getOrAllocTgtPtr( 361 HstPtrBegin, HstPtrBase, data_size, HstPtrName, IsNew, IsHostPtr, 362 IsImplicit, UpdateRef, HasCloseModifier, HasPresentModifier); 363 // If data_size==0, then the argument could be a zero-length pointer to 364 // NULL, so getOrAlloc() returning NULL is not an error. 365 if (!TgtPtrBegin && (data_size || HasPresentModifier)) { 366 REPORT("Call to getOrAllocTgtPtr returned null pointer (%s).\n", 367 HasPresentModifier ? "'present' map type modifier" 368 : "device failure or illegal mapping"); 369 return OFFLOAD_FAIL; 370 } 371 DP("There are %" PRId64 " bytes allocated at target address " DPxMOD 372 " - is%s new\n", 373 data_size, DPxPTR(TgtPtrBegin), (IsNew ? "" : " not")); 374 375 if (arg_types[i] & OMP_TGT_MAPTYPE_RETURN_PARAM) { 376 uintptr_t Delta = (uintptr_t)HstPtrBegin - (uintptr_t)HstPtrBase; 377 void *TgtPtrBase = (void *)((uintptr_t)TgtPtrBegin - Delta); 378 DP("Returning device pointer " DPxMOD "\n", DPxPTR(TgtPtrBase)); 379 args_base[i] = TgtPtrBase; 380 } 381 382 if (arg_types[i] & OMP_TGT_MAPTYPE_TO) { 383 bool copy = false; 384 if (!(PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY) || 385 HasCloseModifier) { 386 if (IsNew || (arg_types[i] & OMP_TGT_MAPTYPE_ALWAYS)) { 387 copy = true; 388 } else if ((arg_types[i] & OMP_TGT_MAPTYPE_MEMBER_OF) && 389 !(arg_types[i] & OMP_TGT_MAPTYPE_PTR_AND_OBJ)) { 390 // Copy data only if the "parent" struct has RefCount==1. 391 // If this is a PTR_AND_OBJ entry, the OBJ is not part of the struct, 392 // so exclude it from this check. 393 int32_t parent_idx = getParentIndex(arg_types[i]); 394 uint64_t parent_rc = Device.getMapEntryRefCnt(args[parent_idx]); 395 assert(parent_rc > 0 && "parent struct not found"); 396 if (parent_rc == 1) { 397 copy = true; 398 } 399 } 400 } 401 402 if (copy && !IsHostPtr) { 403 DP("Moving %" PRId64 " bytes (hst:" DPxMOD ") -> (tgt:" DPxMOD ")\n", 404 data_size, DPxPTR(HstPtrBegin), DPxPTR(TgtPtrBegin)); 405 int rt = Device.submitData(TgtPtrBegin, HstPtrBegin, data_size, 406 async_info_ptr); 407 if (rt != OFFLOAD_SUCCESS) { 408 REPORT("Copying data to device failed.\n"); 409 return OFFLOAD_FAIL; 410 } 411 } 412 } 413 414 if (arg_types[i] & OMP_TGT_MAPTYPE_PTR_AND_OBJ && !IsHostPtr) { 415 DP("Update pointer (" DPxMOD ") -> [" DPxMOD "]\n", 416 DPxPTR(PointerTgtPtrBegin), DPxPTR(TgtPtrBegin)); 417 uint64_t Delta = (uint64_t)HstPtrBegin - (uint64_t)HstPtrBase; 418 void *TgtPtrBase = (void *)((uint64_t)TgtPtrBegin - Delta); 419 int rt = Device.submitData(PointerTgtPtrBegin, &TgtPtrBase, 420 sizeof(void *), async_info_ptr); 421 if (rt != OFFLOAD_SUCCESS) { 422 REPORT("Copying data to device failed.\n"); 423 return OFFLOAD_FAIL; 424 } 425 // create shadow pointers for this entry 426 Device.ShadowMtx.lock(); 427 Device.ShadowPtrMap[Pointer_HstPtrBegin] = { 428 HstPtrBase, PointerTgtPtrBegin, TgtPtrBase}; 429 Device.ShadowMtx.unlock(); 430 } 431 } 432 433 return OFFLOAD_SUCCESS; 434 } 435 436 namespace { 437 /// This structure contains information to deallocate a target pointer, aka. 438 /// used to call the function \p DeviceTy::deallocTgtPtr. 439 struct DeallocTgtPtrInfo { 440 /// Host pointer used to look up into the map table 441 void *HstPtrBegin; 442 /// Size of the data 443 int64_t DataSize; 444 /// Whether it is forced to be removed from the map table 445 bool ForceDelete; 446 /// Whether it has \p close modifier 447 bool HasCloseModifier; 448 449 DeallocTgtPtrInfo(void *HstPtr, int64_t Size, bool ForceDelete, 450 bool HasCloseModifier) 451 : HstPtrBegin(HstPtr), DataSize(Size), ForceDelete(ForceDelete), 452 HasCloseModifier(HasCloseModifier) {} 453 }; 454 455 /// Synchronize device 456 static int syncDevice(DeviceTy &Device, __tgt_async_info *AsyncInfo) { 457 assert(AsyncInfo && AsyncInfo->Queue && "Invalid AsyncInfo"); 458 if (Device.synchronize(AsyncInfo) != OFFLOAD_SUCCESS) { 459 REPORT("Failed to synchronize device.\n"); 460 return OFFLOAD_FAIL; 461 } 462 463 return OFFLOAD_SUCCESS; 464 } 465 } // namespace 466 467 /// Internal function to undo the mapping and retrieve the data from the device. 468 int targetDataEnd(ident_t *loc, DeviceTy &Device, int32_t ArgNum, 469 void **ArgBases, void **Args, int64_t *ArgSizes, 470 int64_t *ArgTypes, map_var_info_t *ArgNames, 471 void **ArgMappers, __tgt_async_info *AsyncInfo) { 472 int Ret; 473 std::vector<DeallocTgtPtrInfo> DeallocTgtPtrs; 474 // process each input. 475 for (int32_t I = ArgNum - 1; I >= 0; --I) { 476 // Ignore private variables and arrays - there is no mapping for them. 477 // Also, ignore the use_device_ptr directive, it has no effect here. 478 if ((ArgTypes[I] & OMP_TGT_MAPTYPE_LITERAL) || 479 (ArgTypes[I] & OMP_TGT_MAPTYPE_PRIVATE)) 480 continue; 481 482 if (ArgMappers && ArgMappers[I]) { 483 // Instead of executing the regular path of targetDataEnd, call the 484 // targetDataMapper variant which will call targetDataEnd again 485 // with new arguments. 486 DP("Calling targetDataMapper for the %dth argument\n", I); 487 488 map_var_info_t ArgName = (!ArgNames) ? nullptr : ArgNames[I]; 489 Ret = 490 targetDataMapper(loc, Device, ArgBases[I], Args[I], ArgSizes[I], 491 ArgTypes[I], ArgName, ArgMappers[I], targetDataEnd); 492 493 if (Ret != OFFLOAD_SUCCESS) { 494 REPORT("Call to targetDataEnd via targetDataMapper for custom mapper" 495 " failed.\n"); 496 return OFFLOAD_FAIL; 497 } 498 499 // Skip the rest of this function, continue to the next argument. 500 continue; 501 } 502 503 void *HstPtrBegin = Args[I]; 504 int64_t DataSize = ArgSizes[I]; 505 // Adjust for proper alignment if this is a combined entry (for structs). 506 // Look at the next argument - if that is MEMBER_OF this one, then this one 507 // is a combined entry. 508 const int NextI = I + 1; 509 if (getParentIndex(ArgTypes[I]) < 0 && NextI < ArgNum && 510 getParentIndex(ArgTypes[NextI]) == I) { 511 int64_t Padding = (int64_t)HstPtrBegin % Alignment; 512 if (Padding) { 513 DP("Using a Padding of %" PRId64 " bytes for begin address " DPxMOD 514 "\n", 515 Padding, DPxPTR(HstPtrBegin)); 516 HstPtrBegin = (char *)HstPtrBegin - Padding; 517 DataSize += Padding; 518 } 519 } 520 521 bool IsLast, IsHostPtr; 522 bool IsImplicit = ArgTypes[I] & OMP_TGT_MAPTYPE_IMPLICIT; 523 bool UpdateRef = !(ArgTypes[I] & OMP_TGT_MAPTYPE_MEMBER_OF) || 524 (ArgTypes[I] & OMP_TGT_MAPTYPE_PTR_AND_OBJ); 525 bool ForceDelete = ArgTypes[I] & OMP_TGT_MAPTYPE_DELETE; 526 bool HasCloseModifier = ArgTypes[I] & OMP_TGT_MAPTYPE_CLOSE; 527 bool HasPresentModifier = ArgTypes[I] & OMP_TGT_MAPTYPE_PRESENT; 528 529 // If PTR_AND_OBJ, HstPtrBegin is address of pointee 530 void *TgtPtrBegin = Device.getTgtPtrBegin( 531 HstPtrBegin, DataSize, IsLast, UpdateRef, IsHostPtr, !IsImplicit); 532 if (!TgtPtrBegin && (DataSize || HasPresentModifier)) { 533 DP("Mapping does not exist (%s)\n", 534 (HasPresentModifier ? "'present' map type modifier" : "ignored")); 535 if (HasPresentModifier) { 536 // This should be an error upon entering an "omp target exit data". It 537 // should not be an error upon exiting an "omp target data" or "omp 538 // target". For "omp target data", Clang thus doesn't include present 539 // modifiers for end calls. For "omp target", we have not found a valid 540 // OpenMP program for which the error matters: it appears that, if a 541 // program can guarantee that data is present at the beginning of an 542 // "omp target" region so that there's no error there, that data is also 543 // guaranteed to be present at the end. 544 MESSAGE("device mapping required by 'present' map type modifier does " 545 "not exist for host address " DPxMOD " (%" PRId64 " bytes)", 546 DPxPTR(HstPtrBegin), DataSize); 547 return OFFLOAD_FAIL; 548 } 549 } else { 550 DP("There are %" PRId64 " bytes allocated at target address " DPxMOD 551 " - is%s last\n", 552 DataSize, DPxPTR(TgtPtrBegin), (IsLast ? "" : " not")); 553 } 554 555 bool DelEntry = IsLast || ForceDelete; 556 557 if ((ArgTypes[I] & OMP_TGT_MAPTYPE_MEMBER_OF) && 558 !(ArgTypes[I] & OMP_TGT_MAPTYPE_PTR_AND_OBJ)) { 559 DelEntry = false; // protect parent struct from being deallocated 560 } 561 562 if ((ArgTypes[I] & OMP_TGT_MAPTYPE_FROM) || DelEntry) { 563 // Move data back to the host 564 if (ArgTypes[I] & OMP_TGT_MAPTYPE_FROM) { 565 bool Always = ArgTypes[I] & OMP_TGT_MAPTYPE_ALWAYS; 566 bool CopyMember = false; 567 if (!(PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY) || 568 HasCloseModifier) { 569 if ((ArgTypes[I] & OMP_TGT_MAPTYPE_MEMBER_OF) && 570 !(ArgTypes[I] & OMP_TGT_MAPTYPE_PTR_AND_OBJ)) { 571 // Copy data only if the "parent" struct has RefCount==1. 572 int32_t ParentIdx = getParentIndex(ArgTypes[I]); 573 uint64_t ParentRC = Device.getMapEntryRefCnt(Args[ParentIdx]); 574 assert(ParentRC > 0 && "parent struct not found"); 575 if (ParentRC == 1) 576 CopyMember = true; 577 } 578 } 579 580 if ((DelEntry || Always || CopyMember) && 581 !(PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY && 582 TgtPtrBegin == HstPtrBegin)) { 583 DP("Moving %" PRId64 " bytes (tgt:" DPxMOD ") -> (hst:" DPxMOD ")\n", 584 DataSize, DPxPTR(TgtPtrBegin), DPxPTR(HstPtrBegin)); 585 Ret = Device.retrieveData(HstPtrBegin, TgtPtrBegin, DataSize, 586 AsyncInfo); 587 if (Ret != OFFLOAD_SUCCESS) { 588 REPORT("Copying data from device failed.\n"); 589 return OFFLOAD_FAIL; 590 } 591 } 592 } 593 594 // If we copied back to the host a struct/array containing pointers, we 595 // need to restore the original host pointer values from their shadow 596 // copies. If the struct is going to be deallocated, remove any remaining 597 // shadow pointer entries for this struct. 598 uintptr_t LB = (uintptr_t)HstPtrBegin; 599 uintptr_t UB = (uintptr_t)HstPtrBegin + DataSize; 600 Device.ShadowMtx.lock(); 601 for (ShadowPtrListTy::iterator Itr = Device.ShadowPtrMap.begin(); 602 Itr != Device.ShadowPtrMap.end();) { 603 void **ShadowHstPtrAddr = (void **)Itr->first; 604 605 // An STL map is sorted on its keys; use this property 606 // to quickly determine when to break out of the loop. 607 if ((uintptr_t)ShadowHstPtrAddr < LB) { 608 ++Itr; 609 continue; 610 } 611 if ((uintptr_t)ShadowHstPtrAddr >= UB) 612 break; 613 614 // If we copied the struct to the host, we need to restore the pointer. 615 if (ArgTypes[I] & OMP_TGT_MAPTYPE_FROM) { 616 DP("Restoring original host pointer value " DPxMOD " for host " 617 "pointer " DPxMOD "\n", 618 DPxPTR(Itr->second.HstPtrVal), DPxPTR(ShadowHstPtrAddr)); 619 *ShadowHstPtrAddr = Itr->second.HstPtrVal; 620 } 621 // If the struct is to be deallocated, remove the shadow entry. 622 if (DelEntry) { 623 DP("Removing shadow pointer " DPxMOD "\n", DPxPTR(ShadowHstPtrAddr)); 624 Itr = Device.ShadowPtrMap.erase(Itr); 625 } else { 626 ++Itr; 627 } 628 } 629 Device.ShadowMtx.unlock(); 630 631 // Add pointer to the buffer for later deallocation 632 if (DelEntry) 633 DeallocTgtPtrs.emplace_back(HstPtrBegin, DataSize, ForceDelete, 634 HasCloseModifier); 635 } 636 } 637 638 // We need to synchronize before deallocating data. 639 // If AsyncInfo is nullptr, the previous data transfer (if has) will be 640 // synchronous, so we don't need to synchronize again. If AsyncInfo->Queue is 641 // nullptr, there is no data transfer happened because once there is, 642 // AsyncInfo->Queue will not be nullptr, so again, we don't need to 643 // synchronize. 644 if (AsyncInfo && AsyncInfo->Queue) { 645 Ret = syncDevice(Device, AsyncInfo); 646 if (Ret != OFFLOAD_SUCCESS) 647 return OFFLOAD_FAIL; 648 } 649 650 // Deallocate target pointer 651 for (DeallocTgtPtrInfo &Info : DeallocTgtPtrs) { 652 Ret = Device.deallocTgtPtr(Info.HstPtrBegin, Info.DataSize, 653 Info.ForceDelete, Info.HasCloseModifier); 654 if (Ret != OFFLOAD_SUCCESS) { 655 REPORT("Deallocating data from device failed.\n"); 656 return OFFLOAD_FAIL; 657 } 658 } 659 660 return OFFLOAD_SUCCESS; 661 } 662 663 static int targetDataContiguous(ident_t *loc, DeviceTy &Device, void *ArgsBase, 664 void *HstPtrBegin, int64_t ArgSize, 665 int64_t ArgType) { 666 TIMESCOPE_WITH_IDENT(loc); 667 bool IsLast, IsHostPtr; 668 void *TgtPtrBegin = Device.getTgtPtrBegin(HstPtrBegin, ArgSize, IsLast, false, 669 IsHostPtr, /*MustContain=*/true); 670 if (!TgtPtrBegin) { 671 DP("hst data:" DPxMOD " not found, becomes a noop\n", DPxPTR(HstPtrBegin)); 672 if (ArgType & OMP_TGT_MAPTYPE_PRESENT) { 673 MESSAGE("device mapping required by 'present' motion modifier does not " 674 "exist for host address " DPxMOD " (%" PRId64 " bytes)", 675 DPxPTR(HstPtrBegin), ArgSize); 676 return OFFLOAD_FAIL; 677 } 678 return OFFLOAD_SUCCESS; 679 } 680 681 if (PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY && 682 TgtPtrBegin == HstPtrBegin) { 683 DP("hst data:" DPxMOD " unified and shared, becomes a noop\n", 684 DPxPTR(HstPtrBegin)); 685 return OFFLOAD_SUCCESS; 686 } 687 688 if (ArgType & OMP_TGT_MAPTYPE_FROM) { 689 DP("Moving %" PRId64 " bytes (tgt:" DPxMOD ") -> (hst:" DPxMOD ")\n", 690 ArgSize, DPxPTR(TgtPtrBegin), DPxPTR(HstPtrBegin)); 691 int Ret = Device.retrieveData(HstPtrBegin, TgtPtrBegin, ArgSize, nullptr); 692 if (Ret != OFFLOAD_SUCCESS) { 693 REPORT("Copying data from device failed.\n"); 694 return OFFLOAD_FAIL; 695 } 696 697 uintptr_t LB = (uintptr_t)HstPtrBegin; 698 uintptr_t UB = (uintptr_t)HstPtrBegin + ArgSize; 699 Device.ShadowMtx.lock(); 700 for (ShadowPtrListTy::iterator IT = Device.ShadowPtrMap.begin(); 701 IT != Device.ShadowPtrMap.end(); ++IT) { 702 void **ShadowHstPtrAddr = (void **)IT->first; 703 if ((uintptr_t)ShadowHstPtrAddr < LB) 704 continue; 705 if ((uintptr_t)ShadowHstPtrAddr >= UB) 706 break; 707 DP("Restoring original host pointer value " DPxMOD 708 " for host pointer " DPxMOD "\n", 709 DPxPTR(IT->second.HstPtrVal), DPxPTR(ShadowHstPtrAddr)); 710 *ShadowHstPtrAddr = IT->second.HstPtrVal; 711 } 712 Device.ShadowMtx.unlock(); 713 } 714 715 if (ArgType & OMP_TGT_MAPTYPE_TO) { 716 DP("Moving %" PRId64 " bytes (hst:" DPxMOD ") -> (tgt:" DPxMOD ")\n", 717 ArgSize, DPxPTR(HstPtrBegin), DPxPTR(TgtPtrBegin)); 718 int Ret = Device.submitData(TgtPtrBegin, HstPtrBegin, ArgSize, nullptr); 719 if (Ret != OFFLOAD_SUCCESS) { 720 REPORT("Copying data to device failed.\n"); 721 return OFFLOAD_FAIL; 722 } 723 724 uintptr_t LB = (uintptr_t)HstPtrBegin; 725 uintptr_t UB = (uintptr_t)HstPtrBegin + ArgSize; 726 Device.ShadowMtx.lock(); 727 for (ShadowPtrListTy::iterator IT = Device.ShadowPtrMap.begin(); 728 IT != Device.ShadowPtrMap.end(); ++IT) { 729 void **ShadowHstPtrAddr = (void **)IT->first; 730 if ((uintptr_t)ShadowHstPtrAddr < LB) 731 continue; 732 if ((uintptr_t)ShadowHstPtrAddr >= UB) 733 break; 734 DP("Restoring original target pointer value " DPxMOD " for target " 735 "pointer " DPxMOD "\n", 736 DPxPTR(IT->second.TgtPtrVal), DPxPTR(IT->second.TgtPtrAddr)); 737 Ret = Device.submitData(IT->second.TgtPtrAddr, &IT->second.TgtPtrVal, 738 sizeof(void *), nullptr); 739 if (Ret != OFFLOAD_SUCCESS) { 740 REPORT("Copying data to device failed.\n"); 741 Device.ShadowMtx.unlock(); 742 return OFFLOAD_FAIL; 743 } 744 } 745 Device.ShadowMtx.unlock(); 746 } 747 return OFFLOAD_SUCCESS; 748 } 749 750 static int targetDataNonContiguous(ident_t *loc, DeviceTy &Device, 751 void *ArgsBase, 752 __tgt_target_non_contig *NonContig, 753 uint64_t Size, int64_t ArgType, 754 int CurrentDim, int DimSize, 755 uint64_t Offset) { 756 TIMESCOPE_WITH_IDENT(loc); 757 int Ret = OFFLOAD_SUCCESS; 758 if (CurrentDim < DimSize) { 759 for (unsigned int I = 0; I < NonContig[CurrentDim].Count; ++I) { 760 uint64_t CurOffset = 761 (NonContig[CurrentDim].Offset + I) * NonContig[CurrentDim].Stride; 762 // we only need to transfer the first element for the last dimension 763 // since we've already got a contiguous piece. 764 if (CurrentDim != DimSize - 1 || I == 0) { 765 Ret = targetDataNonContiguous(loc, Device, ArgsBase, NonContig, Size, 766 ArgType, CurrentDim + 1, DimSize, 767 Offset + CurOffset); 768 // Stop the whole process if any contiguous piece returns anything 769 // other than OFFLOAD_SUCCESS. 770 if (Ret != OFFLOAD_SUCCESS) 771 return Ret; 772 } 773 } 774 } else { 775 char *Ptr = (char *)ArgsBase + Offset; 776 DP("Transfer of non-contiguous : host ptr " DPxMOD " offset %" PRIu64 777 " len %" PRIu64 "\n", 778 DPxPTR(Ptr), Offset, Size); 779 Ret = targetDataContiguous(loc, Device, ArgsBase, Ptr, Size, ArgType); 780 } 781 return Ret; 782 } 783 784 static int getNonContigMergedDimension(__tgt_target_non_contig *NonContig, 785 int32_t DimSize) { 786 int RemovedDim = 0; 787 for (int I = DimSize - 1; I > 0; --I) { 788 if (NonContig[I].Count * NonContig[I].Stride == NonContig[I - 1].Stride) 789 RemovedDim++; 790 } 791 return RemovedDim; 792 } 793 794 /// Internal function to pass data to/from the target. 795 // async_info_ptr is currently unused, added here so targetDataUpdate has the 796 // same signature as targetDataBegin and targetDataEnd. 797 int targetDataUpdate(ident_t *loc, DeviceTy &Device, int32_t ArgNum, 798 void **ArgsBase, void **Args, int64_t *ArgSizes, 799 int64_t *ArgTypes, map_var_info_t *ArgNames, 800 void **ArgMappers, __tgt_async_info *AsyncInfoPtr) { 801 // process each input. 802 for (int32_t I = 0; I < ArgNum; ++I) { 803 if ((ArgTypes[I] & OMP_TGT_MAPTYPE_LITERAL) || 804 (ArgTypes[I] & OMP_TGT_MAPTYPE_PRIVATE)) 805 continue; 806 807 if (ArgMappers && ArgMappers[I]) { 808 // Instead of executing the regular path of targetDataUpdate, call the 809 // targetDataMapper variant which will call targetDataUpdate again 810 // with new arguments. 811 DP("Calling targetDataMapper for the %dth argument\n", I); 812 813 map_var_info_t ArgName = (!ArgNames) ? nullptr : ArgNames[I]; 814 int Ret = targetDataMapper(loc, Device, ArgsBase[I], Args[I], ArgSizes[I], 815 ArgTypes[I], ArgName, ArgMappers[I], 816 targetDataUpdate); 817 818 if (Ret != OFFLOAD_SUCCESS) { 819 REPORT("Call to targetDataUpdate via targetDataMapper for custom mapper" 820 " failed.\n"); 821 return OFFLOAD_FAIL; 822 } 823 824 // Skip the rest of this function, continue to the next argument. 825 continue; 826 } 827 828 int Ret = OFFLOAD_SUCCESS; 829 830 if (ArgTypes[I] & OMP_TGT_MAPTYPE_NON_CONTIG) { 831 __tgt_target_non_contig *NonContig = (__tgt_target_non_contig *)Args[I]; 832 int32_t DimSize = ArgSizes[I]; 833 uint64_t Size = 834 NonContig[DimSize - 1].Count * NonContig[DimSize - 1].Stride; 835 int32_t MergedDim = getNonContigMergedDimension(NonContig, DimSize); 836 Ret = targetDataNonContiguous( 837 loc, Device, ArgsBase[I], NonContig, Size, ArgTypes[I], 838 /*current_dim=*/0, DimSize - MergedDim, /*offset=*/0); 839 } else { 840 Ret = targetDataContiguous(loc, Device, ArgsBase[I], Args[I], ArgSizes[I], 841 ArgTypes[I]); 842 } 843 if (Ret == OFFLOAD_FAIL) 844 return OFFLOAD_FAIL; 845 } 846 return OFFLOAD_SUCCESS; 847 } 848 849 static const unsigned LambdaMapping = OMP_TGT_MAPTYPE_PTR_AND_OBJ | 850 OMP_TGT_MAPTYPE_LITERAL | 851 OMP_TGT_MAPTYPE_IMPLICIT; 852 static bool isLambdaMapping(int64_t Mapping) { 853 return (Mapping & LambdaMapping) == LambdaMapping; 854 } 855 856 namespace { 857 /// Find the table information in the map or look it up in the translation 858 /// tables. 859 TableMap *getTableMap(void *HostPtr) { 860 std::lock_guard<std::mutex> TblMapLock(PM->TblMapMtx); 861 HostPtrToTableMapTy::iterator TableMapIt = 862 PM->HostPtrToTableMap.find(HostPtr); 863 864 if (TableMapIt != PM->HostPtrToTableMap.end()) 865 return &TableMapIt->second; 866 867 // We don't have a map. So search all the registered libraries. 868 TableMap *TM = nullptr; 869 std::lock_guard<std::mutex> TrlTblLock(PM->TrlTblMtx); 870 for (HostEntriesBeginToTransTableTy::iterator Itr = 871 PM->HostEntriesBeginToTransTable.begin(); 872 Itr != PM->HostEntriesBeginToTransTable.end(); ++Itr) { 873 // get the translation table (which contains all the good info). 874 TranslationTable *TransTable = &Itr->second; 875 // iterate over all the host table entries to see if we can locate the 876 // host_ptr. 877 __tgt_offload_entry *Cur = TransTable->HostTable.EntriesBegin; 878 for (uint32_t I = 0; Cur < TransTable->HostTable.EntriesEnd; ++Cur, ++I) { 879 if (Cur->addr != HostPtr) 880 continue; 881 // we got a match, now fill the HostPtrToTableMap so that we 882 // may avoid this search next time. 883 TM = &(PM->HostPtrToTableMap)[HostPtr]; 884 TM->Table = TransTable; 885 TM->Index = I; 886 return TM; 887 } 888 } 889 890 return nullptr; 891 } 892 893 /// Get loop trip count 894 /// FIXME: This function will not work right if calling 895 /// __kmpc_push_target_tripcount in one thread but doing offloading in another 896 /// thread, which might occur when we call task yield. 897 uint64_t getLoopTripCount(int64_t DeviceId) { 898 DeviceTy &Device = PM->Devices[DeviceId]; 899 uint64_t LoopTripCount = 0; 900 901 { 902 std::lock_guard<std::mutex> TblMapLock(PM->TblMapMtx); 903 auto I = Device.LoopTripCnt.find(__kmpc_global_thread_num(NULL)); 904 if (I != Device.LoopTripCnt.end()) { 905 LoopTripCount = I->second; 906 Device.LoopTripCnt.erase(I); 907 DP("loop trip count is %" PRIu64 ".\n", LoopTripCount); 908 } 909 } 910 911 return LoopTripCount; 912 } 913 914 /// A class manages private arguments in a target region. 915 class PrivateArgumentManagerTy { 916 /// A data structure for the information of first-private arguments. We can 917 /// use this information to optimize data transfer by packing all 918 /// first-private arguments and transfer them all at once. 919 struct FirstPrivateArgInfoTy { 920 /// The index of the element in \p TgtArgs corresponding to the argument 921 const int Index; 922 /// Host pointer begin 923 const char *HstPtrBegin; 924 /// Host pointer end 925 const char *HstPtrEnd; 926 /// Aligned size 927 const int64_t AlignedSize; 928 /// Host pointer name 929 const map_var_info_t HstPtrName = nullptr; 930 931 FirstPrivateArgInfoTy(int Index, const void *HstPtr, int64_t Size, 932 const map_var_info_t HstPtrName = nullptr) 933 : Index(Index), HstPtrBegin(reinterpret_cast<const char *>(HstPtr)), 934 HstPtrEnd(HstPtrBegin + Size), AlignedSize(Size + Size % Alignment), 935 HstPtrName(HstPtrName) {} 936 }; 937 938 /// A vector of target pointers for all private arguments 939 std::vector<void *> TgtPtrs; 940 941 /// A vector of information of all first-private arguments to be packed 942 std::vector<FirstPrivateArgInfoTy> FirstPrivateArgInfo; 943 /// Host buffer for all arguments to be packed 944 std::vector<char> FirstPrivateArgBuffer; 945 /// The total size of all arguments to be packed 946 int64_t FirstPrivateArgSize = 0; 947 948 /// A reference to the \p DeviceTy object 949 DeviceTy &Device; 950 /// A pointer to a \p __tgt_async_info object 951 __tgt_async_info *AsyncInfo; 952 953 // TODO: What would be the best value here? Should we make it configurable? 954 // If the size is larger than this threshold, we will allocate and transfer it 955 // immediately instead of packing it. 956 static constexpr const int64_t FirstPrivateArgSizeThreshold = 1024; 957 958 public: 959 /// Constructor 960 PrivateArgumentManagerTy(DeviceTy &Dev, __tgt_async_info *AsyncInfo) 961 : Device(Dev), AsyncInfo(AsyncInfo) {} 962 963 /// Add a private argument 964 int addArg(void *HstPtr, int64_t ArgSize, int64_t ArgOffset, 965 bool IsFirstPrivate, void *&TgtPtr, int TgtArgsIndex, 966 const map_var_info_t HstPtrName = nullptr) { 967 // If the argument is not first-private, or its size is greater than a 968 // predefined threshold, we will allocate memory and issue the transfer 969 // immediately. 970 if (ArgSize > FirstPrivateArgSizeThreshold || !IsFirstPrivate) { 971 TgtPtr = Device.allocData(ArgSize, HstPtr); 972 if (!TgtPtr) { 973 DP("Data allocation for %sprivate array " DPxMOD " failed.\n", 974 (IsFirstPrivate ? "first-" : ""), DPxPTR(HstPtr)); 975 return OFFLOAD_FAIL; 976 } 977 #ifdef OMPTARGET_DEBUG 978 void *TgtPtrBase = (void *)((intptr_t)TgtPtr + ArgOffset); 979 DP("Allocated %" PRId64 " bytes of target memory at " DPxMOD 980 " for %sprivate array " DPxMOD " - pushing target argument " DPxMOD 981 "\n", 982 ArgSize, DPxPTR(TgtPtr), (IsFirstPrivate ? "first-" : ""), 983 DPxPTR(HstPtr), DPxPTR(TgtPtrBase)); 984 #endif 985 // If first-private, copy data from host 986 if (IsFirstPrivate) { 987 int Ret = Device.submitData(TgtPtr, HstPtr, ArgSize, AsyncInfo); 988 if (Ret != OFFLOAD_SUCCESS) { 989 DP("Copying data to device failed, failed.\n"); 990 return OFFLOAD_FAIL; 991 } 992 } 993 TgtPtrs.push_back(TgtPtr); 994 } else { 995 DP("Firstprivate array " DPxMOD " of size %" PRId64 " will be packed\n", 996 DPxPTR(HstPtr), ArgSize); 997 // When reach this point, the argument must meet all following 998 // requirements: 999 // 1. Its size does not exceed the threshold (see the comment for 1000 // FirstPrivateArgSizeThreshold); 1001 // 2. It must be first-private (needs to be mapped to target device). 1002 // We will pack all this kind of arguments to transfer them all at once 1003 // to reduce the number of data transfer. We will not take 1004 // non-first-private arguments, aka. private arguments that doesn't need 1005 // to be mapped to target device, into account because data allocation 1006 // can be very efficient with memory manager. 1007 1008 // Placeholder value 1009 TgtPtr = nullptr; 1010 FirstPrivateArgInfo.emplace_back(TgtArgsIndex, HstPtr, ArgSize, 1011 HstPtrName); 1012 FirstPrivateArgSize += FirstPrivateArgInfo.back().AlignedSize; 1013 } 1014 1015 return OFFLOAD_SUCCESS; 1016 } 1017 1018 /// Pack first-private arguments, replace place holder pointers in \p TgtArgs, 1019 /// and start the transfer. 1020 int packAndTransfer(std::vector<void *> &TgtArgs) { 1021 if (!FirstPrivateArgInfo.empty()) { 1022 assert(FirstPrivateArgSize != 0 && 1023 "FirstPrivateArgSize is 0 but FirstPrivateArgInfo is empty"); 1024 FirstPrivateArgBuffer.resize(FirstPrivateArgSize, 0); 1025 auto Itr = FirstPrivateArgBuffer.begin(); 1026 // Copy all host data to this buffer 1027 for (FirstPrivateArgInfoTy &Info : FirstPrivateArgInfo) { 1028 std::copy(Info.HstPtrBegin, Info.HstPtrEnd, Itr); 1029 Itr = std::next(Itr, Info.AlignedSize); 1030 } 1031 // Allocate target memory 1032 void *TgtPtr = 1033 Device.allocData(FirstPrivateArgSize, FirstPrivateArgBuffer.data()); 1034 if (TgtPtr == nullptr) { 1035 DP("Failed to allocate target memory for private arguments.\n"); 1036 return OFFLOAD_FAIL; 1037 } 1038 TgtPtrs.push_back(TgtPtr); 1039 DP("Allocated %" PRId64 " bytes of target memory at " DPxMOD "\n", 1040 FirstPrivateArgSize, DPxPTR(TgtPtr)); 1041 // Transfer data to target device 1042 int Ret = Device.submitData(TgtPtr, FirstPrivateArgBuffer.data(), 1043 FirstPrivateArgSize, AsyncInfo); 1044 if (Ret != OFFLOAD_SUCCESS) { 1045 DP("Failed to submit data of private arguments.\n"); 1046 return OFFLOAD_FAIL; 1047 } 1048 // Fill in all placeholder pointers 1049 auto TP = reinterpret_cast<uintptr_t>(TgtPtr); 1050 for (FirstPrivateArgInfoTy &Info : FirstPrivateArgInfo) { 1051 void *&Ptr = TgtArgs[Info.Index]; 1052 assert(Ptr == nullptr && "Target pointer is already set by mistaken"); 1053 Ptr = reinterpret_cast<void *>(TP); 1054 TP += Info.AlignedSize; 1055 DP("Firstprivate array " DPxMOD " of size %" PRId64 " mapped to " DPxMOD 1056 "\n", 1057 DPxPTR(Info.HstPtrBegin), Info.HstPtrEnd - Info.HstPtrBegin, 1058 DPxPTR(Ptr)); 1059 } 1060 } 1061 1062 return OFFLOAD_SUCCESS; 1063 } 1064 1065 /// Free all target memory allocated for private arguments 1066 int free() { 1067 for (void *P : TgtPtrs) { 1068 int Ret = Device.deleteData(P); 1069 if (Ret != OFFLOAD_SUCCESS) { 1070 DP("Deallocation of (first-)private arrays failed.\n"); 1071 return OFFLOAD_FAIL; 1072 } 1073 } 1074 1075 TgtPtrs.clear(); 1076 1077 return OFFLOAD_SUCCESS; 1078 } 1079 }; 1080 1081 /// Process data before launching the kernel, including calling targetDataBegin 1082 /// to map and transfer data to target device, transferring (first-)private 1083 /// variables. 1084 static int processDataBefore(ident_t *loc, int64_t DeviceId, void *HostPtr, 1085 int32_t ArgNum, void **ArgBases, void **Args, 1086 int64_t *ArgSizes, int64_t *ArgTypes, 1087 map_var_info_t *ArgNames, void **ArgMappers, 1088 std::vector<void *> &TgtArgs, 1089 std::vector<ptrdiff_t> &TgtOffsets, 1090 PrivateArgumentManagerTy &PrivateArgumentManager, 1091 __tgt_async_info *AsyncInfo) { 1092 TIMESCOPE_WITH_NAME_AND_IDENT("mappingBeforeTargetRegion", loc); 1093 DeviceTy &Device = PM->Devices[DeviceId]; 1094 int Ret = targetDataBegin(loc, Device, ArgNum, ArgBases, Args, ArgSizes, 1095 ArgTypes, ArgNames, ArgMappers, AsyncInfo); 1096 if (Ret != OFFLOAD_SUCCESS) { 1097 REPORT("Call to targetDataBegin failed, abort target.\n"); 1098 return OFFLOAD_FAIL; 1099 } 1100 1101 // List of (first-)private arrays allocated for this target region 1102 std::vector<int> TgtArgsPositions(ArgNum, -1); 1103 1104 for (int32_t I = 0; I < ArgNum; ++I) { 1105 if (!(ArgTypes[I] & OMP_TGT_MAPTYPE_TARGET_PARAM)) { 1106 // This is not a target parameter, do not push it into TgtArgs. 1107 // Check for lambda mapping. 1108 if (isLambdaMapping(ArgTypes[I])) { 1109 assert((ArgTypes[I] & OMP_TGT_MAPTYPE_MEMBER_OF) && 1110 "PTR_AND_OBJ must be also MEMBER_OF."); 1111 unsigned Idx = getParentIndex(ArgTypes[I]); 1112 int TgtIdx = TgtArgsPositions[Idx]; 1113 assert(TgtIdx != -1 && "Base address must be translated already."); 1114 // The parent lambda must be processed already and it must be the last 1115 // in TgtArgs and TgtOffsets arrays. 1116 void *HstPtrVal = Args[I]; 1117 void *HstPtrBegin = ArgBases[I]; 1118 void *HstPtrBase = Args[Idx]; 1119 bool IsLast, IsHostPtr; // unused. 1120 void *TgtPtrBase = 1121 (void *)((intptr_t)TgtArgs[TgtIdx] + TgtOffsets[TgtIdx]); 1122 DP("Parent lambda base " DPxMOD "\n", DPxPTR(TgtPtrBase)); 1123 uint64_t Delta = (uint64_t)HstPtrBegin - (uint64_t)HstPtrBase; 1124 void *TgtPtrBegin = (void *)((uintptr_t)TgtPtrBase + Delta); 1125 void *PointerTgtPtrBegin = Device.getTgtPtrBegin( 1126 HstPtrVal, ArgSizes[I], IsLast, false, IsHostPtr); 1127 if (!PointerTgtPtrBegin) { 1128 DP("No lambda captured variable mapped (" DPxMOD ") - ignored\n", 1129 DPxPTR(HstPtrVal)); 1130 continue; 1131 } 1132 if (PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY && 1133 TgtPtrBegin == HstPtrBegin) { 1134 DP("Unified memory is active, no need to map lambda captured" 1135 "variable (" DPxMOD ")\n", 1136 DPxPTR(HstPtrVal)); 1137 continue; 1138 } 1139 DP("Update lambda reference (" DPxMOD ") -> [" DPxMOD "]\n", 1140 DPxPTR(PointerTgtPtrBegin), DPxPTR(TgtPtrBegin)); 1141 Ret = Device.submitData(TgtPtrBegin, &PointerTgtPtrBegin, 1142 sizeof(void *), AsyncInfo); 1143 if (Ret != OFFLOAD_SUCCESS) { 1144 REPORT("Copying data to device failed.\n"); 1145 return OFFLOAD_FAIL; 1146 } 1147 } 1148 continue; 1149 } 1150 void *HstPtrBegin = Args[I]; 1151 void *HstPtrBase = ArgBases[I]; 1152 void *TgtPtrBegin; 1153 map_var_info_t HstPtrName = (!ArgNames) ? nullptr : ArgNames[I]; 1154 ptrdiff_t TgtBaseOffset; 1155 bool IsLast, IsHostPtr; // unused. 1156 if (ArgTypes[I] & OMP_TGT_MAPTYPE_LITERAL) { 1157 DP("Forwarding first-private value " DPxMOD " to the target construct\n", 1158 DPxPTR(HstPtrBase)); 1159 TgtPtrBegin = HstPtrBase; 1160 TgtBaseOffset = 0; 1161 } else if (ArgTypes[I] & OMP_TGT_MAPTYPE_PRIVATE) { 1162 TgtBaseOffset = (intptr_t)HstPtrBase - (intptr_t)HstPtrBegin; 1163 // Can be marked for optimization if the next argument(s) do(es) not 1164 // depend on this one. 1165 const bool IsFirstPrivate = 1166 (I >= ArgNum - 1 || !(ArgTypes[I + 1] & OMP_TGT_MAPTYPE_MEMBER_OF)); 1167 Ret = PrivateArgumentManager.addArg( 1168 HstPtrBegin, ArgSizes[I], TgtBaseOffset, IsFirstPrivate, TgtPtrBegin, 1169 TgtArgs.size(), HstPtrName); 1170 if (Ret != OFFLOAD_SUCCESS) { 1171 REPORT("Failed to process %sprivate argument " DPxMOD "\n", 1172 (IsFirstPrivate ? "first-" : ""), DPxPTR(HstPtrBegin)); 1173 return OFFLOAD_FAIL; 1174 } 1175 } else { 1176 if (ArgTypes[I] & OMP_TGT_MAPTYPE_PTR_AND_OBJ) 1177 HstPtrBase = *reinterpret_cast<void **>(HstPtrBase); 1178 TgtPtrBegin = Device.getTgtPtrBegin(HstPtrBegin, ArgSizes[I], IsLast, 1179 false, IsHostPtr); 1180 TgtBaseOffset = (intptr_t)HstPtrBase - (intptr_t)HstPtrBegin; 1181 #ifdef OMPTARGET_DEBUG 1182 void *TgtPtrBase = (void *)((intptr_t)TgtPtrBegin + TgtBaseOffset); 1183 DP("Obtained target argument " DPxMOD " from host pointer " DPxMOD "\n", 1184 DPxPTR(TgtPtrBase), DPxPTR(HstPtrBegin)); 1185 #endif 1186 } 1187 TgtArgsPositions[I] = TgtArgs.size(); 1188 TgtArgs.push_back(TgtPtrBegin); 1189 TgtOffsets.push_back(TgtBaseOffset); 1190 } 1191 1192 assert(TgtArgs.size() == TgtOffsets.size() && 1193 "Size mismatch in arguments and offsets"); 1194 1195 // Pack and transfer first-private arguments 1196 Ret = PrivateArgumentManager.packAndTransfer(TgtArgs); 1197 if (Ret != OFFLOAD_SUCCESS) { 1198 DP("Failed to pack and transfer first private arguments\n"); 1199 return OFFLOAD_FAIL; 1200 } 1201 1202 return OFFLOAD_SUCCESS; 1203 } 1204 1205 /// Process data after launching the kernel, including transferring data back to 1206 /// host if needed and deallocating target memory of (first-)private variables. 1207 static int processDataAfter(ident_t *loc, int64_t DeviceId, void *HostPtr, 1208 int32_t ArgNum, void **ArgBases, void **Args, 1209 int64_t *ArgSizes, int64_t *ArgTypes, 1210 map_var_info_t *ArgNames, void **ArgMappers, 1211 PrivateArgumentManagerTy &PrivateArgumentManager, 1212 __tgt_async_info *AsyncInfo) { 1213 TIMESCOPE_WITH_NAME_AND_IDENT("mappingAfterTargetRegion", loc); 1214 DeviceTy &Device = PM->Devices[DeviceId]; 1215 1216 // Move data from device. 1217 int Ret = targetDataEnd(loc, Device, ArgNum, ArgBases, Args, ArgSizes, 1218 ArgTypes, ArgNames, ArgMappers, AsyncInfo); 1219 if (Ret != OFFLOAD_SUCCESS) { 1220 REPORT("Call to targetDataEnd failed, abort target.\n"); 1221 return OFFLOAD_FAIL; 1222 } 1223 1224 // Free target memory for private arguments 1225 Ret = PrivateArgumentManager.free(); 1226 if (Ret != OFFLOAD_SUCCESS) { 1227 REPORT("Failed to deallocate target memory for private args\n"); 1228 return OFFLOAD_FAIL; 1229 } 1230 1231 return OFFLOAD_SUCCESS; 1232 } 1233 } // namespace 1234 1235 /// performs the same actions as data_begin in case arg_num is 1236 /// non-zero and initiates run of the offloaded region on the target platform; 1237 /// if arg_num is non-zero after the region execution is done it also 1238 /// performs the same action as data_update and data_end above. This function 1239 /// returns 0 if it was able to transfer the execution to a target and an 1240 /// integer different from zero otherwise. 1241 int target(ident_t *loc, int64_t DeviceId, void *HostPtr, int32_t ArgNum, 1242 void **ArgBases, void **Args, int64_t *ArgSizes, int64_t *ArgTypes, 1243 map_var_info_t *ArgNames, void **ArgMappers, int32_t TeamNum, 1244 int32_t ThreadLimit, int IsTeamConstruct) { 1245 DeviceTy &Device = PM->Devices[DeviceId]; 1246 1247 TableMap *TM = getTableMap(HostPtr); 1248 // No map for this host pointer found! 1249 if (!TM) { 1250 REPORT("Host ptr " DPxMOD " does not have a matching target pointer.\n", 1251 DPxPTR(HostPtr)); 1252 return OFFLOAD_FAIL; 1253 } 1254 1255 // get target table. 1256 __tgt_target_table *TargetTable = nullptr; 1257 { 1258 std::lock_guard<std::mutex> TrlTblLock(PM->TrlTblMtx); 1259 assert(TM->Table->TargetsTable.size() > (size_t)DeviceId && 1260 "Not expecting a device ID outside the table's bounds!"); 1261 TargetTable = TM->Table->TargetsTable[DeviceId]; 1262 } 1263 assert(TargetTable && "Global data has not been mapped\n"); 1264 1265 __tgt_async_info AsyncInfo; 1266 1267 std::vector<void *> TgtArgs; 1268 std::vector<ptrdiff_t> TgtOffsets; 1269 1270 PrivateArgumentManagerTy PrivateArgumentManager(Device, &AsyncInfo); 1271 1272 int Ret; 1273 if (ArgNum) { 1274 // Process data, such as data mapping, before launching the kernel 1275 Ret = processDataBefore(loc, DeviceId, HostPtr, ArgNum, ArgBases, Args, 1276 ArgSizes, ArgTypes, ArgNames, ArgMappers, TgtArgs, 1277 TgtOffsets, PrivateArgumentManager, &AsyncInfo); 1278 if (Ret != OFFLOAD_SUCCESS) { 1279 REPORT("Failed to process data before launching the kernel.\n"); 1280 return OFFLOAD_FAIL; 1281 } 1282 } 1283 1284 // Get loop trip count 1285 uint64_t LoopTripCount = getLoopTripCount(DeviceId); 1286 1287 // Launch device execution. 1288 void *TgtEntryPtr = TargetTable->EntriesBegin[TM->Index].addr; 1289 DP("Launching target execution %s with pointer " DPxMOD " (index=%d).\n", 1290 TargetTable->EntriesBegin[TM->Index].name, DPxPTR(TgtEntryPtr), TM->Index); 1291 1292 { 1293 TIMESCOPE_WITH_NAME_AND_IDENT( 1294 IsTeamConstruct ? "runTargetTeamRegion" : "runTargetRegion", loc); 1295 if (IsTeamConstruct) 1296 Ret = Device.runTeamRegion(TgtEntryPtr, &TgtArgs[0], &TgtOffsets[0], 1297 TgtArgs.size(), TeamNum, ThreadLimit, 1298 LoopTripCount, &AsyncInfo); 1299 else 1300 Ret = Device.runRegion(TgtEntryPtr, &TgtArgs[0], &TgtOffsets[0], 1301 TgtArgs.size(), &AsyncInfo); 1302 } 1303 1304 if (Ret != OFFLOAD_SUCCESS) { 1305 REPORT("Executing target region abort target.\n"); 1306 return OFFLOAD_FAIL; 1307 } 1308 1309 if (ArgNum) { 1310 // Transfer data back and deallocate target memory for (first-)private 1311 // variables 1312 Ret = processDataAfter(loc, DeviceId, HostPtr, ArgNum, ArgBases, Args, 1313 ArgSizes, ArgTypes, ArgNames, ArgMappers, 1314 PrivateArgumentManager, &AsyncInfo); 1315 if (Ret != OFFLOAD_SUCCESS) { 1316 REPORT("Failed to process data after launching the kernel.\n"); 1317 return OFFLOAD_FAIL; 1318 } 1319 } else if (AsyncInfo.Queue) { 1320 // If ArgNum is zero, but AsyncInfo.Queue is valid, then the kernel doesn't 1321 // hava any argument, and the device supports async operations, so we need a 1322 // sync at this point. 1323 return syncDevice(Device, &AsyncInfo); 1324 } 1325 1326 return OFFLOAD_SUCCESS; 1327 } 1328