[libc-commits] [libc] ddc30ff - [libc] Implement the 'ungetc' function on the GPU (#69248)

via libc-commits libc-commits at lists.llvm.org
Tue Oct 17 11:02:35 PDT 2023


Author: Joseph Huber
Date: 2023-10-17T13:02:31-05:00
New Revision: ddc30ff802eb135934fc7b785d33c05217ab9e39

URL: https://github.com/llvm/llvm-project/commit/ddc30ff802eb135934fc7b785d33c05217ab9e39
DIFF: https://github.com/llvm/llvm-project/commit/ddc30ff802eb135934fc7b785d33c05217ab9e39.diff

LOG: [libc] Implement the 'ungetc' function on the GPU (#69248)

Summary:
This function follows closely with the pattern of all the other
functions. That is, making a new opcode and forwarding the call to the
host. However, this also required modifying the test somewhat. It seems
that not all `libc` implementations follow the same error rules as are
tested here, and it is not explicit in the standard, so we simply
disable these EOF checks when targeting the GPU.

Added: 
    libc/src/stdio/generic/ungetc.cpp
    libc/src/stdio/gpu/ungetc.cpp

Modified: 
    libc/config/gpu/entrypoints.txt
    libc/docs/gpu/support.rst
    libc/include/llvm-libc-types/rpc_opcodes_t.h
    libc/src/stdio/CMakeLists.txt
    libc/src/stdio/generic/CMakeLists.txt
    libc/src/stdio/gpu/CMakeLists.txt
    libc/test/src/stdio/ungetc_test.cpp
    libc/utils/gpu/server/rpc_server.cpp

Removed: 
    libc/src/stdio/ungetc.cpp


################################################################################
diff  --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index ad68216a76b9429..731508088cb6f8b 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -104,6 +104,7 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.stdio.fgetc
     libc.src.stdio.getc
     libc.src.stdio.getchar
+    libc.src.stdio.ungetc
     libc.src.stdio.stdin
     libc.src.stdio.stdout
     libc.src.stdio.stderr

diff  --git a/libc/docs/gpu/support.rst b/libc/docs/gpu/support.rst
index fd27273ed562e49..806af5f219dfb43 100644
--- a/libc/docs/gpu/support.rst
+++ b/libc/docs/gpu/support.rst
@@ -134,6 +134,7 @@ ftell          |check|    |check|
 fflush         |check|    |check|
 fgetc          |check|    |check|
 fgets          |check|    |check|
+ungetc         |check|    |check|
 getc           |check|    |check|
 getchar        |check|    |check|
 puts           |check|    |check|

diff  --git a/libc/include/llvm-libc-types/rpc_opcodes_t.h b/libc/include/llvm-libc-types/rpc_opcodes_t.h
index 61e17756fa64776..2fd318f06a7db16 100644
--- a/libc/include/llvm-libc-types/rpc_opcodes_t.h
+++ b/libc/include/llvm-libc-types/rpc_opcodes_t.h
@@ -29,6 +29,7 @@ typedef enum {
   RPC_FSEEK,
   RPC_FTELL,
   RPC_FFLUSH,
+  RPC_UNGETC,
   RPC_LAST = 0xFFFF,
 } rpc_opcode_t;
 

diff  --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 169bc592dee4885..380474ce2711804 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -54,18 +54,6 @@ add_entrypoint_object(
     libc.src.__support.File.platform_file
 )
 
-add_entrypoint_object(
-  ungetc
-  SRCS
-    ungetc.cpp
-  HDRS
-    ungetc.h
-  DEPENDS
-    libc.include.stdio
-    libc.src.__support.File.file
-    libc.src.__support.File.platform_file
-)
-
 add_entrypoint_object(
   fopencookie
   SRCS
@@ -286,6 +274,7 @@ 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(ungetc)
 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 282d056bba71299..2ecef879eb4bbfe 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -342,6 +342,18 @@ add_entrypoint_object(
     libc.src.__support.File.platform_file
 )
 
+add_entrypoint_object(
+  ungetc
+  SRCS
+    ungetc.cpp
+  HDRS
+    ../ungetc.h
+  DEPENDS
+    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/ungetc.cpp b/libc/src/stdio/generic/ungetc.cpp
similarity index 100%
rename from libc/src/stdio/ungetc.cpp
rename to libc/src/stdio/generic/ungetc.cpp

diff  --git a/libc/src/stdio/gpu/CMakeLists.txt b/libc/src/stdio/gpu/CMakeLists.txt
index 047b68931bce5c3..1b1e2a903cc0b9b 100644
--- a/libc/src/stdio/gpu/CMakeLists.txt
+++ b/libc/src/stdio/gpu/CMakeLists.txt
@@ -251,6 +251,17 @@ add_entrypoint_object(
     .ferror
 )
 
+add_entrypoint_object(
+  ungetc
+  SRCS
+    ungetc.cpp
+  HDRS
+    ../ungetc.h
+  DEPENDS
+    libc.include.stdio
+    .gpu_file
+)
+
 add_entrypoint_object(
   stdin
   SRCS

diff  --git a/libc/src/stdio/gpu/ungetc.cpp b/libc/src/stdio/gpu/ungetc.cpp
new file mode 100644
index 000000000000000..373164a0c53a326
--- /dev/null
+++ b/libc/src/stdio/gpu/ungetc.cpp
@@ -0,0 +1,29 @@
+//===-- Implementation of ungetc ------------------------------------------===//
+//
+// 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/ungetc.h"
+#include "file.h"
+
+#include <stdio.h>
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, ungetc, (int c, ::FILE *stream)) {
+  int ret;
+  rpc::Client::Port port = rpc::client.open<RPC_UNGETC>();
+  port.send_and_recv(
+      [=](rpc::Buffer *buffer) {
+        buffer->data[0] = c;
+        buffer->data[1] = file::from_stream(stream);
+      },
+      [&](rpc::Buffer *buffer) { ret = static_cast<int>(buffer->data[0]); });
+  port.close();
+  return ret;
+}
+
+} // namespace LIBC_NAMESPACE

diff  --git a/libc/test/src/stdio/ungetc_test.cpp b/libc/test/src/stdio/ungetc_test.cpp
index 75eecc87ef265ff..c98995ff0811bb3 100644
--- a/libc/test/src/stdio/ungetc_test.cpp
+++ b/libc/test/src/stdio/ungetc_test.cpp
@@ -24,12 +24,16 @@ TEST(LlvmLibcUngetcTest, UngetAndReadBack) {
   constexpr size_t CONTENT_SIZE = sizeof(CONTENT);
   ASSERT_EQ(CONTENT_SIZE,
             LIBC_NAMESPACE::fwrite(CONTENT, 1, CONTENT_SIZE, file));
+#ifndef LIBC_TARGET_ARCH_IS_GPU // Behavior varies between libc implementations.
   // Cannot unget to an un-readable file.
   ASSERT_EQ(EOF, LIBC_NAMESPACE::ungetc('1', file));
+#endif
   ASSERT_EQ(0, LIBC_NAMESPACE::fclose(file));
 
   file = LIBC_NAMESPACE::fopen(FILENAME, "r+");
   ASSERT_FALSE(file == nullptr);
+  // Calling with an EOF should always return EOF without doing anything.
+  ASSERT_EQ(EOF, LIBC_NAMESPACE::ungetc(EOF, file));
   char c;
   ASSERT_EQ(LIBC_NAMESPACE::fread(&c, 1, 1, file), size_t(1));
   ASSERT_EQ(c, CONTENT[0]);
@@ -43,8 +47,10 @@ TEST(LlvmLibcUngetcTest, UngetAndReadBack) {
   // ungetc should not fail after a seek operation.
   int unget_char = 'z';
   ASSERT_EQ(unget_char, LIBC_NAMESPACE::ungetc(unget_char, file));
+#ifndef LIBC_TARGET_ARCH_IS_GPU // Behavior varies between libc implementations.
   // Another unget should fail.
   ASSERT_EQ(EOF, LIBC_NAMESPACE::ungetc(unget_char, file));
+#endif
   // ungetting a char at the beginning of the file will allow us to fetch
   // one additional character.
   char new_data[CONTENT_SIZE + 1];
@@ -53,8 +59,10 @@ TEST(LlvmLibcUngetcTest, UngetAndReadBack) {
   ASSERT_STREQ("zabcdef", new_data);
 
   ASSERT_EQ(size_t(1), LIBC_NAMESPACE::fwrite("x", 1, 1, file));
+#ifndef LIBC_TARGET_ARCH_IS_GPU // Behavior varies between libc implementations.
   // unget should fail after a write operation.
   ASSERT_EQ(EOF, LIBC_NAMESPACE::ungetc('1', file));
+#endif
 
   ASSERT_EQ(0, LIBC_NAMESPACE::fclose(file));
 }

diff  --git a/libc/utils/gpu/server/rpc_server.cpp b/libc/utils/gpu/server/rpc_server.cpp
index 1c1c9f1ae9e6b5d..0550115f7cd1a1c 100644
--- a/libc/utils/gpu/server/rpc_server.cpp
+++ b/libc/utils/gpu/server/rpc_server.cpp
@@ -186,6 +186,13 @@ struct Server {
       });
       break;
     }
+    case RPC_UNGETC: {
+      port->recv_and_send([](rpc::Buffer *buffer) {
+        buffer->data[0] = ungetc(static_cast<int>(buffer->data[0]),
+                                 file::to_stream(buffer->data[1]));
+      });
+      break;
+    }
     case RPC_NOOP: {
       port->recv([](rpc::Buffer *) {});
       break;


        


More information about the libc-commits mailing list