diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt index ad68216a76b94..731508088cb6f 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 fd27273ed562e..806af5f219dfb 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 61e17756fa647..2fd318f06a7db 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 169bc592dee48..380474ce27118 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 282d056bba712..2ecef879eb4bb 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 047b68931bce5..1b1e2a903cc0b 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 0000000000000..373164a0c53a3 --- /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 + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(int, ungetc, (int c, ::FILE *stream)) { + int ret; + rpc::Client::Port port = rpc::client.open(); + port.send_and_recv( + [=](rpc::Buffer *buffer) { + buffer->data[0] = c; + buffer->data[1] = file::from_stream(stream); + }, + [&](rpc::Buffer *buffer) { ret = static_cast(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 75eecc87ef265..c98995ff0811b 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 1c1c9f1ae9e6b..0550115f7cd1a 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(buffer->data[0]), + file::to_stream(buffer->data[1])); + }); + break; + } case RPC_NOOP: { port->recv([](rpc::Buffer *) {}); break;