xref: /llvm-project/offload/DeviceRTL/src/Misc.cpp (revision 760a786d1580d2d933664a95cf1b5273d30ccdf6)
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