[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.
This commit is contained in:
Joseph Huber 2023-10-17 14:02:31 -04:00 committed by GitHub
parent 761c9dd927
commit ddc30ff802
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 71 additions and 12 deletions

View File

@ -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

View File

@ -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|

View File

@ -29,6 +29,7 @@ typedef enum {
RPC_FSEEK,
RPC_FTELL,
RPC_FFLUSH,
RPC_UNGETC,
RPC_LAST = 0xFFFF,
} rpc_opcode_t;

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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));
}

View File

@ -186,6 +186,13 @@ private:
});
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;