[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 04:33:25 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] [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;



More information about the libc-commits mailing list