[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