[libc-commits] [libc] [libc] Export the RPC interface from `libc` (PR #71432)

Joseph Huber via libc-commits libc-commits at lists.llvm.org
Mon Nov 6 11:43:22 PST 2023


https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/71432

>From bf64ebcc65a1c20830fe029aa7f9a5f8f14939c8 Mon Sep 17 00:00:00 2001
From: Joseph Huber <jhuber6 at vols.utk.edu>
Date: Mon, 6 Nov 2023 13:08:42 -0600
Subject: [PATCH] [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.

I was unsure whether or not to use function pointers to give a `send /
recv` interface that is more true to what the C++ version offers.
However, I elected to simply return the buffer (moving a wait for
ownership inside of this interface) as function pointers on the GPU can
be somewhat difficult to work with. The downside is now that the
interface can hang if it is not ordered correctly, i.e., calling
`rpc_get_buffer` before a `send` call and after a `recv` call. I am open
to changing this if others have opinions.

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`.
---
 libc/config/gpu/entrypoints.txt               |  5 +
 libc/include/CMakeLists.txt                   |  1 +
 libc/include/gpu/rpc.h.def                    |  3 +
 libc/include/llvm-libc-types/CMakeLists.txt   |  1 +
 libc/include/llvm-libc-types/rpc_port_t.h     | 20 ++++
 libc/spec/gpu_ext.td                          | 32 +++++-
 libc/src/__support/RPC/rpc.h                  | 18 +++-
 libc/src/gpu/CMakeLists.txt                   | 55 +++++++++++
 libc/src/gpu/rpc_close_port.cpp               | 25 +++++
 libc/src/gpu/rpc_close_port.h                 | 20 ++++
 libc/src/gpu/rpc_get_buffer.cpp               | 25 +++++
 libc/src/gpu/rpc_get_buffer.h                 | 20 ++++
 libc/src/gpu/rpc_open_port.cpp                | 26 +++++
 libc/src/gpu/rpc_open_port.h                  | 20 ++++
 libc/src/gpu/rpc_recv.cpp                     | 25 +++++
 libc/src/gpu/rpc_recv.h                       | 20 ++++
 libc/src/gpu/rpc_send.cpp                     | 25 +++++
 libc/src/gpu/rpc_send.h                       | 20 ++++
 .../integration/startup/gpu/CMakeLists.txt    | 13 +++
 .../gpu/rpc_external_interface_test.cpp       | 99 +++++++++++++++++++
 20 files changed, 469 insertions(+), 4 deletions(-)
 create mode 100644 libc/include/llvm-libc-types/rpc_port_t.h
 create mode 100644 libc/src/gpu/rpc_close_port.cpp
 create mode 100644 libc/src/gpu/rpc_close_port.h
 create mode 100644 libc/src/gpu/rpc_get_buffer.cpp
 create mode 100644 libc/src/gpu/rpc_get_buffer.h
 create mode 100644 libc/src/gpu/rpc_open_port.cpp
 create mode 100644 libc/src/gpu/rpc_open_port.h
 create mode 100644 libc/src/gpu/rpc_recv.cpp
 create mode 100644 libc/src/gpu/rpc_recv.h
 create mode 100644 libc/src/gpu/rpc_send.cpp
 create mode 100644 libc/src/gpu/rpc_send.h
 create mode 100644 libc/test/integration/startup/gpu/rpc_external_interface_test.cpp

diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index 5dda6aa71d36f81..47bcb36db49d55b 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -139,6 +139,11 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # gpu/rpc.h entrypoints
     libc.src.gpu.rpc_host_call
+    libc.src.gpu.rpc_open_port
+    libc.src.gpu.rpc_get_buffer
+    libc.src.gpu.rpc_send
+    libc.src.gpu.rpc_recv
+    libc.src.gpu.rpc_close_port
 )
 
 set(TARGET_LIBM_ENTRYPOINTS
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 9d170603ffa45cd..0f044ff84f28983 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -515,6 +515,7 @@ if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
     DEPENDS
       .llvm_libc_common_h
       .llvm-libc-types.rpc_opcodes_t
+      .llvm-libc-types.rpc_port_t
   )
 endif()
 
diff --git a/libc/include/gpu/rpc.h.def b/libc/include/gpu/rpc.h.def
index 0438cd65e7be22e..f178007ee7083f5 100644
--- a/libc/include/gpu/rpc.h.def
+++ b/libc/include/gpu/rpc.h.def
@@ -11,7 +11,10 @@
 
 #include <__llvm-libc-common.h>
 
+#include <llvm-libc-types/size_t.h>
+
 #include <llvm-libc-types/rpc_opcodes_t.h>
+#include <llvm-libc-types/rpc_port_t.h>
 
 %%public_api()
 
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 3c0cc7bbc71dacb..8e0bf6a50c22bdd 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -91,3 +91,4 @@ add_header(wint_t HDR wint_t.h)
 add_header(sa_family_t HDR sa_family_t.h)
 add_header(struct_sockaddr HDR struct_sockaddr.h)
 add_header(rpc_opcodes_t HDR rpc_opcodes_t.h)
+add_header(rpc_port_t HDR rpc_port_t.h)
diff --git a/libc/include/llvm-libc-types/rpc_port_t.h b/libc/include/llvm-libc-types/rpc_port_t.h
new file mode 100644
index 000000000000000..bebb1bf32b09007
--- /dev/null
+++ b/libc/include/llvm-libc-types/rpc_port_t.h
@@ -0,0 +1,20 @@
+//===-- Definition of type rpc_port_t -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_TYPES_RPC_PORT_T_H__
+#define __LLVM_LIBC_TYPES_RPC_PORT_T_H__
+
+typedef struct {
+  __UINT8_TYPE__ reserved[32];
+} rpc_port_t;
+
+typedef struct {
+  __UINT64_TYPE__ data[8];
+} rpc_buffer_t;
+
+#endif // __LLVM_LIBC_TYPES_RPC_PORT_T_H__
diff --git a/libc/spec/gpu_ext.td b/libc/spec/gpu_ext.td
index dce81ff7786203b..56094d4d50d3d16 100644
--- a/libc/spec/gpu_ext.td
+++ b/libc/spec/gpu_ext.td
@@ -1,8 +1,13 @@
+def RPCPortT : NamedType<"rpc_port_t">;
+def RPCPortPtrT : PtrType<RPCPortT>;
+def RPCBufferT : NamedType<"rpc_buffer_t">;
+def RPCBufferPtrT : PtrType<RPCBufferT>;
+
 def GPUExtensions : StandardSpec<"GPUExtensions"> {
   HeaderSpec RPC = HeaderSpec<
     "gpu/rpc.h",
     [], // Macros
-    [], // Types
+    [RPCPortT, RPCBufferT, SizeTType], // Types
     [], // Enumerations
     [
         FunctionSpec<
@@ -10,6 +15,31 @@ def GPUExtensions : StandardSpec<"GPUExtensions"> {
             RetValSpec<VoidType>,
             [ArgSpec<VoidPtr>, ArgSpec<VoidPtr>, ArgSpec<SizeTType>]
         >,
+        FunctionSpec<
+            "rpc_open_port",
+            RetValSpec<RPCPortT>,
+            [ArgSpec<UnsignedIntType>]
+        >,
+        FunctionSpec<
+            "rpc_get_buffer",
+            RetValSpec<RPCBufferPtrT>,
+            [ArgSpec<RPCPortPtrT>]
+        >,
+        FunctionSpec<
+            "rpc_send",
+            RetValSpec<VoidType>,
+            [ArgSpec<RPCPortPtrT>]
+        >,
+        FunctionSpec<
+            "rpc_recv",
+            RetValSpec<VoidType>,
+            [ArgSpec<RPCPortPtrT>]
+        >,
+        FunctionSpec<
+            "rpc_close_port",
+            RetValSpec<VoidType>,
+            [ArgSpec<RPCPortPtrT>]
+        >,
     ]
   >;
   let Headers = [
diff --git a/libc/src/__support/RPC/rpc.h b/libc/src/__support/RPC/rpc.h
index 08c1dfd10d6d7f3..66c9fb297767780 100644
--- a/libc/src/__support/RPC/rpc.h
+++ b/libc/src/__support/RPC/rpc.h
@@ -319,6 +319,14 @@ template <bool T, typename S> struct Port {
 
   LIBC_INLINE uint16_t get_index() const { return index; }
 
+  /// Waits until this port owns the buffer and returns a pointer to it.
+  LIBC_INLINE Buffer *get_buffer() const {
+    uint32_t in = owns_buffer ? out ^ T : process.load_inbox(lane_mask, index);
+    process.wait_for_ownership(lane_mask, index, out, in);
+
+    return &process.packet[index].payload.slot[gpu::get_lane_id()];
+  }
+
   LIBC_INLINE void close() {
     // The server is passive, if it own the buffer when it closes we need to
     // give ownership back to the client.
@@ -347,7 +355,9 @@ struct Client {
       : process(port_count, buffer) {}
 
   using Port = rpc::Port<false, Packet<gpu::LANE_SIZE>>;
-  template <uint16_t opcode> LIBC_INLINE Port open();
+
+  template <uint16_t opcode> LIBC_INLINE Port open() { return open(opcode); }
+  LIBC_INLINE Port open(const uint16_t opcode);
 
 private:
   Process<false, Packet<gpu::LANE_SIZE>> process;
@@ -510,8 +520,10 @@ LIBC_INLINE void Port<T, S>::recv_n(void **dst, uint64_t *size, A &&alloc) {
 /// only open a port if we find an index that is in a valid sending state. That
 /// is, there are send operations pending that haven't been serviced on this
 /// port. Each port instance uses an associated \p opcode to tell the server
-/// what to do.
-template <uint16_t opcode> LIBC_INLINE Client::Port Client::open() {
+/// what to do. It is very important that the \p opcode value is identical
+/// across each lane. Use the template version of this unless absolutely
+/// necessary.
+LIBC_INLINE Client::Port Client::open(const uint16_t opcode) {
   // Repeatedly perform a naive linear scan for a port that can be opened to
   // send data.
   for (uint32_t index = 0;; ++index) {
diff --git a/libc/src/gpu/CMakeLists.txt b/libc/src/gpu/CMakeLists.txt
index e20228516b5112d..822b7ba2f89fe62 100644
--- a/libc/src/gpu/CMakeLists.txt
+++ b/libc/src/gpu/CMakeLists.txt
@@ -8,3 +8,58 @@ add_entrypoint_object(
     libc.src.__support.RPC.rpc_client
     libc.src.__support.GPU.utils
 )
+
+add_entrypoint_object(
+  rpc_open_port
+  SRCS
+    rpc_open_port.cpp
+  HDRS
+    rpc_open_port.h
+  DEPENDS
+    libc.src.__support.RPC.rpc_client
+    libc.src.__support.GPU.utils
+)
+
+add_entrypoint_object(
+  rpc_close_port
+  SRCS
+    rpc_close_port.cpp
+  HDRS
+    rpc_close_port.h
+  DEPENDS
+    libc.src.__support.RPC.rpc_client
+    libc.src.__support.GPU.utils
+)
+
+add_entrypoint_object(
+  rpc_get_buffer
+  SRCS
+    rpc_get_buffer.cpp
+  HDRS
+    rpc_get_buffer.h
+  DEPENDS
+    libc.src.__support.RPC.rpc_client
+    libc.src.__support.GPU.utils
+)
+
+add_entrypoint_object(
+  rpc_send
+  SRCS
+    rpc_send.cpp
+  HDRS
+    rpc_send.h
+  DEPENDS
+    libc.src.__support.RPC.rpc_client
+    libc.src.__support.GPU.utils
+)
+
+add_entrypoint_object(
+  rpc_recv
+  SRCS
+    rpc_recv.cpp
+  HDRS
+    rpc_recv.h
+  DEPENDS
+    libc.src.__support.RPC.rpc_client
+    libc.src.__support.GPU.utils
+)
diff --git a/libc/src/gpu/rpc_close_port.cpp b/libc/src/gpu/rpc_close_port.cpp
new file mode 100644
index 000000000000000..f4d9f4b194a4b7b
--- /dev/null
+++ b/libc/src/gpu/rpc_close_port.cpp
@@ -0,0 +1,25 @@
+//===---------- GPU implementation of the external RPC port interface -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/gpu/rpc_close_port.h"
+
+#include "src/__support/GPU/utils.h"
+#include "src/__support/RPC/rpc_client.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+static_assert(sizeof(rpc_port_t) == sizeof(rpc::Client::Port), "ABI mismatch");
+static_assert(sizeof(rpc_buffer_t) == sizeof(rpc::Buffer), "ABI mismatch");
+
+LLVM_LIBC_FUNCTION(void, rpc_close_port, (rpc_port_t * handle)) {
+  rpc::Client::Port *port = reinterpret_cast<rpc::Client::Port *>(handle);
+  port->close();
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/gpu/rpc_close_port.h b/libc/src/gpu/rpc_close_port.h
new file mode 100644
index 000000000000000..2be39ce3698a384
--- /dev/null
+++ b/libc/src/gpu/rpc_close_port.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for RPC functions -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GPU_RPC_CLOSE_PORT_H
+#define LLVM_LIBC_SRC_GPU_RPC_CLOSE_PORT_H
+
+#include <gpu/rpc.h>
+
+namespace LIBC_NAMESPACE {
+
+void rpc_close_port(rpc_port_t *handle);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_GPU_RPC_CLOSE_PORT_H
diff --git a/libc/src/gpu/rpc_get_buffer.cpp b/libc/src/gpu/rpc_get_buffer.cpp
new file mode 100644
index 000000000000000..10097b404c81c46
--- /dev/null
+++ b/libc/src/gpu/rpc_get_buffer.cpp
@@ -0,0 +1,25 @@
+//===---------- GPU implementation of the external RPC port interface -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/gpu/rpc_get_buffer.h"
+
+#include "src/__support/GPU/utils.h"
+#include "src/__support/RPC/rpc_client.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+static_assert(sizeof(rpc_port_t) == sizeof(rpc::Client::Port), "ABI mismatch");
+static_assert(sizeof(rpc_buffer_t) == sizeof(rpc::Buffer), "ABI mismatch");
+
+LLVM_LIBC_FUNCTION(rpc_buffer_t *, rpc_get_buffer, (rpc_port_t * handle)) {
+  rpc::Client::Port *port = reinterpret_cast<rpc::Client::Port *>(handle);
+  return reinterpret_cast<rpc_buffer_t *>(port->get_buffer());
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/gpu/rpc_get_buffer.h b/libc/src/gpu/rpc_get_buffer.h
new file mode 100644
index 000000000000000..5a07db1a5470085
--- /dev/null
+++ b/libc/src/gpu/rpc_get_buffer.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for RPC functions -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GPU_RPC_GET_BUFFER_H
+#define LLVM_LIBC_SRC_GPU_RPC_GET_BUFFER_H
+
+#include <gpu/rpc.h>
+
+namespace LIBC_NAMESPACE {
+
+rpc_buffer_t *rpc_get_buffer(rpc_port_t *handle);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_GPU_RPC_GET_BUFFER_H
diff --git a/libc/src/gpu/rpc_open_port.cpp b/libc/src/gpu/rpc_open_port.cpp
new file mode 100644
index 000000000000000..73dab5066fa1bfb
--- /dev/null
+++ b/libc/src/gpu/rpc_open_port.cpp
@@ -0,0 +1,26 @@
+//===---------- GPU implementation of the external RPC port interface -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/gpu/rpc_open_port.h"
+
+#include "src/__support/CPP/bit.h"
+#include "src/__support/GPU/utils.h"
+#include "src/__support/RPC/rpc_client.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+static_assert(sizeof(rpc_port_t) == sizeof(rpc::Client::Port), "ABI mismatch");
+static_assert(sizeof(rpc_buffer_t) == sizeof(rpc::Buffer), "ABI mismatch");
+
+LLVM_LIBC_FUNCTION(rpc_port_t, rpc_open_port, (unsigned opcode)) {
+  rpc::Client::Port port = rpc::client.open(static_cast<uint16_t>(opcode));
+  return cpp::bit_cast<rpc_port_t>(port);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/gpu/rpc_open_port.h b/libc/src/gpu/rpc_open_port.h
new file mode 100644
index 000000000000000..553fa59120aaf68
--- /dev/null
+++ b/libc/src/gpu/rpc_open_port.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for RPC functions -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GPU_RPC_OPEN_PORT_H
+#define LLVM_LIBC_SRC_GPU_RPC_OPEN_PORT_H
+
+#include <gpu/rpc.h>
+
+namespace LIBC_NAMESPACE {
+
+rpc_port_t rpc_open_port(unsigned opcode);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_GPU_RPC_OPEN_PORT_H
diff --git a/libc/src/gpu/rpc_recv.cpp b/libc/src/gpu/rpc_recv.cpp
new file mode 100644
index 000000000000000..96086dc2b17a345
--- /dev/null
+++ b/libc/src/gpu/rpc_recv.cpp
@@ -0,0 +1,25 @@
+//===---------- GPU implementation of the external RPC port interface -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/gpu/rpc_recv.h"
+
+#include "src/__support/GPU/utils.h"
+#include "src/__support/RPC/rpc_client.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+static_assert(sizeof(rpc_port_t) == sizeof(rpc::Client::Port), "ABI mismatch");
+static_assert(sizeof(rpc_buffer_t) == sizeof(rpc::Buffer), "ABI mismatch");
+
+LLVM_LIBC_FUNCTION(void, rpc_recv, (rpc_port_t * handle)) {
+  rpc::Client::Port *port = reinterpret_cast<rpc::Client::Port *>(handle);
+  port->recv([](rpc::Buffer *) { /* Filled by rpc_get_buffer() */ });
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/gpu/rpc_recv.h b/libc/src/gpu/rpc_recv.h
new file mode 100644
index 000000000000000..926565c3b0c16a0
--- /dev/null
+++ b/libc/src/gpu/rpc_recv.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for RPC functions -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GPU_RPC_RECV_H
+#define LLVM_LIBC_SRC_GPU_RPC_RECV_H
+
+#include <gpu/rpc.h>
+
+namespace LIBC_NAMESPACE {
+
+void rpc_recv(rpc_port_t *handle);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_GPU_RPC_RECV_H
diff --git a/libc/src/gpu/rpc_send.cpp b/libc/src/gpu/rpc_send.cpp
new file mode 100644
index 000000000000000..4f4f5bcc2a5eb80
--- /dev/null
+++ b/libc/src/gpu/rpc_send.cpp
@@ -0,0 +1,25 @@
+//===---------- GPU implementation of the external RPC port interface -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/gpu/rpc_send.h"
+
+#include "src/__support/GPU/utils.h"
+#include "src/__support/RPC/rpc_client.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+static_assert(sizeof(rpc_port_t) == sizeof(rpc::Client::Port), "ABI mismatch");
+static_assert(sizeof(rpc_buffer_t) == sizeof(rpc::Buffer), "ABI mismatch");
+
+LLVM_LIBC_FUNCTION(void, rpc_send, (rpc_port_t * handle)) {
+  rpc::Client::Port *port = reinterpret_cast<rpc::Client::Port *>(handle);
+  port->send([](rpc::Buffer *) { /* Filled by rpc_get_buffer() */ });
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/gpu/rpc_send.h b/libc/src/gpu/rpc_send.h
new file mode 100644
index 000000000000000..e367514181f2ba8
--- /dev/null
+++ b/libc/src/gpu/rpc_send.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for RPC functions -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GPU_RPC_SEND_H
+#define LLVM_LIBC_SRC_GPU_RPC_SEND_H
+
+#include <gpu/rpc.h>
+
+namespace LIBC_NAMESPACE {
+
+void rpc_send(rpc_port_t *handle);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_GPU_RPC_SEND_H
diff --git a/libc/test/integration/startup/gpu/CMakeLists.txt b/libc/test/integration/startup/gpu/CMakeLists.txt
index 7555986b16df452..18764ad2bcaf0b0 100644
--- a/libc/test/integration/startup/gpu/CMakeLists.txt
+++ b/libc/test/integration/startup/gpu/CMakeLists.txt
@@ -44,6 +44,19 @@ add_integration_test(
    rpc_interface_test.cpp
 )
 
+add_integration_test(
+  startup_rpc_external_interface_test
+  SUITE libc-startup-tests
+  SRCS
+    rpc_external_interface_test.cpp
+  DEPENDS
+    libc.src.gpu.rpc_open_port
+    libc.src.gpu.rpc_get_buffer
+    libc.src.gpu.rpc_send
+    libc.src.gpu.rpc_recv
+    libc.src.gpu.rpc_close_port
+)
+
 add_integration_test(
   startup_rpc_stream_test
   SUITE libc-startup-tests
diff --git a/libc/test/integration/startup/gpu/rpc_external_interface_test.cpp b/libc/test/integration/startup/gpu/rpc_external_interface_test.cpp
new file mode 100644
index 000000000000000..ae5221f58fc901d
--- /dev/null
+++ b/libc/test/integration/startup/gpu/rpc_external_interface_test.cpp
@@ -0,0 +1,99 @@
+//===-- Loader test to check the external RPC interface with the loader ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <gpu/rpc.h>
+
+#include "src/gpu/rpc_close_port.h"
+#include "src/gpu/rpc_get_buffer.h"
+#include "src/gpu/rpc_open_port.h"
+#include "src/gpu/rpc_recv.h"
+#include "src/gpu/rpc_send.h"
+
+#include "include/llvm-libc-types/test_rpc_opcodes_t.h"
+#include "src/__support/GPU/utils.h"
+#include "src/__support/RPC/rpc_client.h"
+#include "test/IntegrationTest/test.h"
+
+using namespace LIBC_NAMESPACE;
+
+// Test to ensure that we can use aribtrary combinations of sends and recieves
+// as long as they are mirrored using the external interface.
+static void test_interface(bool end_with_send) {
+  uint64_t cnt = 0;
+  // rpc::Client::Port port = rpc::client.open<RPC_TEST_INTERFACE>();
+  rpc_port_t port = LIBC_NAMESPACE::rpc_open_port(RPC_TEST_INTERFACE);
+
+  // port.send([&](rpc::Buffer *buffer) { buffer->data[0] = end_with_send; });
+  rpc_buffer_t *buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+  buffer->data[0] = end_with_send;
+  LIBC_NAMESPACE::rpc_send(&port);
+
+  // port.send([&](rpc::Buffer *buffer) { buffer->data[0] = cnt = cnt + 1; });
+  buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+  buffer->data[0] = cnt = cnt + 1;
+  LIBC_NAMESPACE::rpc_send(&port);
+
+  // port.recv([&](rpc::Buffer *buffer) { cnt = buffer->data[0]; });
+  LIBC_NAMESPACE::rpc_recv(&port);
+  buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+  cnt = buffer->data[0];
+
+  // port.send([&](rpc::Buffer *buffer) { buffer->data[0] = cnt = cnt + 1; });
+  buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+  buffer->data[0] = cnt = cnt + 1;
+  LIBC_NAMESPACE::rpc_send(&port);
+
+  // port.recv([&](rpc::Buffer *buffer) { cnt = buffer->data[0]; });
+  LIBC_NAMESPACE::rpc_recv(&port);
+  buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+  cnt = buffer->data[0];
+
+  // port.send([&](rpc::Buffer *buffer) { buffer->data[0] = cnt = cnt + 1; });
+  buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+  buffer->data[0] = cnt = cnt + 1;
+  LIBC_NAMESPACE::rpc_send(&port);
+
+  // port.send([&](rpc::Buffer *buffer) { buffer->data[0] = cnt = cnt + 1; });
+  buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+  buffer->data[0] = cnt = cnt + 1;
+  LIBC_NAMESPACE::rpc_send(&port);
+
+  // port.recv([&](rpc::Buffer *buffer) { cnt = buffer->data[0]; });
+  LIBC_NAMESPACE::rpc_recv(&port);
+  buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+  cnt = buffer->data[0];
+
+  // port.recv([&](rpc::Buffer *buffer) { cnt = buffer->data[0]; });
+  LIBC_NAMESPACE::rpc_recv(&port);
+  buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+  cnt = buffer->data[0];
+
+  if (end_with_send) {
+    // port.send([&](rpc::Buffer *buffer) { buffer->data[0] = cnt = cnt + 1; });
+    buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+    buffer->data[0] = cnt = cnt + 1;
+    LIBC_NAMESPACE::rpc_send(&port);
+  } else {
+    // port.recv([&](rpc::Buffer *buffer) { cnt = buffer->data[0]; });
+    LIBC_NAMESPACE::rpc_recv(&port);
+    buffer = LIBC_NAMESPACE::rpc_get_buffer(&port);
+    cnt = buffer->data[0];
+  }
+
+  // port.close();
+  LIBC_NAMESPACE::rpc_close_port(&port);
+
+  ASSERT_TRUE(cnt == 9 && "Invalid number of increments");
+}
+
+TEST_MAIN(int argc, char **argv, char **envp) {
+  test_interface(true);
+  test_interface(false);
+
+  return 0;
+}



More information about the libc-commits mailing list