[libc-commits] [libc] [libc] Implement more input functions on the GPU (PR #66288)

Joseph Huber via libc-commits libc-commits at lists.llvm.org
Thu Sep 14 09:53:48 PDT 2023


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

>From b85ea4fce0913d468c0609705d21410715e50472 Mon Sep 17 00:00:00 2001
From: Joseph Huber <jhuber6 at vols.utk.edu>
Date: Wed, 13 Sep 2023 15:29:05 -0500
Subject: [PATCH 1/4] [libc] Implement more input functions on the GPU

Summary:
This patch implements the `fgets`, `getc`, `fgetc`, and `getchar`
functions on the GPU. Their implementations are straightforward enough.
One thing worth noting is that the implementation of `fgets` will be
extremely slow due to the high latency to read a single char. A faster
solution would be to make a new RPC call to call `fgets` (due to the
special rule that newline or null breaks the stream). But this is left
out because performance isn't the primary concern here.
---
 libc/config/gpu/entrypoints.txt               |   7 +
 libc/include/llvm-libc-types/rpc_opcodes_t.h  |   3 +
 libc/src/stdio/CMakeLists.txt                 | 173 +-----------------
 libc/src/stdio/generic/CMakeLists.txt         | 163 +++++++++++++++++
 libc/src/stdio/{ => generic}/clearerr.cpp     |   0
 .../stdio/{ => generic}/clearerr_unlocked.cpp |   0
 libc/src/stdio/{ => generic}/feof.cpp         |   0
 .../src/stdio/{ => generic}/feof_unlocked.cpp |   0
 libc/src/stdio/{ => generic}/ferror.cpp       |   0
 .../stdio/{ => generic}/ferror_unlocked.cpp   |   0
 libc/src/stdio/{ => generic}/fgetc.cpp        |   0
 .../stdio/{ => generic}/fgetc_unlocked.cpp    |   0
 libc/src/stdio/{ => generic}/fgets.cpp        |   0
 libc/src/stdio/{ => generic}/getc.cpp         |   0
 .../src/stdio/{ => generic}/getc_unlocked.cpp |   0
 libc/src/stdio/{ => generic}/getchar.cpp      |   0
 .../stdio/{ => generic}/getchar_unlocked.cpp  |   0
 libc/src/stdio/gpu/CMakeLists.txt             | 113 ++++++++++++
 libc/src/stdio/gpu/clearerr.cpp               |  28 +++
 libc/src/stdio/gpu/feof.cpp                   |  28 +++
 libc/src/stdio/gpu/ferror.cpp                 |  28 +++
 libc/src/stdio/gpu/fgetc.cpp                  |  25 +++
 libc/src/stdio/gpu/fgets.cpp                  |  46 +++++
 libc/src/stdio/gpu/getc.cpp                   |  25 +++
 libc/src/stdio/gpu/getchar.cpp                |  25 +++
 libc/test/src/stdio/CMakeLists.txt            |   6 +-
 libc/utils/gpu/server/rpc_server.cpp          |  18 ++
 27 files changed, 522 insertions(+), 166 deletions(-)
 rename libc/src/stdio/{ => generic}/clearerr.cpp (100%)
 rename libc/src/stdio/{ => generic}/clearerr_unlocked.cpp (100%)
 rename libc/src/stdio/{ => generic}/feof.cpp (100%)
 rename libc/src/stdio/{ => generic}/feof_unlocked.cpp (100%)
 rename libc/src/stdio/{ => generic}/ferror.cpp (100%)
 rename libc/src/stdio/{ => generic}/ferror_unlocked.cpp (100%)
 rename libc/src/stdio/{ => generic}/fgetc.cpp (100%)
 rename libc/src/stdio/{ => generic}/fgetc_unlocked.cpp (100%)
 rename libc/src/stdio/{ => generic}/fgets.cpp (100%)
 rename libc/src/stdio/{ => generic}/getc.cpp (100%)
 rename libc/src/stdio/{ => generic}/getc_unlocked.cpp (100%)
 rename libc/src/stdio/{ => generic}/getchar.cpp (100%)
 rename libc/src/stdio/{ => generic}/getchar_unlocked.cpp (100%)
 create mode 100644 libc/src/stdio/gpu/clearerr.cpp
 create mode 100644 libc/src/stdio/gpu/feof.cpp
 create mode 100644 libc/src/stdio/gpu/ferror.cpp
 create mode 100644 libc/src/stdio/gpu/fgetc.cpp
 create mode 100644 libc/src/stdio/gpu/fgets.cpp
 create mode 100644 libc/src/stdio/gpu/getc.cpp
 create mode 100644 libc/src/stdio/gpu/getchar.cpp

diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index b8d09c9bb8a8bde..ba3e41ce3e5a8ca 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -86,6 +86,9 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.errno.errno
 
     # stdio.h entrypoints
+    libc.src.stdio.feof
+    libc.src.stdio.ferror
+    libc.src.stdio.clearerr
     libc.src.stdio.puts
     libc.src.stdio.fopen
     libc.src.stdio.fclose
@@ -95,6 +98,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.fputc
     libc.src.stdio.putc
     libc.src.stdio.putchar
+    libc.src.stdio.fgets
+    libc.src.stdio.fgetc
+    libc.src.stdio.getc
+    libc.src.stdio.getchar
     libc.src.stdio.stdin
     libc.src.stdio.stdout
     libc.src.stdio.stderr
diff --git a/libc/include/llvm-libc-types/rpc_opcodes_t.h b/libc/include/llvm-libc-types/rpc_opcodes_t.h
index 3916c0ad9538922..2c1c89779568a46 100644
--- a/libc/include/llvm-libc-types/rpc_opcodes_t.h
+++ b/libc/include/llvm-libc-types/rpc_opcodes_t.h
@@ -23,6 +23,9 @@ typedef enum : unsigned short {
   RPC_FREE = 10,
   RPC_HOST_CALL = 11,
   RPC_ABORT = 12,
+  RPC_FEOF = 13,
+  RPC_FERROR = 14,
+  RPC_CLEARERR = 15,
 } rpc_opcode_t;
 
 #endif // __LLVM_LIBC_TYPES_RPC_OPCODE_H__
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index de28b5c02071bf7..db1129cfd7e9031 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -29,169 +29,6 @@ endif()
 add_subdirectory(printf_core)
 add_subdirectory(scanf_core)
 
-add_entrypoint_object(
-  clearerr
-  SRCS
-    clearerr.cpp
-  HDRS
-    clearerr.h
-  DEPENDS
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  clearerr_unlocked
-  SRCS
-    clearerr_unlocked.cpp
-  HDRS
-    clearerr_unlocked.h
-  DEPENDS
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  feof
-  SRCS
-    feof.cpp
-  HDRS
-    feof.h
-  DEPENDS
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  feof_unlocked
-  SRCS
-    feof_unlocked.cpp
-  HDRS
-    feof_unlocked.h
-  DEPENDS
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  ferror
-  SRCS
-    ferror.cpp
-  HDRS
-    ferror.h
-  DEPENDS
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  ferror_unlocked
-  SRCS
-    ferror_unlocked.cpp
-  HDRS
-    ferror_unlocked.h
-  DEPENDS
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  fgetc
-  SRCS
-    fgetc.cpp
-  HDRS
-    fgetc.h
-  DEPENDS
-    libc.src.errno.errno
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  fgetc_unlocked
-  SRCS
-    fgetc_unlocked.cpp
-  HDRS
-    fgetc_unlocked.h
-  DEPENDS
-    libc.src.errno.errno
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  getc
-  SRCS
-    getc.cpp
-  HDRS
-    getc.h
-  DEPENDS
-    libc.src.errno.errno
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  getc_unlocked
-  SRCS
-    getc_unlocked.cpp
-  HDRS
-    getc_unlocked.h
-  DEPENDS
-    libc.src.errno.errno
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  getchar
-  SRCS
-    getchar.cpp
-  HDRS
-    getchar.h
-  DEPENDS
-    libc.src.errno.errno
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  getchar_unlocked
-  SRCS
-    getc_unlocked.cpp
-  HDRS
-    getc_unlocked.h
-  DEPENDS
-    libc.src.errno.errno
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
-add_entrypoint_object(
-  fgets
-  SRCS
-    fgets.cpp
-  HDRS
-    fgets.h
-  DEPENDS
-    libc.src.errno.errno
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
 add_entrypoint_object(
   fflush
   SRCS
@@ -470,6 +307,9 @@ add_entrypoint_object(
 )
 
 # These entrypoints have multiple potential implementations.
+add_stdio_entrypoint_object(feof)
+add_stdio_entrypoint_object(ferror)
+add_stdio_entrypoint_object(clearerr)
 add_stdio_entrypoint_object(fopen)
 add_stdio_entrypoint_object(fclose)
 add_stdio_entrypoint_object(fread_unlocked)
@@ -481,6 +321,13 @@ add_stdio_entrypoint_object(fwrite)
 add_stdio_entrypoint_object(fputc)
 add_stdio_entrypoint_object(putc)
 add_stdio_entrypoint_object(putchar)
+add_stdio_entrypoint_object(fgetc)
+add_stdio_entrypoint_object(fgetc_unlocked)
+add_stdio_entrypoint_object(getc)
+add_stdio_entrypoint_object(getc_unlocked)
+add_stdio_entrypoint_object(getchar)
+add_stdio_entrypoint_object(getchar_unlocked)
+add_stdio_entrypoint_object(fgets)
 add_stdio_entrypoint_object(stdin)
 add_stdio_entrypoint_object(stdout)
 add_stdio_entrypoint_object(stderr)
diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt
index e40e2cc9e04d3e3..b7a9b9b5747e2b4 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -1,3 +1,75 @@
+add_entrypoint_object(
+  clearerr
+  SRCS
+    clearerr.cpp
+  HDRS
+    ../clearerr.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  clearerr_unlocked
+  SRCS
+    clearerr_unlocked.cpp
+  HDRS
+    ../clearerr_unlocked.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  feof
+  SRCS
+    feof.cpp
+  HDRS
+    ../feof.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  feof_unlocked
+  SRCS
+    feof_unlocked.cpp
+  HDRS
+    ../feof_unlocked.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  ferror
+  SRCS
+    ferror.cpp
+  HDRS
+    ../ferror.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  ferror_unlocked
+  SRCS
+    ferror_unlocked.cpp
+  HDRS
+    ../ferror_unlocked.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
 add_entrypoint_object(
   fopen
   SRCS
@@ -140,6 +212,97 @@ add_entrypoint_object(
     libc.src.__support.File.platform_file
 )
 
+add_entrypoint_object(
+  fgetc
+  SRCS
+    fgetc.cpp
+  HDRS
+    ../fgetc.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  fgetc_unlocked
+  SRCS
+    fgetc_unlocked.cpp
+  HDRS
+    ../fgetc_unlocked.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  getc
+  SRCS
+    getc.cpp
+  HDRS
+    ../getc.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  getc_unlocked
+  SRCS
+    getc_unlocked.cpp
+  HDRS
+    ../getc_unlocked.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  getchar
+  SRCS
+    getchar.cpp
+  HDRS
+    ../getchar.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  getchar_unlocked
+  SRCS
+    getc_unlocked.cpp
+  HDRS
+    ../getc_unlocked.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  fgets
+  SRCS
+    fgets.cpp
+  HDRS
+    ../fgets.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
 add_entrypoint_object(
   stdin
   SRCS
diff --git a/libc/src/stdio/clearerr.cpp b/libc/src/stdio/generic/clearerr.cpp
similarity index 100%
rename from libc/src/stdio/clearerr.cpp
rename to libc/src/stdio/generic/clearerr.cpp
diff --git a/libc/src/stdio/clearerr_unlocked.cpp b/libc/src/stdio/generic/clearerr_unlocked.cpp
similarity index 100%
rename from libc/src/stdio/clearerr_unlocked.cpp
rename to libc/src/stdio/generic/clearerr_unlocked.cpp
diff --git a/libc/src/stdio/feof.cpp b/libc/src/stdio/generic/feof.cpp
similarity index 100%
rename from libc/src/stdio/feof.cpp
rename to libc/src/stdio/generic/feof.cpp
diff --git a/libc/src/stdio/feof_unlocked.cpp b/libc/src/stdio/generic/feof_unlocked.cpp
similarity index 100%
rename from libc/src/stdio/feof_unlocked.cpp
rename to libc/src/stdio/generic/feof_unlocked.cpp
diff --git a/libc/src/stdio/ferror.cpp b/libc/src/stdio/generic/ferror.cpp
similarity index 100%
rename from libc/src/stdio/ferror.cpp
rename to libc/src/stdio/generic/ferror.cpp
diff --git a/libc/src/stdio/ferror_unlocked.cpp b/libc/src/stdio/generic/ferror_unlocked.cpp
similarity index 100%
rename from libc/src/stdio/ferror_unlocked.cpp
rename to libc/src/stdio/generic/ferror_unlocked.cpp
diff --git a/libc/src/stdio/fgetc.cpp b/libc/src/stdio/generic/fgetc.cpp
similarity index 100%
rename from libc/src/stdio/fgetc.cpp
rename to libc/src/stdio/generic/fgetc.cpp
diff --git a/libc/src/stdio/fgetc_unlocked.cpp b/libc/src/stdio/generic/fgetc_unlocked.cpp
similarity index 100%
rename from libc/src/stdio/fgetc_unlocked.cpp
rename to libc/src/stdio/generic/fgetc_unlocked.cpp
diff --git a/libc/src/stdio/fgets.cpp b/libc/src/stdio/generic/fgets.cpp
similarity index 100%
rename from libc/src/stdio/fgets.cpp
rename to libc/src/stdio/generic/fgets.cpp
diff --git a/libc/src/stdio/getc.cpp b/libc/src/stdio/generic/getc.cpp
similarity index 100%
rename from libc/src/stdio/getc.cpp
rename to libc/src/stdio/generic/getc.cpp
diff --git a/libc/src/stdio/getc_unlocked.cpp b/libc/src/stdio/generic/getc_unlocked.cpp
similarity index 100%
rename from libc/src/stdio/getc_unlocked.cpp
rename to libc/src/stdio/generic/getc_unlocked.cpp
diff --git a/libc/src/stdio/getchar.cpp b/libc/src/stdio/generic/getchar.cpp
similarity index 100%
rename from libc/src/stdio/getchar.cpp
rename to libc/src/stdio/generic/getchar.cpp
diff --git a/libc/src/stdio/getchar_unlocked.cpp b/libc/src/stdio/generic/getchar_unlocked.cpp
similarity index 100%
rename from libc/src/stdio/getchar_unlocked.cpp
rename to libc/src/stdio/generic/getchar_unlocked.cpp
diff --git a/libc/src/stdio/gpu/CMakeLists.txt b/libc/src/stdio/gpu/CMakeLists.txt
index d35b1925c6a47a0..c47176621144e39 100644
--- a/libc/src/stdio/gpu/CMakeLists.txt
+++ b/libc/src/stdio/gpu/CMakeLists.txt
@@ -3,12 +3,46 @@ add_header_library(
   HDRS
     file.h
   DEPENDS
+    libc.src.__support.RPC.rpc_client
     libc.src.__support.common
     .stdin
     .stdout
     .stderr
 )
 
+add_entrypoint_object(
+  feof
+  SRCS
+    feof.cpp
+  HDRS
+    ../feof.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.RPC.rpc_client
+)
+
+add_entrypoint_object(
+  ferror
+  SRCS
+    ferror.cpp
+  HDRS
+    ../ferror.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.RPC.rpc_client
+)
+
+add_entrypoint_object(
+  clearerr
+  SRCS
+    clearerr.cpp
+  HDRS
+    ../clearerr.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.RPC.rpc_client
+)
+
 add_entrypoint_object(
   fopen
   SRCS
@@ -105,6 +139,85 @@ add_entrypoint_object(
     .gpu_file
 )
 
+add_entrypoint_object(
+  fgetc
+  SRCS
+    fgetc.cpp
+  HDRS
+    ../fgetc.h
+  DEPENDS
+    libc.include.stdio
+    .gpu_file
+)
+
+add_entrypoint_object(
+  fgetc_unlocked
+  SRCS
+    fgetc_unlocked.cpp
+  HDRS
+    ../fgetc_unlocked.h
+  DEPENDS
+    libc.include.stdio
+    .gpu_file
+)
+
+add_entrypoint_object(
+  getc
+  SRCS
+    getc.cpp
+  HDRS
+    ../getc.h
+  DEPENDS
+    libc.include.stdio
+    .gpu_file
+)
+
+add_entrypoint_object(
+  getc_unlocked
+  SRCS
+    getc_unlocked.cpp
+  HDRS
+    ../getc_unlocked.h
+  DEPENDS
+    libc.include.stdio
+    .gpu_file
+)
+
+add_entrypoint_object(
+  getchar
+  SRCS
+    getchar.cpp
+  HDRS
+    ../getchar.h
+  DEPENDS
+    libc.include.stdio
+    .gpu_file
+)
+
+add_entrypoint_object(
+  getchar_unlocked
+  SRCS
+    getc_unlocked.cpp
+  HDRS
+    ../getc_unlocked.h
+  DEPENDS
+    libc.include.stdio
+    .gpu_file
+)
+
+add_entrypoint_object(
+  fgets
+  SRCS
+    fgets.cpp
+  HDRS
+    ../fgets.h
+  DEPENDS
+    libc.include.stdio
+    .gpu_file
+    .feof
+    .ferror
+)
+
 add_entrypoint_object(
   stdin
   SRCS
diff --git a/libc/src/stdio/gpu/clearerr.cpp b/libc/src/stdio/gpu/clearerr.cpp
new file mode 100644
index 000000000000000..3773a93b46e1795
--- /dev/null
+++ b/libc/src/stdio/gpu/clearerr.cpp
@@ -0,0 +1,28 @@
+//===-- Implementation of clearerr ----------------------------------------===//
+//
+// 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/stdio/clearerr.h"
+#include "src/__support/RPC/rpc_client.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(void, clearerr, (::FILE * stream)) {
+  reinterpret_cast<__llvm_libc::File *>(stream)->clearerr();
+  rpc::Client::Port port = rpc::client.open<RPC_FERROR>();
+  port.send_and_recv(
+      [=](rpc::Buffer *buffer) {
+        buffer->data[0] = reinterpret_cast<uintptr_t>(stream);
+      },
+      [&](rpc::Buffer *) {});
+  port.close();
+  return ret;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/gpu/feof.cpp b/libc/src/stdio/gpu/feof.cpp
new file mode 100644
index 000000000000000..8686b283fc39a0f
--- /dev/null
+++ b/libc/src/stdio/gpu/feof.cpp
@@ -0,0 +1,28 @@
+//===-- Implementation of feof --------------------------------------------===//
+//
+// 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/stdio/feof.h"
+#include "src/__support/RPC/rpc_client.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, feof, (::FILE * stream)) {
+  int ret;
+  rpc::Client::Port port = rpc::client.open<RPC_FEOF>();
+  port.send_and_recv(
+      [=](rpc::Buffer *buffer) {
+        buffer->data[0] = reinterpret_cast<uintptr_t>(stream);
+      },
+      [&](rpc::Buffer *buffer) { ret = static_cast<int>(buffer->data[0]); });
+  port.close();
+  return ret;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/gpu/ferror.cpp b/libc/src/stdio/gpu/ferror.cpp
new file mode 100644
index 000000000000000..b2a572573d23c1f
--- /dev/null
+++ b/libc/src/stdio/gpu/ferror.cpp
@@ -0,0 +1,28 @@
+//===-- Implementation of ferror ------------------------------------------===//
+//
+// 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/stdio/ferror.h"
+#include "src/__support/RPC/rpc_client.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, ferror, (::FILE * stream)) {
+  int ret;
+  rpc::Client::Port port = rpc::client.open<RPC_FERROR>();
+  port.send_and_recv(
+      [=](rpc::Buffer *buffer) {
+        buffer->data[0] = reinterpret_cast<uintptr_t>(stream);
+      },
+      [&](rpc::Buffer *buffer) { ret = static_cast<int>(buffer->data[0]); });
+  port.close();
+  return ret;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/gpu/fgetc.cpp b/libc/src/stdio/gpu/fgetc.cpp
new file mode 100644
index 000000000000000..ff4ef27ccb88e9a
--- /dev/null
+++ b/libc/src/stdio/gpu/fgetc.cpp
@@ -0,0 +1,25 @@
+//===-- GPU implementation of fgetc ---------------------------------------===//
+//
+// 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/stdio/fgetc.h"
+#include "file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, fgetc, (::FILE * stream)) {
+  unsigned char c;
+  size_t r = file::read(stream, &c, 1);
+
+  if (r != 1)
+    return EOF;
+  return c;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/gpu/fgets.cpp b/libc/src/stdio/gpu/fgets.cpp
new file mode 100644
index 000000000000000..df00451e2a97034
--- /dev/null
+++ b/libc/src/stdio/gpu/fgets.cpp
@@ -0,0 +1,46 @@
+//===-- GPU implementation of fgets ---------------------------------------===//
+//
+// 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/stdio/fgets.h"
+#include "file.h"
+#include "src/stdio/feof.h"
+#include "src/stdio/ferror.h"
+
+#include <stddef.h>
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(char *, fgets,
+                   (char *__restrict str, int count,
+                    ::FILE *__restrict stream)) {
+  if (count < 1)
+    return nullptr;
+
+  // This implementation is very slow as it makes multiple RPC calls.
+  unsigned char c = '\0';
+  int i = 0;
+  for (; i < count - 1 && c != '\n'; ++i) {
+    auto r = file::read(stream, &c, 1);
+    if (r != 1)
+      break;
+
+    str[i] = c;
+  }
+
+  bool has_error = __llvm_libc::ferror(stream);
+  bool has_eof = __llvm_libc::feof(stream);
+
+  if (has_error || (i == 0 && has_eof))
+    return nullptr;
+
+  str[i] = '\0';
+  return str;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/gpu/getc.cpp b/libc/src/stdio/gpu/getc.cpp
new file mode 100644
index 000000000000000..a2272221d6b7b92
--- /dev/null
+++ b/libc/src/stdio/gpu/getc.cpp
@@ -0,0 +1,25 @@
+//===-- GPU implementation of getc ----------------------------------------===//
+//
+// 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/stdio/getc.h"
+#include "file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, getc, (::FILE * stream)) {
+  unsigned char c;
+  size_t r = file::read(stream, &c, 1);
+
+  if (r != 1)
+    return EOF;
+  return c;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/gpu/getchar.cpp b/libc/src/stdio/gpu/getchar.cpp
new file mode 100644
index 000000000000000..a7a6e6c55c13e1a
--- /dev/null
+++ b/libc/src/stdio/gpu/getchar.cpp
@@ -0,0 +1,25 @@
+//===-- GPU implementation of getchar -------------------------------------===//
+//
+// 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/stdio/getchar.h"
+#include "file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, getchar, ()) {
+  unsigned char c;
+  size_t r = file::read(stdin, &c, 1);
+
+  if (r != 1)
+    return EOF;
+  return c;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index 6090dc1f46c87ef..e042a8bd8be68f2 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -298,7 +298,7 @@ add_libc_test(
     libc.src.stdio.fopen
 )
 
-add_libc_unittest(
+add_libc_test(
   putc_test
   SUITE
     libc_stdio_unittests
@@ -330,7 +330,7 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
   )
 endif()
 
-add_libc_unittest(
+add_libc_test(
   fgetc_test
   SUITE
     libc_stdio_unittests
@@ -370,7 +370,7 @@ add_libc_unittest(
     libc.src.stdio.getc_unlocked
 )
 
-add_libc_unittest(
+add_libc_test(
   fgets_test
   SUITE
     libc_stdio_unittests
diff --git a/libc/utils/gpu/server/rpc_server.cpp b/libc/utils/gpu/server/rpc_server.cpp
index 22e937f0595c6af..70562b7e3e09548 100644
--- a/libc/utils/gpu/server/rpc_server.cpp
+++ b/libc/utils/gpu/server/rpc_server.cpp
@@ -164,6 +164,24 @@ struct Server {
       });
       break;
     }
+    case RPC_FEOF: {
+      port->recv_and_send([](rpc::Buffer *buffer) {
+        buffer->data[0] = feof(reinterpret_cast<FILE *>(buffer->data[0]));
+      });
+      break;
+    }
+    case RPC_FERROR: {
+      port->recv_and_send([](rpc::Buffer *buffer) {
+        buffer->data[0] = ferror(reinterpret_cast<FILE *>(buffer->data[0]));
+      });
+      break;
+    }
+    case RPC_CLEARERR: {
+      port->recv_and_send([](rpc::Buffer *buffer) {
+        clearerr(reinterpret_cast<FILE *>(buffer->data[0]));
+      });
+      break;
+    }
     case RPC_NOOP: {
       port->recv([](rpc::Buffer *) {});
       break;

>From c323d234efff436ab3887b64b005ffff0ab2f68e Mon Sep 17 00:00:00 2001
From: Joseph Huber <jhuber6 at vols.utk.edu>
Date: Thu, 14 Sep 2023 07:14:41 -0500
Subject: [PATCH 2/4] Add support docs

---
 libc/docs/gpu/support.rst | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libc/docs/gpu/support.rst b/libc/docs/gpu/support.rst
index 6d73addb28d0579..a19fca59270af05 100644
--- a/libc/docs/gpu/support.rst
+++ b/libc/docs/gpu/support.rst
@@ -126,6 +126,13 @@ stdio.h
 =============  =========  ============
 Function Name  Available  RPC Required
 =============  =========  ============
+feof           |check|    |check|
+ferror         |check|    |check|
+clearerr       |check|    |check|
+fgetc          |check|    |check|
+fgets          |check|    |check|
+getc           |check|    |check|
+getchar        |check|    |check|
 puts           |check|    |check|
 fputs          |check|    |check|
 fputc          |check|    |check|

>From 405c5d44fce7b0c463a9f4be5f8a2d54b5c54ff0 Mon Sep 17 00:00:00 2001
From: Joseph Huber <jhuber6 at vols.utk.edu>
Date: Thu, 14 Sep 2023 09:50:23 -0500
Subject: [PATCH 3/4] Fix clearerr leftover code

---
 libc/src/stdio/gpu/clearerr.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libc/src/stdio/gpu/clearerr.cpp b/libc/src/stdio/gpu/clearerr.cpp
index 3773a93b46e1795..ad93e61053668eb 100644
--- a/libc/src/stdio/gpu/clearerr.cpp
+++ b/libc/src/stdio/gpu/clearerr.cpp
@@ -14,7 +14,6 @@
 namespace __llvm_libc {
 
 LLVM_LIBC_FUNCTION(void, clearerr, (::FILE * stream)) {
-  reinterpret_cast<__llvm_libc::File *>(stream)->clearerr();
   rpc::Client::Port port = rpc::client.open<RPC_FERROR>();
   port.send_and_recv(
       [=](rpc::Buffer *buffer) {
@@ -22,7 +21,6 @@ LLVM_LIBC_FUNCTION(void, clearerr, (::FILE * stream)) {
       },
       [&](rpc::Buffer *) {});
   port.close();
-  return ret;
 }
 
 } // namespace __llvm_libc

>From de4d1041685789d749e5f54d7e914d563bef2b98 Mon Sep 17 00:00:00 2001
From: Joseph Huber <jhuber6 at vols.utk.edu>
Date: Thu, 14 Sep 2023 11:51:03 -0500
Subject: [PATCH 4/4] Make new ferror functions work when called with standard
 streams

Summary:
When crossing the device boundary we will find that the standard stream
have different pointers. This is intentional as it allows us to perform
IO on the standard streams without first needing to do any setup.
However, this needs to be accounted for in the implementation. This
patch adds some logic to  encode whether or not this is a standard
stream in the low orders of the bits. Because these are aligned to at
least a 4 bytes boundary we will always have access to 2 bits to store 4
different values.
---
 libc/src/stdio/gpu/clearerr.cpp      |  6 ++---
 libc/src/stdio/gpu/feof.cpp          |  6 ++---
 libc/src/stdio/gpu/ferror.cpp        |  6 ++---
 libc/src/stdio/gpu/file.h            | 33 ++++++++++++++++++++++++++++
 libc/utils/gpu/server/rpc_server.cpp |  7 +++---
 5 files changed, 43 insertions(+), 15 deletions(-)

diff --git a/libc/src/stdio/gpu/clearerr.cpp b/libc/src/stdio/gpu/clearerr.cpp
index ad93e61053668eb..dc7a3729aa78982 100644
--- a/libc/src/stdio/gpu/clearerr.cpp
+++ b/libc/src/stdio/gpu/clearerr.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/clearerr.h"
-#include "src/__support/RPC/rpc_client.h"
+#include "file.h"
 
 #include <stdio.h>
 
@@ -16,9 +16,7 @@ namespace __llvm_libc {
 LLVM_LIBC_FUNCTION(void, clearerr, (::FILE * stream)) {
   rpc::Client::Port port = rpc::client.open<RPC_FERROR>();
   port.send_and_recv(
-      [=](rpc::Buffer *buffer) {
-        buffer->data[0] = reinterpret_cast<uintptr_t>(stream);
-      },
+      [=](rpc::Buffer *buffer) { buffer->data[0] = file::from_stream(stream); },
       [&](rpc::Buffer *) {});
   port.close();
 }
diff --git a/libc/src/stdio/gpu/feof.cpp b/libc/src/stdio/gpu/feof.cpp
index 8686b283fc39a0f..27d27a445812c04 100644
--- a/libc/src/stdio/gpu/feof.cpp
+++ b/libc/src/stdio/gpu/feof.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/feof.h"
-#include "src/__support/RPC/rpc_client.h"
+#include "file.h"
 
 #include <stdio.h>
 
@@ -17,9 +17,7 @@ LLVM_LIBC_FUNCTION(int, feof, (::FILE * stream)) {
   int ret;
   rpc::Client::Port port = rpc::client.open<RPC_FEOF>();
   port.send_and_recv(
-      [=](rpc::Buffer *buffer) {
-        buffer->data[0] = reinterpret_cast<uintptr_t>(stream);
-      },
+      [=](rpc::Buffer *buffer) { buffer->data[0] = file::from_stream(stream); },
       [&](rpc::Buffer *buffer) { ret = static_cast<int>(buffer->data[0]); });
   port.close();
   return ret;
diff --git a/libc/src/stdio/gpu/ferror.cpp b/libc/src/stdio/gpu/ferror.cpp
index b2a572573d23c1f..e4bd631f392d4ea 100644
--- a/libc/src/stdio/gpu/ferror.cpp
+++ b/libc/src/stdio/gpu/ferror.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/ferror.h"
-#include "src/__support/RPC/rpc_client.h"
+#include "file.h"
 
 #include <stdio.h>
 
@@ -17,9 +17,7 @@ LLVM_LIBC_FUNCTION(int, ferror, (::FILE * stream)) {
   int ret;
   rpc::Client::Port port = rpc::client.open<RPC_FERROR>();
   port.send_and_recv(
-      [=](rpc::Buffer *buffer) {
-        buffer->data[0] = reinterpret_cast<uintptr_t>(stream);
-      },
+      [=](rpc::Buffer *buffer) { buffer->data[0] = file::from_stream(stream); },
       [&](rpc::Buffer *buffer) { ret = static_cast<int>(buffer->data[0]); });
   port.close();
   return ret;
diff --git a/libc/src/stdio/gpu/file.h b/libc/src/stdio/gpu/file.h
index 77817e070680ec9..42395a6d4287ce5 100644
--- a/libc/src/stdio/gpu/file.h
+++ b/libc/src/stdio/gpu/file.h
@@ -65,5 +65,38 @@ LIBC_INLINE uint64_t read(::FILE *f, void *data, size_t size) {
     return read_from_stream<RPC_READ_FROM_STREAM>(f, data, size);
 }
 
+enum Stream {
+  File = 0,
+  Stdin = 1,
+  Stdout = 2,
+  Stderr = 3,
+};
+
+// When copying between the client and server we need to indicate if this is one
+// of the special streams. We do this by enocding the low order bits of the
+// pointer to indicate if we need to use the host's standard stream.
+LIBC_INLINE uintptr_t from_stream(::FILE *f) {
+  if (f == stdin)
+    return reinterpret_cast<uintptr_t>(f) | Stdin;
+  if (f == stdout)
+    return reinterpret_cast<uintptr_t>(f) | Stdout;
+  if (f == stderr)
+    return reinterpret_cast<uintptr_t>(f) | Stderr;
+  return reinterpret_cast<uintptr_t>(f);
+}
+
+// Get the associated stream out of an encoded number.
+LIBC_INLINE ::FILE *to_stream(uintptr_t f) {
+  ::FILE *stream = reinterpret_cast<FILE *>(f & ~0x3ull);
+  Stream type = static_cast<Stream>(f & 0x3ull);
+  if (type == Stdin)
+    return stdin;
+  if (type == Stdout)
+    return stdout;
+  if (type == Stderr)
+    return stderr;
+  return stream;
+}
+
 } // namespace file
 } // namespace __llvm_libc
diff --git a/libc/utils/gpu/server/rpc_server.cpp b/libc/utils/gpu/server/rpc_server.cpp
index 70562b7e3e09548..c98b9fa46ce058b 100644
--- a/libc/utils/gpu/server/rpc_server.cpp
+++ b/libc/utils/gpu/server/rpc_server.cpp
@@ -9,6 +9,7 @@
 #include "rpc_server.h"
 
 #include "src/__support/RPC/rpc.h"
+#include "src/stdio/gpu/file.h"
 #include <atomic>
 #include <cstdio>
 #include <cstring>
@@ -166,19 +167,19 @@ struct Server {
     }
     case RPC_FEOF: {
       port->recv_and_send([](rpc::Buffer *buffer) {
-        buffer->data[0] = feof(reinterpret_cast<FILE *>(buffer->data[0]));
+        buffer->data[0] = feof(file::to_stream(buffer->data[0]));
       });
       break;
     }
     case RPC_FERROR: {
       port->recv_and_send([](rpc::Buffer *buffer) {
-        buffer->data[0] = ferror(reinterpret_cast<FILE *>(buffer->data[0]));
+        buffer->data[0] = ferror(file::to_stream(buffer->data[0]));
       });
       break;
     }
     case RPC_CLEARERR: {
       port->recv_and_send([](rpc::Buffer *buffer) {
-        clearerr(reinterpret_cast<FILE *>(buffer->data[0]));
+        clearerr(file::to_stream(buffer->data[0]));
       });
       break;
     }



More information about the libc-commits mailing list