Skip to content

[libc] Pull last dependencies into rpc_util.h #116693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions libc/src/__support/RPC/rpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stdint.h>
Expand Down Expand Up @@ -306,7 +306,7 @@ template <bool T> struct Port {

friend struct Client;
friend struct Server;
friend class cpp::optional<Port<T>>;
friend class rpc::optional<Port<T>>;

public:
template <typename U> LIBC_INLINE void recv(U use);
Expand Down Expand Up @@ -362,9 +362,6 @@ struct Client {
private:
Process<false> process;
};
static_assert(cpp::is_trivially_copyable<Client>::value &&
sizeof(Process<true>) == sizeof(Process<false>),
"The client is not trivially copyable from the server");

/// The RPC server used to respond to the client.
struct Server {
Expand All @@ -377,7 +374,7 @@ struct Server {
: process(port_count, buffer) {}

using Port = rpc::Port<true>;
LIBC_INLINE cpp::optional<Port> try_open(uint32_t lane_size,
LIBC_INLINE rpc::optional<Port> try_open(uint32_t lane_size,
uint32_t start = 0);
LIBC_INLINE Port open(uint32_t lane_size);

Expand Down Expand Up @@ -556,7 +553,7 @@ template <uint16_t opcode>

/// 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<typename Server::Port>
[[clang::convergent]] LIBC_INLINE rpc::optional<typename Server::Port>
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) {
Expand All @@ -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<Server::Port> p = try_open(lane_size))
return cpp::move(p.value());
if (rpc::optional<Server::Port> p = try_open(lane_size))
return rpc::move(p.value());
sleep_briefly();
}
}
Expand Down
5 changes: 5 additions & 0 deletions libc/src/__support/RPC/rpc_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Client>::value &&
sizeof(Process<true>) == sizeof(Process<false>),
"The client is not trivially copyable from the server");

/// The libc client instance used to communicate with the server.
extern Client client;

Expand Down
148 changes: 145 additions & 3 deletions libc/src/__support/RPC/rpc_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stddef.h>
#include <stdint.h>

#if defined(__NVPTX__) || defined(__AMDGPU__)
#include <gpuintrin.h>
Expand All @@ -21,6 +23,146 @@
namespace LIBC_NAMESPACE_DECL {
namespace rpc {

template <typename T> struct type_identity {
using type = T;
};

template <class T, T v> struct type_constant {
static inline constexpr T value = v;
};

template <class T> struct remove_reference : type_identity<T> {};
template <class T> struct remove_reference<T &> : type_identity<T> {};
template <class T> struct remove_reference<T &&> : type_identity<T> {};

template <class T> struct is_const : type_constant<bool, false> {};
template <class T> struct is_const<const T> : type_constant<bool, true> {};

/// Freestanding implementation of std::move.
template <class T>
LIBC_INLINE constexpr typename remove_reference<T>::type &&move(T &&t) {
return static_cast<typename remove_reference<T>::type &&>(t);
}

/// Freestanding implementation of std::forward.
template <typename T>
LIBC_INLINE constexpr T &&forward(typename remove_reference<T>::type &value) {
return static_cast<T &&>(value);
}
template <typename T>
LIBC_INLINE constexpr T &&forward(typename remove_reference<T>::type &&value) {
return static_cast<T &&>(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 <typename T> class optional {
template <typename U> struct OptionalStorage {
union {
char empty;
U stored_value;
};

bool in_use = false;

LIBC_INLINE ~OptionalStorage() { reset(); }

LIBC_INLINE constexpr OptionalStorage() : empty() {}

template <typename... Args>
LIBC_INLINE constexpr explicit OptionalStorage(in_place_t, Args &&...args)
: stored_value(forward<Args>(args)...) {}

LIBC_INLINE constexpr void reset() {
if (in_use)
stored_value.~U();
in_use = false;
}
};

OptionalStorage<T> 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
Expand Down Expand Up @@ -109,7 +251,7 @@ template <typename V> LIBC_INLINE V &lane_value(V *val, uint32_t id) {

/// Advance the \p p by \p bytes.
template <typename T, typename U> LIBC_INLINE T *advance(T *ptr, U bytes) {
if constexpr (cpp::is_const_v<T>)
if constexpr (is_const<T>::value)
return reinterpret_cast<T *>(reinterpret_cast<const uint8_t *>(ptr) +
bytes);
else
Expand Down
Loading