Skip to content

Commit 52fa718

Browse files
committed
[libc] Export the RPC interface from libc
Summary: This patch adds new extensions that allow us to export the RPC interface from `libc` to other programs. This should allow external users of the GPU `libc` to interface with the RPC client (which more or less behaves like syscalls in this context). This is done by wrapping the interface into a C-style function call. Obviously, this approach is far less safe than the carefully crafted C++ interface. For example, we now expose the internal packet buffer, and it is theoretically possible to open a single port with conflicting opcodes and break the whole interface. So, extra care will be needed when interacting with this. However, the usage is very similar as shown by the new test. This somewhat stretches the concept of `libc` just doing `libc` things, but I think this is important enough to justify it. It is difficult to split this out, as we do not want to have the situation where we have multiple RPC clients running at one time, so for now it makes sense to just leave it in `libc`.
1 parent cf835b9 commit 52fa718

File tree

22 files changed

+390
-21
lines changed

22 files changed

+390
-21
lines changed

libc/config/gpu/entrypoints.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ set(TARGET_LIBC_ENTRYPOINTS
211211

212212
# gpu/rpc.h entrypoints
213213
libc.src.gpu.rpc_host_call
214+
libc.src.gpu.rpc_open_port
215+
libc.src.gpu.rpc_send_n
216+
libc.src.gpu.rpc_recv_n
217+
libc.src.gpu.rpc_close_port
214218
)
215219

216220
set(TARGET_LIBM_ENTRYPOINTS

libc/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ if(LIBC_TARGET_OS_IS_GPU)
614614
DEPENDS
615615
.llvm_libc_common_h
616616
.llvm-libc-types.rpc_opcodes_t
617+
.llvm-libc-types.rpc_port_t
617618
)
618619
endif()
619620

libc/include/gpu/rpc.h.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111

1212
#include <__llvm-libc-common.h>
1313

14+
#include <llvm-libc-types/size_t.h>
15+
1416
#include <llvm-libc-types/rpc_opcodes_t.h>
17+
#include <llvm-libc-types/rpc_port_t.h>
1518

1619
%%public_api()
1720

libc/include/llvm-libc-types/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ add_header(socklen_t HDR socklen_t.h)
9494
add_header(struct_sockaddr_un HDR struct_sockaddr_un.h)
9595
add_header(struct_sockaddr HDR struct_sockaddr.h)
9696
add_header(rpc_opcodes_t HDR rpc_opcodes_t.h)
97+
add_header(rpc_port_t HDR rpc_port_t.h)
9798
add_header(ACTION HDR ACTION.h)
9899
add_header(ENTRY HDR ENTRY.h)
99100
add_header(struct_hsearch_data HDR struct_hsearch_data.h)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//===-- Definition of type rpc_port_t -------------------------------------===//
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+
#ifndef __LLVM_LIBC_TYPES_RPC_PORT_T_H__
10+
#define __LLVM_LIBC_TYPES_RPC_PORT_T_H__
11+
12+
typedef struct {
13+
__UINT8_TYPE__ reserved[32];
14+
} rpc_port_t;
15+
16+
#endif // __LLVM_LIBC_TYPES_RPC_PORT_T_H__

libc/include/llvm-libc-types/test_rpc_opcodes_t.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ typedef enum : unsigned short {
1515
RPC_TEST_NOOP = 1 << 15,
1616
RPC_TEST_INCREMENT,
1717
RPC_TEST_INTERFACE,
18+
RPC_TEST_EXTERNAL,
1819
RPC_TEST_STREAM,
1920
} rpc_test_opcode_t;
2021

libc/spec/gpu_ext.td

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,38 @@
1+
def RPCPortT : NamedType<"rpc_port_t">;
2+
def RPCPortPtrT : PtrType<RPCPortT>;
3+
14
def GPUExtensions : StandardSpec<"GPUExtensions"> {
25
HeaderSpec RPC = HeaderSpec<
36
"gpu/rpc.h",
47
[], // Macros
5-
[], // Types
8+
[RPCPortT, SizeTType], // Types
69
[], // Enumerations
710
[
811
FunctionSpec<
912
"rpc_host_call",
1013
RetValSpec<VoidType>,
1114
[ArgSpec<VoidPtr>, ArgSpec<VoidPtr>, ArgSpec<SizeTType>]
1215
>,
16+
FunctionSpec<
17+
"rpc_open_port",
18+
RetValSpec<RPCPortT>,
19+
[ArgSpec<UnsignedIntType>]
20+
>,
21+
FunctionSpec<
22+
"rpc_send_n",
23+
RetValSpec<VoidType>,
24+
[ArgSpec<RPCPortPtrT>, ArgSpec<VoidPtr>, ArgSpec<SizeTType>]
25+
>,
26+
FunctionSpec<
27+
"rpc_recv_n",
28+
RetValSpec<VoidType>,
29+
[ArgSpec<RPCPortPtrT>, ArgSpec<VoidPtr>, ArgSpec<SizeTPtr>]
30+
>,
31+
FunctionSpec<
32+
"rpc_close_port",
33+
RetValSpec<VoidType>,
34+
[ArgSpec<RPCPortPtrT>]
35+
>,
1336
]
1437
>;
1538
let Headers = [

libc/src/__support/CPP/bit.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ namespace LIBC_NAMESPACE::cpp {
2525
#define LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE
2626
#endif
2727

28-
// This implementation of bit_cast requires trivially-constructible To, to avoid
29-
// UB in the implementation.
3028
template <typename To, typename From>
3129
LIBC_INLINE constexpr cpp::enable_if_t<
3230
(sizeof(To) == sizeof(From)) &&
31+
// Implementation of bit_cast that cannot use the compiler builtin must be
32+
// trivially-constructible To, to avoid UB in the implementation.
33+
#if !LIBC_HAS_BUILTIN(__builtin_bit_cast)
3334
cpp::is_trivially_constructible<To>::value &&
35+
#endif
3436
cpp::is_trivially_copyable<To>::value &&
3537
cpp::is_trivially_copyable<From>::value,
3638
To>

libc/src/__support/RPC/rpc.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ template <bool T> struct Port {
323323
LIBC_INLINE void send_n(const void *src, uint64_t size);
324324
template <typename A>
325325
LIBC_INLINE void recv_n(void **dst, uint64_t *size, A &&alloc);
326+
LIBC_INLINE void recv_n(void *dst, uint64_t *size);
326327

327328
LIBC_INLINE uint16_t get_opcode() const {
328329
return process.header[index].opcode;
@@ -359,7 +360,9 @@ struct Client {
359360
: process(port_count, buffer) {}
360361

361362
using Port = rpc::Port<false>;
362-
template <uint16_t opcode> LIBC_INLINE Port open();
363+
364+
template <uint16_t opcode> LIBC_INLINE Port open() { return open(opcode); }
365+
LIBC_INLINE Port open(uint16_t opcode);
363366

364367
private:
365368
Process<false> process;
@@ -484,6 +487,14 @@ LIBC_INLINE void Port<T>::send_n(const void *const *src, uint64_t *size) {
484487
}
485488
}
486489

490+
/// Helper routine to simplify the interface when recieving from the GPU using
491+
/// thread private pointers to the underly value and the destination pointer
492+
/// contains enough data to recieve the values.
493+
template <bool T> LIBC_INLINE void Port<T>::recv_n(void *dst, uint64_t *size) {
494+
void **dst_ptr = &dst;
495+
recv_n(dst_ptr, size, [=](uint64_t) { return dst; });
496+
}
497+
487498
/// Receives an arbitrarily sized data buffer across the shared channel in
488499
/// multiples of the packet length. The \p alloc function is called with the
489500
/// size of the data so that we can initialize the size of the \p dst buffer.
@@ -522,9 +533,9 @@ LIBC_INLINE void Port<T>::recv_n(void **dst, uint64_t *size, A &&alloc) {
522533
/// is, there are send operations pending that haven't been serviced on this
523534
/// port. Each port instance uses an associated \p opcode to tell the server
524535
/// what to do. The Client interface provides the appropriate lane size to the
525-
/// port using the platform's returned value.
526-
template <uint16_t opcode>
527-
[[clang::convergent]] LIBC_INLINE Client::Port Client::open() {
536+
/// port using the platform's returned value. It is required that \p opcode is
537+
/// uniform between all the lanes for this to work.
538+
[[clang::convergent]] LIBC_INLINE Client::Port Client::open(uint16_t opcode) {
528539
// Repeatedly perform a naive linear scan for a port that can be opened to
529540
// send data.
530541
for (uint32_t index = gpu::get_cluster_id();; ++index) {

libc/src/__support/UInt.h

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,24 +1057,13 @@ using make_integral_or_big_int_signed_t =
10571057

10581058
namespace cpp {
10591059

1060-
// Specialization of cpp::bit_cast ('bit.h') from T to BigInt.
1061-
template <typename To, typename From>
1062-
LIBC_INLINE constexpr cpp::enable_if_t<
1063-
(sizeof(To) == sizeof(From)) && cpp::is_trivially_copyable<To>::value &&
1064-
cpp::is_trivially_copyable<From>::value && is_big_int<To>::value,
1065-
To>
1066-
bit_cast(const From &from) {
1067-
To out;
1068-
using Storage = decltype(out.val);
1069-
out.val = cpp::bit_cast<Storage>(from);
1070-
return out;
1071-
}
1072-
10731060
// Specialization of cpp::bit_cast ('bit.h') from BigInt to T.
10741061
template <typename To, size_t Bits>
10751062
LIBC_INLINE constexpr cpp::enable_if_t<
10761063
sizeof(To) == sizeof(UInt<Bits>) &&
1064+
#if !LIBC_HAS_BUILTIN(__builtin_bit_cast)
10771065
cpp::is_trivially_constructible<To>::value &&
1066+
#endif
10781067
cpp::is_trivially_copyable<To>::value &&
10791068
cpp::is_trivially_copyable<UInt<Bits>>::value,
10801069
To>

0 commit comments

Comments
 (0)