1 //===-- GPU implementation of the nanosleep function ----------------------===// 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 #include "src/time/nanosleep.h" 10 11 #include "src/__support/macros/config.h" 12 #include "time_utils.h" 13 14 namespace LIBC_NAMESPACE_DECL { 15 16 constexpr uint64_t TICKS_PER_SEC = 1000000000UL; 17 18 LLVM_LIBC_FUNCTION(int, nanosleep, 19 (const struct timespec *req, struct timespec *rem)) { 20 if (!GPU_CLOCKS_PER_SEC || !req) 21 return -1; 22 23 uint64_t nsecs = req->tv_nsec + req->tv_sec * TICKS_PER_SEC; 24 uint64_t tick_rate = TICKS_PER_SEC / GPU_CLOCKS_PER_SEC; 25 26 uint64_t start = gpu::fixed_frequency_clock(); 27 #if defined(LIBC_TARGET_ARCH_IS_NVPTX) 28 uint64_t end = start + (nsecs + tick_rate - 1) / tick_rate; 29 uint64_t cur = gpu::fixed_frequency_clock(); 30 // The NVPTX architecture supports sleeping and guaruntees the actual time 31 // slept will be somewhere between zero and twice the requested amount. Here 32 // we will sleep again if we undershot the time. 33 while (cur < end) { 34 if (__nvvm_reflect("__CUDA_ARCH") >= 700) 35 LIBC_INLINE_ASM("nanosleep.u32 %0;" ::"r"(nsecs)); 36 cur = gpu::fixed_frequency_clock(); 37 nsecs -= nsecs > cur - start ? cur - start : 0; 38 } 39 #elif defined(LIBC_TARGET_ARCH_IS_AMDGPU) 40 uint64_t end = start + (nsecs + tick_rate - 1) / tick_rate; 41 uint64_t cur = gpu::fixed_frequency_clock(); 42 // The AMDGPU architecture does not provide a sleep implementation with a 43 // known delay so we simply repeatedly sleep with a large value of ~960 clock 44 // cycles and check until we've passed the time using the known frequency. 45 __builtin_amdgcn_s_sleep(2); 46 while (cur < end) { 47 __builtin_amdgcn_s_sleep(15); 48 cur = gpu::fixed_frequency_clock(); 49 } 50 #else 51 // Sleeping is not supported. 52 if (rem) { 53 rem->tv_sec = req->tv_sec; 54 rem->tv_nsec = req->tv_nsec; 55 } 56 return -1; 57 #endif 58 uint64_t stop = gpu::fixed_frequency_clock(); 59 60 // Check to make sure we slept for at least the desired duration and set the 61 // remaining time if not. 62 uint64_t elapsed = (stop - start) * tick_rate; 63 if (elapsed < nsecs) { 64 if (rem) { 65 rem->tv_sec = (nsecs - elapsed) / TICKS_PER_SEC; 66 rem->tv_nsec = (nsecs - elapsed) % TICKS_PER_SEC; 67 } 68 return -1; 69 } 70 71 return 0; 72 } 73 74 } // namespace LIBC_NAMESPACE_DECL 75