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