1 //===--------- Misc.cpp - OpenMP device misc interfaces ----------- 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 // 10 //===----------------------------------------------------------------------===// 11 12 #include "Allocator.h" 13 #include "Configuration.h" 14 #include "DeviceTypes.h" 15 #include "Shared/RPCOpcodes.h" 16 #include "shared/rpc.h" 17 18 #include "Debug.h" 19 20 #pragma omp begin declare target device_type(nohost) 21 22 namespace ompx { 23 namespace impl { 24 25 double getWTick(); 26 27 double getWTime(); 28 29 /// AMDGCN Implementation 30 /// 31 ///{ 32 #pragma omp begin declare variant match(device = {arch(amdgcn)}) 33 34 double getWTick() { 35 // The number of ticks per second for the AMDGPU clock varies by card and can 36 // only be retrived by querying the driver. We rely on the device environment 37 // to inform us what the proper frequency is. 38 return 1.0 / config::getClockFrequency(); 39 } 40 41 double getWTime() { 42 return static_cast<double>(__builtin_readsteadycounter()) * getWTick(); 43 } 44 45 #pragma omp end declare variant 46 47 /// NVPTX Implementation 48 /// 49 ///{ 50 #pragma omp begin declare variant match( \ 51 device = {arch(nvptx, nvptx64)}, \ 52 implementation = {extension(match_any)}) 53 54 double getWTick() { 55 // Timer precision is 1ns 56 return ((double)1E-9); 57 } 58 59 double getWTime() { 60 uint64_t nsecs = __nvvm_read_ptx_sreg_globaltimer(); 61 return static_cast<double>(nsecs) * getWTick(); 62 } 63 64 #pragma omp end declare variant 65 66 /// Lookup a device-side function using a host pointer /p HstPtr using the table 67 /// provided by the device plugin. The table is an ordered pair of host and 68 /// device pointers sorted on the value of the host pointer. 69 void *indirectCallLookup(void *HstPtr) { 70 if (!HstPtr) 71 return nullptr; 72 73 struct IndirectCallTable { 74 void *HstPtr; 75 void *DevPtr; 76 }; 77 IndirectCallTable *Table = 78 reinterpret_cast<IndirectCallTable *>(config::getIndirectCallTablePtr()); 79 uint64_t TableSize = config::getIndirectCallTableSize(); 80 81 // If the table is empty we assume this is device pointer. 82 if (!Table || !TableSize) 83 return HstPtr; 84 85 uint32_t Left = 0; 86 uint32_t Right = TableSize; 87 88 // If the pointer is definitely not contained in the table we exit early. 89 if (HstPtr < Table[Left].HstPtr || HstPtr > Table[Right - 1].HstPtr) 90 return HstPtr; 91 92 while (Left != Right) { 93 uint32_t Current = Left + (Right - Left) / 2; 94 if (Table[Current].HstPtr == HstPtr) 95 return Table[Current].DevPtr; 96 97 if (HstPtr < Table[Current].HstPtr) 98 Right = Current; 99 else 100 Left = Current; 101 } 102 103 // If we searched the whole table and found nothing this is a device pointer. 104 return HstPtr; 105 } 106 107 /// The openmp client instance used to communicate with the server. 108 [[gnu::visibility("protected"), 109 gnu::weak]] rpc::Client Client asm("__llvm_rpc_client"); 110 111 } // namespace impl 112 } // namespace ompx 113 114 /// Interfaces 115 /// 116 ///{ 117 118 extern "C" { 119 int32_t __kmpc_cancellationpoint(IdentTy *, int32_t, int32_t) { return 0; } 120 121 int32_t __kmpc_cancel(IdentTy *, int32_t, int32_t) { return 0; } 122 123 double omp_get_wtick(void) { return ompx::impl::getWTick(); } 124 125 double omp_get_wtime(void) { return ompx::impl::getWTime(); } 126 127 void *__llvm_omp_indirect_call_lookup(void *HstPtr) { 128 return ompx::impl::indirectCallLookup(HstPtr); 129 } 130 131 void *omp_alloc(size_t size, omp_allocator_handle_t allocator) { 132 switch (allocator) { 133 case omp_default_mem_alloc: 134 case omp_large_cap_mem_alloc: 135 case omp_const_mem_alloc: 136 case omp_high_bw_mem_alloc: 137 case omp_low_lat_mem_alloc: 138 return malloc(size); 139 default: 140 return nullptr; 141 } 142 } 143 144 void omp_free(void *ptr, omp_allocator_handle_t allocator) { 145 switch (allocator) { 146 case omp_default_mem_alloc: 147 case omp_large_cap_mem_alloc: 148 case omp_const_mem_alloc: 149 case omp_high_bw_mem_alloc: 150 case omp_low_lat_mem_alloc: 151 free(ptr); 152 case omp_null_allocator: 153 default: 154 return; 155 } 156 } 157 158 unsigned long long __llvm_omp_host_call(void *fn, void *data, size_t size) { 159 rpc::Client::Port Port = ompx::impl::Client.open<OFFLOAD_HOST_CALL>(); 160 Port.send_n(data, size); 161 Port.send([=](rpc::Buffer *buffer, uint32_t) { 162 buffer->data[0] = reinterpret_cast<uintptr_t>(fn); 163 }); 164 unsigned long long Ret; 165 Port.recv([&](rpc::Buffer *Buffer, uint32_t) { 166 Ret = static_cast<unsigned long long>(Buffer->data[0]); 167 }); 168 Port.close(); 169 return Ret; 170 } 171 } 172 173 ///} 174 #pragma omp end declare target 175