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