Skip to content

Commit

Permalink
[libc] Pull last dependencies into rpc_util.h (llvm#116693)
Browse files Browse the repository at this point in the history
Summary:
Last bit in-place to remove the dependencies on LLVM libc headers. This
just pulls the `sleep_briefly`, `std::optinal` and `type_traits`
definitions into the
`rpc_util.h` header. This duplicates some code for now but will soon be
moved into the `include/rpc` directory. At that point I will remove all
the `LIBC_INLINE` and just make it `RPC_INLINE`. Internal use will then
have a wrapper to make it all LIBC namespaced, implementations will then
implement their own handling.
  • Loading branch information
jhuber6 authored Nov 19, 2024
1 parent c84a99d commit def22f4
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 13 deletions.
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

0 comments on commit def22f4

Please sign in to comment.