diff --git a/libc/src/__support/RPC/rpc.h b/libc/src/__support/RPC/rpc.h index 45a7ce3109f57b..19561d05bff9ef 100644 --- a/libc/src/__support/RPC/rpc.h +++ b/libc/src/__support/RPC/rpc.h @@ -19,7 +19,7 @@ #define LLVM_LIBC_SRC___SUPPORT_RPC_RPC_H #include "rpc_util.h" -#include "src/__support/CPP/optional.h" +#include "src/__support/macros/attributes.h" #include "src/__support/macros/config.h" #include @@ -306,7 +306,7 @@ template struct Port { friend struct Client; friend struct Server; - friend class cpp::optional>; + friend class rpc::optional>; public: template LIBC_INLINE void recv(U use); @@ -362,9 +362,6 @@ struct Client { private: Process process; }; -static_assert(cpp::is_trivially_copyable::value && - sizeof(Process) == sizeof(Process), - "The client is not trivially copyable from the server"); /// The RPC server used to respond to the client. struct Server { @@ -377,7 +374,7 @@ struct Server { : process(port_count, buffer) {} using Port = rpc::Port; - LIBC_INLINE cpp::optional try_open(uint32_t lane_size, + LIBC_INLINE rpc::optional try_open(uint32_t lane_size, uint32_t start = 0); LIBC_INLINE Port open(uint32_t lane_size); @@ -556,7 +553,7 @@ template /// Attempts to open a port to use as the server. The server can only open a /// port if it has a pending receive operation -[[clang::convergent]] LIBC_INLINE cpp::optional +[[clang::convergent]] LIBC_INLINE rpc::optional Server::try_open(uint32_t lane_size, uint32_t start) { // Perform a naive linear scan for a port that has a pending request. for (uint32_t index = start; index < process.port_count; ++index) { @@ -583,13 +580,13 @@ Server::try_open(uint32_t lane_size, uint32_t start) { return Port(process, lane_mask, lane_size, index, out); } - return cpp::nullopt; + return rpc::nullopt; } LIBC_INLINE Server::Port Server::open(uint32_t lane_size) { for (;;) { - if (cpp::optional p = try_open(lane_size)) - return cpp::move(p.value()); + if (rpc::optional p = try_open(lane_size)) + return rpc::move(p.value()); sleep_briefly(); } } diff --git a/libc/src/__support/RPC/rpc_client.h b/libc/src/__support/RPC/rpc_client.h index 695b6b7515bf7b..7bd6d0b5e00b47 100644 --- a/libc/src/__support/RPC/rpc_client.h +++ b/libc/src/__support/RPC/rpc_client.h @@ -12,11 +12,16 @@ #include "rpc.h" #include "include/llvm-libc-types/rpc_opcodes_t.h" +#include "src/__support/CPP/type_traits.h" #include "src/__support/macros/config.h" namespace LIBC_NAMESPACE_DECL { namespace rpc { +static_assert(cpp::is_trivially_copyable::value && + sizeof(Process) == sizeof(Process), + "The client is not trivially copyable from the server"); + /// The libc client instance used to communicate with the server. extern Client client; diff --git a/libc/src/__support/RPC/rpc_util.h b/libc/src/__support/RPC/rpc_util.h index 39e5f30b84ac44..7067dfc974eb31 100644 --- a/libc/src/__support/RPC/rpc_util.h +++ b/libc/src/__support/RPC/rpc_util.h @@ -9,9 +9,11 @@ #ifndef LLVM_LIBC_SRC___SUPPORT_RPC_RPC_UTIL_H #define LLVM_LIBC_SRC___SUPPORT_RPC_RPC_UTIL_H -#include "src/__support/CPP/type_traits.h" +#include "src/__support/macros/attributes.h" #include "src/__support/macros/config.h" -#include "src/__support/threads/sleep.h" + +#include +#include #if defined(__NVPTX__) || defined(__AMDGPU__) #include @@ -21,6 +23,146 @@ namespace LIBC_NAMESPACE_DECL { namespace rpc { +template struct type_identity { + using type = T; +}; + +template struct type_constant { + static inline constexpr T value = v; +}; + +template struct remove_reference : type_identity {}; +template struct remove_reference : type_identity {}; +template struct remove_reference : type_identity {}; + +template struct is_const : type_constant {}; +template struct is_const : type_constant {}; + +/// Freestanding implementation of std::move. +template +LIBC_INLINE constexpr typename remove_reference::type &&move(T &&t) { + return static_cast::type &&>(t); +} + +/// Freestanding implementation of std::forward. +template +LIBC_INLINE constexpr T &&forward(typename remove_reference::type &value) { + return static_cast(value); +} +template +LIBC_INLINE constexpr T &&forward(typename remove_reference::type &&value) { + return static_cast(value); +} + +struct in_place_t { + LIBC_INLINE explicit in_place_t() = default; +}; + +struct nullopt_t { + LIBC_INLINE constexpr explicit nullopt_t() = default; +}; + +constexpr inline in_place_t in_place{}; +constexpr inline nullopt_t nullopt{}; + +/// Freestanding and minimal implementation of std::optional. +template class optional { + template struct OptionalStorage { + union { + char empty; + U stored_value; + }; + + bool in_use = false; + + LIBC_INLINE ~OptionalStorage() { reset(); } + + LIBC_INLINE constexpr OptionalStorage() : empty() {} + + template + LIBC_INLINE constexpr explicit OptionalStorage(in_place_t, Args &&...args) + : stored_value(forward(args)...) {} + + LIBC_INLINE constexpr void reset() { + if (in_use) + stored_value.~U(); + in_use = false; + } + }; + + OptionalStorage storage; + +public: + LIBC_INLINE constexpr optional() = default; + LIBC_INLINE constexpr optional(nullopt_t) {} + + LIBC_INLINE constexpr optional(const T &t) : storage(in_place, t) { + storage.in_use = true; + } + LIBC_INLINE constexpr optional(const optional &) = default; + + LIBC_INLINE constexpr optional(T &&t) : storage(in_place, move(t)) { + storage.in_use = true; + } + LIBC_INLINE constexpr optional(optional &&O) = default; + + LIBC_INLINE constexpr optional &operator=(T &&t) { + storage = move(t); + return *this; + } + LIBC_INLINE constexpr optional &operator=(optional &&) = default; + + LIBC_INLINE constexpr optional &operator=(const T &t) { + storage = t; + return *this; + } + LIBC_INLINE constexpr optional &operator=(const optional &) = default; + + LIBC_INLINE constexpr void reset() { storage.reset(); } + + LIBC_INLINE constexpr const T &value() const & { + return storage.stored_value; + } + + LIBC_INLINE constexpr T &value() & { return storage.stored_value; } + + LIBC_INLINE constexpr explicit operator bool() const { + return storage.in_use; + } + LIBC_INLINE constexpr bool has_value() const { return storage.in_use; } + LIBC_INLINE constexpr const T *operator->() const { + return &storage.stored_value; + } + LIBC_INLINE constexpr T *operator->() { return &storage.stored_value; } + LIBC_INLINE constexpr const T &operator*() const & { + return storage.stored_value; + } + LIBC_INLINE constexpr T &operator*() & { return storage.stored_value; } + + LIBC_INLINE constexpr T &&value() && { return move(storage.stored_value); } + LIBC_INLINE constexpr T &&operator*() && { + return move(storage.stored_value); + } +}; + +/// Suspend the thread briefly to assist the thread scheduler during busy loops. +LIBC_INLINE void sleep_briefly() { +#if defined(LIBC_TARGET_ARCH_IS_NVPTX) + if (__nvvm_reflect("__CUDA_ARCH") >= 700) + asm("nanosleep.u32 64;" ::: "memory"); +#elif defined(LIBC_TARGET_ARCH_IS_AMDGPU) + __builtin_amdgcn_s_sleep(2); +#elif defined(LIBC_TARGET_ARCH_IS_X86) + __builtin_ia32_pause(); +#elif defined(LIBC_TARGET_ARCH_IS_AARCH64) && __has_builtin(__builtin_arm_isb) + __builtin_arm_isb(0xf); +#elif defined(LIBC_TARGET_ARCH_IS_AARCH64) + asm volatile("isb\n" ::: "memory"); +#else + // Simply do nothing if sleeping isn't supported on this platform. +#endif +} + /// Conditional to indicate if this process is running on the GPU. LIBC_INLINE constexpr bool is_process_gpu() { #ifdef RPC_TARGET_IS_GPU @@ -109,7 +251,7 @@ template LIBC_INLINE V &lane_value(V *val, uint32_t id) { /// Advance the \p p by \p bytes. template LIBC_INLINE T *advance(T *ptr, U bytes) { - if constexpr (cpp::is_const_v) + if constexpr (is_const::value) return reinterpret_cast(reinterpret_cast(ptr) + bytes); else