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 // This should be an error upon entering an "omp target exit data". It 549 // should not be an error upon exiting an "omp target data" or "omp 550 // target". For "omp target data", Clang thus doesn't include present 551 // modifiers for end calls. For "omp target", we have not found a valid 552 // OpenMP program for which the error matters: it appears that, if a 553 // program can guarantee that data is present at the beginning of an 554 // "omp target" region so that there's no error there, that data is also 555 // guaranteed to be present at the end. 556 MESSAGE("device mapping required by 'present' map type modifier does " 557 "not exist for host address " DPxMOD " (%" PRId64 " bytes)", 558 DPxPTR(HstPtrBegin), DataSize); 559 return OFFLOAD_FAIL; 560 } 561 } else { 562 DP("There are %" PRId64 " bytes allocated at target address " DPxMOD 563 " - is%s last\n", 564 DataSize, DPxPTR(TgtPtrBegin), (IsLast ? "" : " not")); 565 } 566 567 bool DelEntry = IsLast || ForceDelete; 568 569 if ((ArgTypes[I] & OMP_TGT_MAPTYPE_MEMBER_OF) && 570 !(ArgTypes[I] & OMP_TGT_MAPTYPE_PTR_AND_OBJ)) { 571 DelEntry = false; // protect parent struct from being deallocated 572 } 573 574 if ((ArgTypes[I] & OMP_TGT_MAPTYPE_FROM) || DelEntry) { 575 // Move data back to the host 576 if (ArgTypes[I] & OMP_TGT_MAPTYPE_FROM) { 577 bool Always = ArgTypes[I] & OMP_TGT_MAPTYPE_ALWAYS; 578 bool CopyMember = false; 579 if (!(PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY) || 580 HasCloseModifier) { 581 if ((ArgTypes[I] & OMP_TGT_MAPTYPE_MEMBER_OF) && 582 !(ArgTypes[I] & OMP_TGT_MAPTYPE_PTR_AND_OBJ)) { 583 // Copy data only if the "parent" struct has RefCount==1. 584 int32_t ParentIdx = getParentIndex(ArgTypes[I]); 585 uint64_t ParentRC = Device.getMapEntryRefCnt(Args[ParentIdx]); 586 assert(ParentRC > 0 && "parent struct not found"); 587 if (ParentRC == 1) 588 CopyMember = true; 589 } 590 } 591 592 if ((DelEntry || Always || CopyMember) && 593 !(PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY && 594 TgtPtrBegin == HstPtrBegin)) { 595 DP("Moving %" PRId64 " bytes (tgt:" DPxMOD ") -> (hst:" DPxMOD ")\n", 596 DataSize, DPxPTR(TgtPtrBegin), DPxPTR(HstPtrBegin)); 597 Ret = Device.retrieveData(HstPtrBegin, TgtPtrBegin, DataSize, 598 AsyncInfo); 599 if (Ret != OFFLOAD_SUCCESS) { 600 REPORT("Copying data from device failed.\n"); 601 return OFFLOAD_FAIL; 602 } 603 } 604 } 605 606 // If we copied back to the host a struct/array containing pointers, we 607 // need to restore the original host pointer values from their shadow 608 // copies. If the struct is going to be deallocated, remove any remaining 609 // shadow pointer entries for this struct. 610 uintptr_t LB = (uintptr_t)HstPtrBegin; 611 uintptr_t UB = (uintptr_t)HstPtrBegin + DataSize; 612 Device.ShadowMtx.lock(); 613 for (ShadowPtrListTy::iterator Itr = Device.ShadowPtrMap.begin(); 614 Itr != Device.ShadowPtrMap.end();) { 615 void **ShadowHstPtrAddr = (void **)Itr->first; 616 617 // An STL map is sorted on its keys; use this property 618 // to quickly determine when to break out of the loop. 619 if ((uintptr_t)ShadowHstPtrAddr < LB) { 620 ++Itr; 621 continue; 622 } 623 if ((uintptr_t)ShadowHstPtrAddr >= UB) 624 break; 625 626 // If we copied the struct to the host, we need to restore the pointer. 627 if (ArgTypes[I] & OMP_TGT_MAPTYPE_FROM) { 628 DP("Restoring original host pointer value " DPxMOD " for host " 629 "pointer " DPxMOD "\n", 630 DPxPTR(Itr->second.HstPtrVal), DPxPTR(ShadowHstPtrAddr)); 631 *ShadowHstPtrAddr = Itr->second.HstPtrVal; 632 } 633 // If the struct is to be deallocated, remove the shadow entry. 634 if (DelEntry) { 635 DP("Removing shadow pointer " DPxMOD "\n", DPxPTR(ShadowHstPtrAddr)); 636 Itr = Device.ShadowPtrMap.erase(Itr); 637 } else { 638 ++Itr; 639 } 640 } 641 Device.ShadowMtx.unlock(); 642 643 // Add pointer to the buffer for later deallocation 644 if (DelEntry) 645 DeallocTgtPtrs.emplace_back(HstPtrBegin, DataSize, ForceDelete, 646 HasCloseModifier); 647 } 648 } 649 650 // TODO: We should not synchronize here but pass the AsyncInfo object to the 651 // allocate/deallocate device APIs. 652 // 653 // We need to synchronize before deallocating data. 654 Ret = AsyncInfo.synchronize(); 655 if (Ret != OFFLOAD_SUCCESS) 656 return OFFLOAD_FAIL; 657 658 // Deallocate target pointer 659 for (DeallocTgtPtrInfo &Info : DeallocTgtPtrs) { 660 Ret = Device.deallocTgtPtr(Info.HstPtrBegin, Info.DataSize, 661 Info.ForceDelete, Info.HasCloseModifier); 662 if (Ret != OFFLOAD_SUCCESS) { 663 REPORT("Deallocating data from device failed.\n"); 664 return OFFLOAD_FAIL; 665 } 666 } 667 668 return OFFLOAD_SUCCESS; 669 } 670 671 static int targetDataContiguous(ident_t *loc, DeviceTy &Device, void *ArgsBase, 672 void *HstPtrBegin, int64_t ArgSize, 673 int64_t ArgType, AsyncInfoTy &AsyncInfo) { 674 TIMESCOPE_WITH_IDENT(loc); 675 bool IsLast, IsHostPtr; 676 void *TgtPtrBegin = Device.getTgtPtrBegin(HstPtrBegin, ArgSize, IsLast, false, 677 IsHostPtr, /*MustContain=*/true); 678 if (!TgtPtrBegin) { 679 DP("hst data:" DPxMOD " not found, becomes a noop\n", DPxPTR(HstPtrBegin)); 680 if (ArgType & OMP_TGT_MAPTYPE_PRESENT) { 681 MESSAGE("device mapping required by 'present' motion modifier does not " 682 "exist for host address " DPxMOD " (%" PRId64 " bytes)", 683 DPxPTR(HstPtrBegin), ArgSize); 684 return OFFLOAD_FAIL; 685 } 686 return OFFLOAD_SUCCESS; 687 } 688 689 if (PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY && 690 TgtPtrBegin == HstPtrBegin) { 691 DP("hst data:" DPxMOD " unified and shared, becomes a noop\n", 692 DPxPTR(HstPtrBegin)); 693 return OFFLOAD_SUCCESS; 694 } 695 696 if (ArgType & OMP_TGT_MAPTYPE_FROM) { 697 DP("Moving %" PRId64 " bytes (tgt:" DPxMOD ") -> (hst:" DPxMOD ")\n", 698 ArgSize, DPxPTR(TgtPtrBegin), DPxPTR(HstPtrBegin)); 699 int Ret = Device.retrieveData(HstPtrBegin, TgtPtrBegin, ArgSize, AsyncInfo); 700 if (Ret != OFFLOAD_SUCCESS) { 701 REPORT("Copying data from device failed.\n"); 702 return OFFLOAD_FAIL; 703 } 704 705 uintptr_t LB = (uintptr_t)HstPtrBegin; 706 uintptr_t UB = (uintptr_t)HstPtrBegin + ArgSize; 707 Device.ShadowMtx.lock(); 708 for (ShadowPtrListTy::iterator IT = Device.ShadowPtrMap.begin(); 709 IT != Device.ShadowPtrMap.end(); ++IT) { 710 void **ShadowHstPtrAddr = (void **)IT->first; 711 if ((uintptr_t)ShadowHstPtrAddr < LB) 712 continue; 713 if ((uintptr_t)ShadowHstPtrAddr >= UB) 714 break; 715 DP("Restoring original host pointer value " DPxMOD 716 " for host pointer " DPxMOD "\n", 717 DPxPTR(IT->second.HstPtrVal), DPxPTR(ShadowHstPtrAddr)); 718 *ShadowHstPtrAddr = IT->second.HstPtrVal; 719 } 720 Device.ShadowMtx.unlock(); 721 } 722 723 if (ArgType & OMP_TGT_MAPTYPE_TO) { 724 DP("Moving %" PRId64 " bytes (hst:" DPxMOD ") -> (tgt:" DPxMOD ")\n", 725 ArgSize, DPxPTR(HstPtrBegin), DPxPTR(TgtPtrBegin)); 726 int Ret = Device.submitData(TgtPtrBegin, HstPtrBegin, ArgSize, AsyncInfo); 727 if (Ret != OFFLOAD_SUCCESS) { 728 REPORT("Copying data to device failed.\n"); 729 return OFFLOAD_FAIL; 730 } 731 732 uintptr_t LB = (uintptr_t)HstPtrBegin; 733 uintptr_t UB = (uintptr_t)HstPtrBegin + ArgSize; 734 Device.ShadowMtx.lock(); 735 for (ShadowPtrListTy::iterator IT = Device.ShadowPtrMap.begin(); 736 IT != Device.ShadowPtrMap.end(); ++IT) { 737 void **ShadowHstPtrAddr = (void **)IT->first; 738 if ((uintptr_t)ShadowHstPtrAddr < LB) 739 continue; 740 if ((uintptr_t)ShadowHstPtrAddr >= UB) 741 break; 742 DP("Restoring original target pointer value " DPxMOD " for target " 743 "pointer " DPxMOD "\n", 744 DPxPTR(IT->second.TgtPtrVal), DPxPTR(IT->second.TgtPtrAddr)); 745 Ret = Device.submitData(IT->second.TgtPtrAddr, &IT->second.TgtPtrVal, 746 sizeof(void *), AsyncInfo); 747 if (Ret != OFFLOAD_SUCCESS) { 748 REPORT("Copying data to device failed.\n"); 749 Device.ShadowMtx.unlock(); 750 return OFFLOAD_FAIL; 751 } 752 } 753 Device.ShadowMtx.unlock(); 754 } 755 return OFFLOAD_SUCCESS; 756 } 757 758 static int targetDataNonContiguous(ident_t *loc, DeviceTy &Device, 759 void *ArgsBase, 760 __tgt_target_non_contig *NonContig, 761 uint64_t Size, int64_t ArgType, 762 int CurrentDim, int DimSize, uint64_t Offset, 763 AsyncInfoTy &AsyncInfo) { 764 TIMESCOPE_WITH_IDENT(loc); 765 int Ret = OFFLOAD_SUCCESS; 766 if (CurrentDim < DimSize) { 767 for (unsigned int I = 0; I < NonContig[CurrentDim].Count; ++I) { 768 uint64_t CurOffset = 769 (NonContig[CurrentDim].Offset + I) * NonContig[CurrentDim].Stride; 770 // we only need to transfer the first element for the last dimension 771 // since we've already got a contiguous piece. 772 if (CurrentDim != DimSize - 1 || I == 0) { 773 Ret = targetDataNonContiguous(loc, Device, ArgsBase, NonContig, Size, 774 ArgType, CurrentDim + 1, DimSize, 775 Offset + CurOffset, AsyncInfo); 776 // Stop the whole process if any contiguous piece returns anything 777 // other than OFFLOAD_SUCCESS. 778 if (Ret != OFFLOAD_SUCCESS) 779 return Ret; 780 } 781 } 782 } else { 783 char *Ptr = (char *)ArgsBase + Offset; 784 DP("Transfer of non-contiguous : host ptr " DPxMOD " offset %" PRIu64 785 " len %" PRIu64 "\n", 786 DPxPTR(Ptr), Offset, Size); 787 Ret = targetDataContiguous(loc, Device, ArgsBase, Ptr, Size, ArgType, 788 AsyncInfo); 789 } 790 return Ret; 791 } 792 793 static int getNonContigMergedDimension(__tgt_target_non_contig *NonContig, 794 int32_t DimSize) { 795 int RemovedDim = 0; 796 for (int I = DimSize - 1; I > 0; --I) { 797 if (NonContig[I].Count * NonContig[I].Stride == NonContig[I - 1].Stride) 798 RemovedDim++; 799 } 800 return RemovedDim; 801 } 802 803 /// Internal function to pass data to/from the target. 804 int targetDataUpdate(ident_t *loc, DeviceTy &Device, int32_t ArgNum, 805 void **ArgsBase, void **Args, int64_t *ArgSizes, 806 int64_t *ArgTypes, map_var_info_t *ArgNames, 807 void **ArgMappers, AsyncInfoTy &AsyncInfo) { 808 // process each input. 809 for (int32_t I = 0; I < ArgNum; ++I) { 810 if ((ArgTypes[I] & OMP_TGT_MAPTYPE_LITERAL) || 811 (ArgTypes[I] & OMP_TGT_MAPTYPE_PRIVATE)) 812 continue; 813 814 if (ArgMappers && ArgMappers[I]) { 815 // Instead of executing the regular path of targetDataUpdate, call the 816 // targetDataMapper variant which will call targetDataUpdate again 817 // with new arguments. 818 DP("Calling targetDataMapper for the %dth argument\n", I); 819 820 map_var_info_t ArgName = (!ArgNames) ? nullptr : ArgNames[I]; 821 int Ret = targetDataMapper(loc, Device, ArgsBase[I], Args[I], ArgSizes[I], 822 ArgTypes[I], ArgName, ArgMappers[I], AsyncInfo, 823 targetDataUpdate); 824 825 if (Ret != OFFLOAD_SUCCESS) { 826 REPORT("Call to targetDataUpdate via targetDataMapper for custom mapper" 827 " failed.\n"); 828 return OFFLOAD_FAIL; 829 } 830 831 // Skip the rest of this function, continue to the next argument. 832 continue; 833 } 834 835 int Ret = OFFLOAD_SUCCESS; 836 837 if (ArgTypes[I] & OMP_TGT_MAPTYPE_NON_CONTIG) { 838 __tgt_target_non_contig *NonContig = (__tgt_target_non_contig *)Args[I]; 839 int32_t DimSize = ArgSizes[I]; 840 uint64_t Size = 841 NonContig[DimSize - 1].Count * NonContig[DimSize - 1].Stride; 842 int32_t MergedDim = getNonContigMergedDimension(NonContig, DimSize); 843 Ret = targetDataNonContiguous( 844 loc, Device, ArgsBase[I], NonContig, Size, ArgTypes[I], 845 /*current_dim=*/0, DimSize - MergedDim, /*offset=*/0, AsyncInfo); 846 } else { 847 Ret = targetDataContiguous(loc, Device, ArgsBase[I], Args[I], ArgSizes[I], 848 ArgTypes[I], AsyncInfo); 849 } 850 if (Ret == OFFLOAD_FAIL) 851 return OFFLOAD_FAIL; 852 } 853 return OFFLOAD_SUCCESS; 854 } 855 856 static const unsigned LambdaMapping = OMP_TGT_MAPTYPE_PTR_AND_OBJ | 857 OMP_TGT_MAPTYPE_LITERAL | 858 OMP_TGT_MAPTYPE_IMPLICIT; 859 static bool isLambdaMapping(int64_t Mapping) { 860 return (Mapping & LambdaMapping) == LambdaMapping; 861 } 862 863 namespace { 864 /// Find the table information in the map or look it up in the translation 865 /// tables. 866 TableMap *getTableMap(void *HostPtr) { 867 std::lock_guard<std::mutex> TblMapLock(PM->TblMapMtx); 868 HostPtrToTableMapTy::iterator TableMapIt = 869 PM->HostPtrToTableMap.find(HostPtr); 870 871 if (TableMapIt != PM->HostPtrToTableMap.end()) 872 return &TableMapIt->second; 873 874 // We don't have a map. So search all the registered libraries. 875 TableMap *TM = nullptr; 876 std::lock_guard<std::mutex> TrlTblLock(PM->TrlTblMtx); 877 for (HostEntriesBeginToTransTableTy::iterator Itr = 878 PM->HostEntriesBeginToTransTable.begin(); 879 Itr != PM->HostEntriesBeginToTransTable.end(); ++Itr) { 880 // get the translation table (which contains all the good info). 881 TranslationTable *TransTable = &Itr->second; 882 // iterate over all the host table entries to see if we can locate the 883 // host_ptr. 884 __tgt_offload_entry *Cur = TransTable->HostTable.EntriesBegin; 885 for (uint32_t I = 0; Cur < TransTable->HostTable.EntriesEnd; ++Cur, ++I) { 886 if (Cur->addr != HostPtr) 887 continue; 888 // we got a match, now fill the HostPtrToTableMap so that we 889 // may avoid this search next time. 890 TM = &(PM->HostPtrToTableMap)[HostPtr]; 891 TM->Table = TransTable; 892 TM->Index = I; 893 return TM; 894 } 895 } 896 897 return nullptr; 898 } 899 900 /// Get loop trip count 901 /// FIXME: This function will not work right if calling 902 /// __kmpc_push_target_tripcount in one thread but doing offloading in another 903 /// thread, which might occur when we call task yield. 904 uint64_t getLoopTripCount(int64_t DeviceId) { 905 DeviceTy &Device = PM->Devices[DeviceId]; 906 uint64_t LoopTripCount = 0; 907 908 { 909 std::lock_guard<std::mutex> TblMapLock(PM->TblMapMtx); 910 auto I = Device.LoopTripCnt.find(__kmpc_global_thread_num(NULL)); 911 if (I != Device.LoopTripCnt.end()) { 912 LoopTripCount = I->second; 913 Device.LoopTripCnt.erase(I); 914 DP("loop trip count is %" PRIu64 ".\n", LoopTripCount); 915 } 916 } 917 918 return LoopTripCount; 919 } 920 921 /// A class manages private arguments in a target region. 922 class PrivateArgumentManagerTy { 923 /// A data structure for the information of first-private arguments. We can 924 /// use this information to optimize data transfer by packing all 925 /// first-private arguments and transfer them all at once. 926 struct FirstPrivateArgInfoTy { 927 /// The index of the element in \p TgtArgs corresponding to the argument 928 const int Index; 929 /// Host pointer begin 930 const char *HstPtrBegin; 931 /// Host pointer end 932 const char *HstPtrEnd; 933 /// Aligned size 934 const int64_t AlignedSize; 935 /// Host pointer name 936 const map_var_info_t HstPtrName = nullptr; 937 938 FirstPrivateArgInfoTy(int Index, const void *HstPtr, int64_t Size, 939 const map_var_info_t HstPtrName = nullptr) 940 : Index(Index), HstPtrBegin(reinterpret_cast<const char *>(HstPtr)), 941 HstPtrEnd(HstPtrBegin + Size), AlignedSize(Size + Size % Alignment), 942 HstPtrName(HstPtrName) {} 943 }; 944 945 /// A vector of target pointers for all private arguments 946 std::vector<void *> TgtPtrs; 947 948 /// A vector of information of all first-private arguments to be packed 949 std::vector<FirstPrivateArgInfoTy> FirstPrivateArgInfo; 950 /// Host buffer for all arguments to be packed 951 std::vector<char> FirstPrivateArgBuffer; 952 /// The total size of all arguments to be packed 953 int64_t FirstPrivateArgSize = 0; 954 955 /// A reference to the \p DeviceTy object 956 DeviceTy &Device; 957 /// A pointer to a \p AsyncInfoTy object 958 AsyncInfoTy &AsyncInfo; 959 960 // TODO: What would be the best value here? Should we make it configurable? 961 // If the size is larger than this threshold, we will allocate and transfer it 962 // immediately instead of packing it. 963 static constexpr const int64_t FirstPrivateArgSizeThreshold = 1024; 964 965 public: 966 /// Constructor 967 PrivateArgumentManagerTy(DeviceTy &Dev, AsyncInfoTy &AsyncInfo) 968 : Device(Dev), AsyncInfo(AsyncInfo) {} 969 970 /// Add a private argument 971 int addArg(void *HstPtr, int64_t ArgSize, int64_t ArgOffset, 972 bool IsFirstPrivate, void *&TgtPtr, int TgtArgsIndex, 973 const map_var_info_t HstPtrName = nullptr) { 974 // If the argument is not first-private, or its size is greater than a 975 // predefined threshold, we will allocate memory and issue the transfer 976 // immediately. 977 if (ArgSize > FirstPrivateArgSizeThreshold || !IsFirstPrivate) { 978 TgtPtr = Device.allocData(ArgSize, HstPtr); 979 if (!TgtPtr) { 980 DP("Data allocation for %sprivate array " DPxMOD " failed.\n", 981 (IsFirstPrivate ? "first-" : ""), DPxPTR(HstPtr)); 982 return OFFLOAD_FAIL; 983 } 984 #ifdef OMPTARGET_DEBUG 985 void *TgtPtrBase = (void *)((intptr_t)TgtPtr + ArgOffset); 986 DP("Allocated %" PRId64 " bytes of target memory at " DPxMOD 987 " for %sprivate array " DPxMOD " - pushing target argument " DPxMOD 988 "\n", 989 ArgSize, DPxPTR(TgtPtr), (IsFirstPrivate ? "first-" : ""), 990 DPxPTR(HstPtr), DPxPTR(TgtPtrBase)); 991 #endif 992 // If first-private, copy data from host 993 if (IsFirstPrivate) { 994 int Ret = Device.submitData(TgtPtr, HstPtr, ArgSize, AsyncInfo); 995 if (Ret != OFFLOAD_SUCCESS) { 996 DP("Copying data to device failed, failed.\n"); 997 return OFFLOAD_FAIL; 998 } 999 } 1000 TgtPtrs.push_back(TgtPtr); 1001 } else { 1002 DP("Firstprivate array " DPxMOD " of size %" PRId64 " will be packed\n", 1003 DPxPTR(HstPtr), ArgSize); 1004 // When reach this point, the argument must meet all following 1005 // requirements: 1006 // 1. Its size does not exceed the threshold (see the comment for 1007 // FirstPrivateArgSizeThreshold); 1008 // 2. It must be first-private (needs to be mapped to target device). 1009 // We will pack all this kind of arguments to transfer them all at once 1010 // to reduce the number of data transfer. We will not take 1011 // non-first-private arguments, aka. private arguments that doesn't need 1012 // to be mapped to target device, into account because data allocation 1013 // can be very efficient with memory manager. 1014 1015 // Placeholder value 1016 TgtPtr = nullptr; 1017 FirstPrivateArgInfo.emplace_back(TgtArgsIndex, HstPtr, ArgSize, 1018 HstPtrName); 1019 FirstPrivateArgSize += FirstPrivateArgInfo.back().AlignedSize; 1020 } 1021 1022 return OFFLOAD_SUCCESS; 1023 } 1024 1025 /// Pack first-private arguments, replace place holder pointers in \p TgtArgs, 1026 /// and start the transfer. 1027 int packAndTransfer(std::vector<void *> &TgtArgs) { 1028 if (!FirstPrivateArgInfo.empty()) { 1029 assert(FirstPrivateArgSize != 0 && 1030 "FirstPrivateArgSize is 0 but FirstPrivateArgInfo is empty"); 1031 FirstPrivateArgBuffer.resize(FirstPrivateArgSize, 0); 1032 auto Itr = FirstPrivateArgBuffer.begin(); 1033 // Copy all host data to this buffer 1034 for (FirstPrivateArgInfoTy &Info : FirstPrivateArgInfo) { 1035 std::copy(Info.HstPtrBegin, Info.HstPtrEnd, Itr); 1036 Itr = std::next(Itr, Info.AlignedSize); 1037 } 1038 // Allocate target memory 1039 void *TgtPtr = 1040 Device.allocData(FirstPrivateArgSize, FirstPrivateArgBuffer.data()); 1041 if (TgtPtr == nullptr) { 1042 DP("Failed to allocate target memory for private arguments.\n"); 1043 return OFFLOAD_FAIL; 1044 } 1045 TgtPtrs.push_back(TgtPtr); 1046 DP("Allocated %" PRId64 " bytes of target memory at " DPxMOD "\n", 1047 FirstPrivateArgSize, DPxPTR(TgtPtr)); 1048 // Transfer data to target device 1049 int Ret = Device.submitData(TgtPtr, FirstPrivateArgBuffer.data(), 1050 FirstPrivateArgSize, AsyncInfo); 1051 if (Ret != OFFLOAD_SUCCESS) { 1052 DP("Failed to submit data of private arguments.\n"); 1053 return OFFLOAD_FAIL; 1054 } 1055 // Fill in all placeholder pointers 1056 auto TP = reinterpret_cast<uintptr_t>(TgtPtr); 1057 for (FirstPrivateArgInfoTy &Info : FirstPrivateArgInfo) { 1058 void *&Ptr = TgtArgs[Info.Index]; 1059 assert(Ptr == nullptr && "Target pointer is already set by mistaken"); 1060 Ptr = reinterpret_cast<void *>(TP); 1061 TP += Info.AlignedSize; 1062 DP("Firstprivate array " DPxMOD " of size %" PRId64 " mapped to " DPxMOD 1063 "\n", 1064 DPxPTR(Info.HstPtrBegin), Info.HstPtrEnd - Info.HstPtrBegin, 1065 DPxPTR(Ptr)); 1066 } 1067 } 1068 1069 return OFFLOAD_SUCCESS; 1070 } 1071 1072 /// Free all target memory allocated for private arguments 1073 int free() { 1074 for (void *P : TgtPtrs) { 1075 int Ret = Device.deleteData(P); 1076 if (Ret != OFFLOAD_SUCCESS) { 1077 DP("Deallocation of (first-)private arrays failed.\n"); 1078 return OFFLOAD_FAIL; 1079 } 1080 } 1081 1082 TgtPtrs.clear(); 1083 1084 return OFFLOAD_SUCCESS; 1085 } 1086 }; 1087 1088 /// Process data before launching the kernel, including calling targetDataBegin 1089 /// to map and transfer data to target device, transferring (first-)private 1090 /// variables. 1091 static int processDataBefore(ident_t *loc, int64_t DeviceId, void *HostPtr, 1092 int32_t ArgNum, void **ArgBases, void **Args, 1093 int64_t *ArgSizes, int64_t *ArgTypes, 1094 map_var_info_t *ArgNames, void **ArgMappers, 1095 std::vector<void *> &TgtArgs, 1096 std::vector<ptrdiff_t> &TgtOffsets, 1097 PrivateArgumentManagerTy &PrivateArgumentManager, 1098 AsyncInfoTy &AsyncInfo) { 1099 TIMESCOPE_WITH_NAME_AND_IDENT("mappingBeforeTargetRegion", loc); 1100 DeviceTy &Device = PM->Devices[DeviceId]; 1101 int Ret = targetDataBegin(loc, Device, ArgNum, ArgBases, Args, ArgSizes, 1102 ArgTypes, ArgNames, ArgMappers, AsyncInfo); 1103 if (Ret != OFFLOAD_SUCCESS) { 1104 REPORT("Call to targetDataBegin failed, abort target.\n"); 1105 return OFFLOAD_FAIL; 1106 } 1107 1108 // List of (first-)private arrays allocated for this target region 1109 std::vector<int> TgtArgsPositions(ArgNum, -1); 1110 1111 for (int32_t I = 0; I < ArgNum; ++I) { 1112 if (!(ArgTypes[I] & OMP_TGT_MAPTYPE_TARGET_PARAM)) { 1113 // This is not a target parameter, do not push it into TgtArgs. 1114 // Check for lambda mapping. 1115 if (isLambdaMapping(ArgTypes[I])) { 1116 assert((ArgTypes[I] & OMP_TGT_MAPTYPE_MEMBER_OF) && 1117 "PTR_AND_OBJ must be also MEMBER_OF."); 1118 unsigned Idx = getParentIndex(ArgTypes[I]); 1119 int TgtIdx = TgtArgsPositions[Idx]; 1120 assert(TgtIdx != -1 && "Base address must be translated already."); 1121 // The parent lambda must be processed already and it must be the last 1122 // in TgtArgs and TgtOffsets arrays. 1123 void *HstPtrVal = Args[I]; 1124 void *HstPtrBegin = ArgBases[I]; 1125 void *HstPtrBase = Args[Idx]; 1126 bool IsLast, IsHostPtr; // unused. 1127 void *TgtPtrBase = 1128 (void *)((intptr_t)TgtArgs[TgtIdx] + TgtOffsets[TgtIdx]); 1129 DP("Parent lambda base " DPxMOD "\n", DPxPTR(TgtPtrBase)); 1130 uint64_t Delta = (uint64_t)HstPtrBegin - (uint64_t)HstPtrBase; 1131 void *TgtPtrBegin = (void *)((uintptr_t)TgtPtrBase + Delta); 1132 void *&PointerTgtPtrBegin = AsyncInfo.getVoidPtrLocation(); 1133 PointerTgtPtrBegin = Device.getTgtPtrBegin(HstPtrVal, ArgSizes[I], 1134 IsLast, false, IsHostPtr); 1135 if (!PointerTgtPtrBegin) { 1136 DP("No lambda captured variable mapped (" DPxMOD ") - ignored\n", 1137 DPxPTR(HstPtrVal)); 1138 continue; 1139 } 1140 if (PM->RTLs.RequiresFlags & OMP_REQ_UNIFIED_SHARED_MEMORY && 1141 TgtPtrBegin == HstPtrBegin) { 1142 DP("Unified memory is active, no need to map lambda captured" 1143 "variable (" DPxMOD ")\n", 1144 DPxPTR(HstPtrVal)); 1145 continue; 1146 } 1147 DP("Update lambda reference (" DPxMOD ") -> [" DPxMOD "]\n", 1148 DPxPTR(PointerTgtPtrBegin), DPxPTR(TgtPtrBegin)); 1149 Ret = Device.submitData(TgtPtrBegin, &PointerTgtPtrBegin, 1150 sizeof(void *), AsyncInfo); 1151 if (Ret != OFFLOAD_SUCCESS) { 1152 REPORT("Copying data to device failed.\n"); 1153 return OFFLOAD_FAIL; 1154 } 1155 } 1156 continue; 1157 } 1158 void *HstPtrBegin = Args[I]; 1159 void *HstPtrBase = ArgBases[I]; 1160 void *TgtPtrBegin; 1161 map_var_info_t HstPtrName = (!ArgNames) ? nullptr : ArgNames[I]; 1162 ptrdiff_t TgtBaseOffset; 1163 bool IsLast, IsHostPtr; // unused. 1164 if (ArgTypes[I] & OMP_TGT_MAPTYPE_LITERAL) { 1165 DP("Forwarding first-private value " DPxMOD " to the target construct\n", 1166 DPxPTR(HstPtrBase)); 1167 TgtPtrBegin = HstPtrBase; 1168 TgtBaseOffset = 0; 1169 } else if (ArgTypes[I] & OMP_TGT_MAPTYPE_PRIVATE) { 1170 TgtBaseOffset = (intptr_t)HstPtrBase - (intptr_t)HstPtrBegin; 1171 // Can be marked for optimization if the next argument(s) do(es) not 1172 // depend on this one. 1173 const bool IsFirstPrivate = 1174 (I >= ArgNum - 1 || !(ArgTypes[I + 1] & OMP_TGT_MAPTYPE_MEMBER_OF)); 1175 Ret = PrivateArgumentManager.addArg( 1176 HstPtrBegin, ArgSizes[I], TgtBaseOffset, IsFirstPrivate, TgtPtrBegin, 1177 TgtArgs.size(), HstPtrName); 1178 if (Ret != OFFLOAD_SUCCESS) { 1179 REPORT("Failed to process %sprivate argument " DPxMOD "\n", 1180 (IsFirstPrivate ? "first-" : ""), DPxPTR(HstPtrBegin)); 1181 return OFFLOAD_FAIL; 1182 } 1183 } else { 1184 if (ArgTypes[I] & OMP_TGT_MAPTYPE_PTR_AND_OBJ) 1185 HstPtrBase = *reinterpret_cast<void **>(HstPtrBase); 1186 TgtPtrBegin = Device.getTgtPtrBegin(HstPtrBegin, ArgSizes[I], IsLast, 1187 false, IsHostPtr); 1188 TgtBaseOffset = (intptr_t)HstPtrBase - (intptr_t)HstPtrBegin; 1189 #ifdef OMPTARGET_DEBUG 1190 void *TgtPtrBase = (void *)((intptr_t)TgtPtrBegin + TgtBaseOffset); 1191 DP("Obtained target argument " DPxMOD " from host pointer " DPxMOD "\n", 1192 DPxPTR(TgtPtrBase), DPxPTR(HstPtrBegin)); 1193 #endif 1194 } 1195 TgtArgsPositions[I] = TgtArgs.size(); 1196 TgtArgs.push_back(TgtPtrBegin); 1197 TgtOffsets.push_back(TgtBaseOffset); 1198 } 1199 1200 assert(TgtArgs.size() == TgtOffsets.size() && 1201 "Size mismatch in arguments and offsets"); 1202 1203 // Pack and transfer first-private arguments 1204 Ret = PrivateArgumentManager.packAndTransfer(TgtArgs); 1205 if (Ret != OFFLOAD_SUCCESS) { 1206 DP("Failed to pack and transfer first private arguments\n"); 1207 return OFFLOAD_FAIL; 1208 } 1209 1210 return OFFLOAD_SUCCESS; 1211 } 1212 1213 /// Process data after launching the kernel, including transferring data back to 1214 /// host if needed and deallocating target memory of (first-)private variables. 1215 static int processDataAfter(ident_t *loc, int64_t DeviceId, void *HostPtr, 1216 int32_t ArgNum, void **ArgBases, void **Args, 1217 int64_t *ArgSizes, int64_t *ArgTypes, 1218 map_var_info_t *ArgNames, void **ArgMappers, 1219 PrivateArgumentManagerTy &PrivateArgumentManager, 1220 AsyncInfoTy &AsyncInfo) { 1221 TIMESCOPE_WITH_NAME_AND_IDENT("mappingAfterTargetRegion", loc); 1222 DeviceTy &Device = PM->Devices[DeviceId]; 1223 1224 // Move data from device. 1225 int Ret = targetDataEnd(loc, Device, ArgNum, ArgBases, Args, ArgSizes, 1226 ArgTypes, ArgNames, ArgMappers, AsyncInfo); 1227 if (Ret != OFFLOAD_SUCCESS) { 1228 REPORT("Call to targetDataEnd failed, abort target.\n"); 1229 return OFFLOAD_FAIL; 1230 } 1231 1232 // Free target memory for private arguments 1233 Ret = PrivateArgumentManager.free(); 1234 if (Ret != OFFLOAD_SUCCESS) { 1235 REPORT("Failed to deallocate target memory for private args\n"); 1236 return OFFLOAD_FAIL; 1237 } 1238 1239 return OFFLOAD_SUCCESS; 1240 } 1241 } // namespace 1242 1243 /// performs the same actions as data_begin in case arg_num is 1244 /// non-zero and initiates run of the offloaded region on the target platform; 1245 /// if arg_num is non-zero after the region execution is done it also 1246 /// performs the same action as data_update and data_end above. This function 1247 /// returns 0 if it was able to transfer the execution to a target and an 1248 /// integer different from zero otherwise. 1249 int target(ident_t *loc, DeviceTy &Device, void *HostPtr, int32_t ArgNum, 1250 void **ArgBases, void **Args, int64_t *ArgSizes, int64_t *ArgTypes, 1251 map_var_info_t *ArgNames, void **ArgMappers, int32_t TeamNum, 1252 int32_t ThreadLimit, int IsTeamConstruct, AsyncInfoTy &AsyncInfo) { 1253 int32_t DeviceId = Device.DeviceID; 1254 1255 TableMap *TM = getTableMap(HostPtr); 1256 // No map for this host pointer found! 1257 if (!TM) { 1258 REPORT("Host ptr " DPxMOD " does not have a matching target pointer.\n", 1259 DPxPTR(HostPtr)); 1260 return OFFLOAD_FAIL; 1261 } 1262 1263 // get target table. 1264 __tgt_target_table *TargetTable = nullptr; 1265 { 1266 std::lock_guard<std::mutex> TrlTblLock(PM->TrlTblMtx); 1267 assert(TM->Table->TargetsTable.size() > (size_t)DeviceId && 1268 "Not expecting a device ID outside the table's bounds!"); 1269 TargetTable = TM->Table->TargetsTable[DeviceId]; 1270 } 1271 assert(TargetTable && "Global data has not been mapped\n"); 1272 1273 std::vector<void *> TgtArgs; 1274 std::vector<ptrdiff_t> TgtOffsets; 1275 1276 PrivateArgumentManagerTy PrivateArgumentManager(Device, AsyncInfo); 1277 1278 int Ret; 1279 if (ArgNum) { 1280 // Process data, such as data mapping, before launching the kernel 1281 Ret = processDataBefore(loc, DeviceId, HostPtr, ArgNum, ArgBases, Args, 1282 ArgSizes, ArgTypes, ArgNames, ArgMappers, TgtArgs, 1283 TgtOffsets, PrivateArgumentManager, AsyncInfo); 1284 if (Ret != OFFLOAD_SUCCESS) { 1285 REPORT("Failed to process data before launching the kernel.\n"); 1286 return OFFLOAD_FAIL; 1287 } 1288 } 1289 1290 // Get loop trip count 1291 uint64_t LoopTripCount = getLoopTripCount(DeviceId); 1292 1293 // Launch device execution. 1294 void *TgtEntryPtr = TargetTable->EntriesBegin[TM->Index].addr; 1295 DP("Launching target execution %s with pointer " DPxMOD " (index=%d).\n", 1296 TargetTable->EntriesBegin[TM->Index].name, DPxPTR(TgtEntryPtr), TM->Index); 1297 1298 { 1299 TIMESCOPE_WITH_NAME_AND_IDENT( 1300 IsTeamConstruct ? "runTargetTeamRegion" : "runTargetRegion", loc); 1301 if (IsTeamConstruct) 1302 Ret = Device.runTeamRegion(TgtEntryPtr, &TgtArgs[0], &TgtOffsets[0], 1303 TgtArgs.size(), TeamNum, ThreadLimit, 1304 LoopTripCount, AsyncInfo); 1305 else 1306 Ret = Device.runRegion(TgtEntryPtr, &TgtArgs[0], &TgtOffsets[0], 1307 TgtArgs.size(), AsyncInfo); 1308 } 1309 1310 if (Ret != OFFLOAD_SUCCESS) { 1311 REPORT("Executing target region abort target.\n"); 1312 return OFFLOAD_FAIL; 1313 } 1314 1315 if (ArgNum) { 1316 // Transfer data back and deallocate target memory for (first-)private 1317 // variables 1318 Ret = processDataAfter(loc, DeviceId, HostPtr, ArgNum, ArgBases, Args, 1319 ArgSizes, ArgTypes, ArgNames, ArgMappers, 1320 PrivateArgumentManager, AsyncInfo); 1321 if (Ret != OFFLOAD_SUCCESS) { 1322 REPORT("Failed to process data after launching the kernel.\n"); 1323 return OFFLOAD_FAIL; 1324 } 1325 } 1326 1327 return OFFLOAD_SUCCESS; 1328 } 1329