1 //===- RPC.h - Interface for remote procedure calls from the GPU ----------===// 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 // This file provides the interface to support remote procedure calls (RPC) from 10 // the GPU. This is required to implement host services like printf or malloc. 11 // The interface to the RPC server is provided by the 'libc' project in LLVM. 12 // For more information visit https://libc.llvm.org/gpu/. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #ifndef OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_RPC_H 17 #define OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_RPC_H 18 19 #include "llvm/ADT/DenseMap.h" 20 #include "llvm/Support/Error.h" 21 22 #include <atomic> 23 #include <condition_variable> 24 #include <cstdint> 25 #include <mutex> 26 #include <thread> 27 28 namespace llvm::omp::target { 29 namespace plugin { 30 struct GenericPluginTy; 31 struct GenericDeviceTy; 32 class GenericGlobalHandlerTy; 33 class DeviceImageTy; 34 } // namespace plugin 35 36 /// A generic class implementing the interface between the RPC server provided 37 /// by the 'libc' project and 'libomptarget'. If the RPC server is not availible 38 /// these routines will perform no action. 39 struct RPCServerTy { 40 public: 41 /// Initializes the handles to the number of devices we may need to service. 42 RPCServerTy(plugin::GenericPluginTy &Plugin); 43 44 /// Deinitialize the associated memory and resources. 45 llvm::Error shutDown(); 46 47 /// Initialize the worker thread. 48 llvm::Error startThread(); 49 50 /// Check if this device image is using an RPC server. This checks for the 51 /// precense of an externally visible symbol in the device image that will 52 /// be present whenever RPC code is called. 53 llvm::Expected<bool> isDeviceUsingRPC(plugin::GenericDeviceTy &Device, 54 plugin::GenericGlobalHandlerTy &Handler, 55 plugin::DeviceImageTy &Image); 56 57 /// Initialize the RPC server for the given device. This will allocate host 58 /// memory for the internal server and copy the data to the client on the 59 /// device. The device must be loaded before this is valid. 60 llvm::Error initDevice(plugin::GenericDeviceTy &Device, 61 plugin::GenericGlobalHandlerTy &Handler, 62 plugin::DeviceImageTy &Image); 63 64 /// Deinitialize the RPC server for the given device. This will free the 65 /// memory associated with the k 66 llvm::Error deinitDevice(plugin::GenericDeviceTy &Device); 67 68 private: 69 /// Array from this device's identifier to its attached devices. 70 std::unique_ptr<void *[]> Buffers; 71 72 /// Array of associated devices. These must be alive as long as the server is. 73 std::unique_ptr<plugin::GenericDeviceTy *[]> Devices; 74 75 /// A helper class for running the user thread that handles the RPC interface. 76 /// Because we only need to check the RPC server while any kernels are 77 /// working, we track submission / completion events to allow the thread to 78 /// sleep when it is not needed. 79 struct ServerThread { 80 std::thread Worker; 81 82 /// A boolean indicating whether or not the worker thread should continue. 83 std::atomic<bool> Running; 84 85 /// The number of currently executing kernels across all devices that need 86 /// the server thread to be running. 87 std::atomic<uint32_t> NumUsers; 88 89 /// The condition variable used to suspend the thread if no work is needed. 90 std::condition_variable CV; 91 std::mutex Mutex; 92 93 /// A reference to all the RPC interfaces that the server is handling. 94 llvm::ArrayRef<void *> Buffers; 95 96 /// A reference to the associated generic device for the buffer. 97 llvm::ArrayRef<plugin::GenericDeviceTy *> Devices; 98 99 /// Initialize the worker thread to run in the background. 100 ServerThread(void *Buffers[], plugin::GenericDeviceTy *Devices[], 101 size_t Length) 102 : Running(false), NumUsers(0), CV(), Mutex(), Buffers(Buffers, Length), 103 Devices(Devices, Length) {} 104 105 ~ServerThread() { assert(!Running && "Thread not shut down explicitly\n"); } 106 107 /// Notify the worker thread that there is a user that needs it. 108 void notify() { 109 std::lock_guard<decltype(Mutex)> Lock(Mutex); 110 NumUsers.fetch_add(1, std::memory_order_relaxed); 111 CV.notify_all(); 112 } 113 114 /// Indicate that one of the dependent users has finished. 115 void finish() { 116 [[maybe_unused]] uint32_t Old = 117 NumUsers.fetch_sub(1, std::memory_order_relaxed); 118 assert(Old > 0 && "Attempt to signal finish with no pending work"); 119 } 120 121 /// Destroy the worker thread and wait. 122 void shutDown(); 123 124 /// Initialize the worker thread. 125 void startThread(); 126 127 /// Run the server thread to continuously check the RPC interface for work 128 /// to be done for the device. 129 void run(); 130 }; 131 132 public: 133 /// Pointer to the server thread instance. 134 std::unique_ptr<ServerThread> Thread; 135 }; 136 137 } // namespace llvm::omp::target 138 139 #endif 140