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