aboutsummaryrefslogtreecommitdiff
path: root/libc
diff options
context:
space:
mode:
Diffstat (limited to 'libc')
-rw-r--r--libc/config/gpu/entrypoints.txt3
-rw-r--r--libc/docs/gpu/support.rst4
-rw-r--r--libc/src/__support/File/CMakeLists.txt3
-rw-r--r--libc/src/__support/File/file.cpp10
-rw-r--r--libc/src/__support/File/file.h12
-rw-r--r--libc/src/__support/File/gpu/CMakeLists.txt21
-rw-r--r--libc/src/__support/File/gpu/file.cpp183
-rw-r--r--libc/src/stdio/CMakeLists.txt37
-rw-r--r--libc/src/stdio/gpu/CMakeLists.txt61
-rw-r--r--libc/src/stdio/gpu/file.h89
-rw-r--r--libc/src/stdio/gpu/fputs.cpp27
-rw-r--r--libc/src/stdio/gpu/puts.cpp29
-rw-r--r--libc/src/stdio/gpu/stderr.cpp16
-rw-r--r--libc/src/stdio/gpu/stdin.cpp (renamed from libc/src/__support/File/gpu/dir.cpp)13
-rw-r--r--libc/src/stdio/gpu/stdout.cpp16
-rw-r--r--libc/test/src/stdio/CMakeLists.txt2
16 files changed, 287 insertions, 239 deletions
diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index f7a9f43..8239bd0 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -83,9 +83,6 @@ set(TARGET_LIBC_ENTRYPOINTS
# stdio.h entrypoints
libc.src.stdio.puts
libc.src.stdio.fputs
- libc.src.stdio.fread
- libc.src.stdio.fclose
- libc.src.stdio.fopen
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 1c7b253..f172c9b 100644
--- a/libc/docs/gpu/support.rst
+++ b/libc/docs/gpu/support.rst
@@ -124,6 +124,6 @@ Function Name Available RPC Required
============= ========= ============
puts |check| |check|
fputs |check| |check|
-fclose |check| |check|
-fopen |check| |check|
+fclose |check|
+fopen |check|
============= ========= ============
diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt
index 53b2171..9644a5b 100644
--- a/libc/src/__support/File/CMakeLists.txt
+++ b/libc/src/__support/File/CMakeLists.txt
@@ -1,4 +1,5 @@
-if(NOT (TARGET libc.src.__support.threads.mutex))
+if(NOT (TARGET libc.src.__support.threads.mutex)
+ OR LIBC_TARGET_ARCHITECTURE_IS_GPU)
# Not all platforms have a mutex implementation. If mutex is unvailable,
# we just skip everything about files.
return()
diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp
index 65bfb585..f106d51 100644
--- a/libc/src/__support/File/file.cpp
+++ b/libc/src/__support/File/file.cpp
@@ -25,7 +25,7 @@ FileIOResult File::write_unlocked(const void *data, size_t len) {
prev_op = FileOp::WRITE;
- if (!ENABLE_BUFFER || bufmode == _IONBF) { // unbuffered.
+ if (bufmode == _IONBF) { // unbuffered.
size_t ret_val =
write_unlocked_nbf(static_cast<const uint8_t *>(data), len);
flush_unlocked();
@@ -38,7 +38,7 @@ FileIOResult File::write_unlocked(const void *data, size_t len) {
}
FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) {
- if (ENABLE_BUFFER && pos > 0) { // If the buffer is not empty
+ if (pos > 0) { // If the buffer is not empty
// Flush the buffer
const size_t write_size = pos;
auto write_result = platform_write(this, buf, write_size);
@@ -325,9 +325,6 @@ ErrorOr<long> File::tell() {
}
int File::flush_unlocked() {
- if constexpr (!ENABLE_BUFFER)
- return 0;
-
if (prev_op == FileOp::WRITE && pos > 0) {
auto buf_result = platform_write(this, buf, pos);
if (buf_result.has_error() || buf_result.value < pos) {
@@ -341,9 +338,6 @@ int File::flush_unlocked() {
}
int File::set_buffer(void *buffer, size_t size, int buffer_mode) {
- if constexpr (!ENABLE_BUFFER)
- return EINVAL;
-
// We do not need to lock the file as this method should be called before
// other operations are performed on the file.
if (buffer != nullptr && size == 0)
diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h
index 2c6aff3..c1c6563 100644
--- a/libc/src/__support/File/file.h
+++ b/libc/src/__support/File/file.h
@@ -38,15 +38,6 @@ class File {
public:
static constexpr size_t DEFAULT_BUFFER_SIZE = 1024;
-// Some platforms like the GPU build cannot support buffering due to extra
-// resource usage or hardware constraints. This function allows us to optimize
-// out the buffering portions of the code in the general implementation.
-#if defined(LIBC_TARGET_ARCH_IS_GPU)
- static constexpr bool ENABLE_BUFFER = false;
-#else
- static constexpr bool ENABLE_BUFFER = true;
-#endif
-
using LockFunc = void(File *);
using UnlockFunc = void(File *);
@@ -167,8 +158,7 @@ public:
buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned),
mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0),
eof(false), err(false) {
- if constexpr (ENABLE_BUFFER)
- adjust_buf();
+ adjust_buf();
}
// Buffered write of |len| bytes from |data| without the file lock.
diff --git a/libc/src/__support/File/gpu/CMakeLists.txt b/libc/src/__support/File/gpu/CMakeLists.txt
deleted file mode 100644
index 9e32af5e..0000000
--- a/libc/src/__support/File/gpu/CMakeLists.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-add_object_library(
- gpu_file
- SRCS
- file.cpp
- DEPENDS
- libc.include.stdio
- libc.src.errno.errno
- libc.src.__support.CPP.new
- libc.src.__support.error_or
- libc.src.__support.File.file
-)
-
-add_object_library(
- gpu_dir
- SRCS
- dir.cpp
- DEPENDS
- libc.src.errno.errno
- libc.src.__support.error_or
- libc.src.__support.File.dir
-)
diff --git a/libc/src/__support/File/gpu/file.cpp b/libc/src/__support/File/gpu/file.cpp
deleted file mode 100644
index 80cf072..0000000
--- a/libc/src/__support/File/gpu/file.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-//===--- GPU specialization of the File data structure --------------------===//
-//
-// 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/__support/File/file.h"
-
-#include "src/__support/RPC/rpc_client.h"
-#include "src/errno/libc_errno.h" // For error macros
-#include "src/string/string_utils.h"
-
-#include <stdio.h>
-
-namespace __llvm_libc {
-
-namespace {
-
-FileIOResult write_func(File *, const void *, size_t);
-FileIOResult read_func(File *, void *, size_t);
-int close_func(File *);
-
-} // namespace
-
-class GPUFile : public File {
- uintptr_t file;
-
-public:
- constexpr GPUFile(uintptr_t file, File::ModeFlags modeflags)
- : File(&write_func, &read_func, nullptr, &close_func, nullptr, 0, _IONBF,
- false, modeflags),
- file(file) {}
-
- uintptr_t get_file() const { return file; }
-};
-
-namespace {
-
-int write_to_stdout(const void *data, size_t size) {
- uint64_t ret = 0;
- rpc::Client::Port port = rpc::client.open<RPC_WRITE_TO_STDOUT>();
- port.send_n(data, size);
- port.recv([&](rpc::Buffer *buffer) {
- ret = reinterpret_cast<uint64_t *>(buffer->data)[0];
- });
- port.close();
- return ret;
-}
-
-int write_to_stderr(const void *data, size_t size) {
- uint64_t ret = 0;
- rpc::Client::Port port = rpc::client.open<RPC_WRITE_TO_STDERR>();
- port.send_n(data, size);
- port.recv([&](rpc::Buffer *buffer) {
- ret = reinterpret_cast<uint64_t *>(buffer->data)[0];
- });
- port.close();
- return ret;
-}
-
-int write_to_stream(uintptr_t file, const void *data, size_t size) {
- uint64_t ret = 0;
- rpc::Client::Port port = rpc::client.open<RPC_WRITE_TO_STREAM>();
- port.send([&](rpc::Buffer *buffer) {
- reinterpret_cast<uintptr_t *>(buffer->data)[0] = file;
- });
- port.send_n(data, size);
- port.recv([&](rpc::Buffer *buffer) {
- ret = reinterpret_cast<uint64_t *>(buffer->data)[0];
- });
- port.close();
- return ret;
-}
-
-FileIOResult write_func(File *f, const void *data, size_t size) {
- auto *gpu_file = reinterpret_cast<GPUFile *>(f);
- int ret = 0;
- if (gpu_file == stdout)
- ret = write_to_stdout(data, size);
- else if (gpu_file == stderr)
- ret = write_to_stderr(data, size);
- else
- ret = write_to_stream(gpu_file->get_file(), data, size);
- if (ret < 0)
- return {0, -ret};
- return ret;
-}
-
-int read_from_stdin(void *buf, size_t size) {
- int ret = 0;
- uint64_t recv_size;
- rpc::Client::Port port = rpc::client.open<RPC_READ_FROM_STDIN>();
- port.send([=](rpc::Buffer *buffer) { buffer->data[0] = size; });
- port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; });
- port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; });
- port.close();
- return ret;
-}
-
-int read_from_stream(uintptr_t file, void *buf, size_t size) {
- int ret = 0;
- uint64_t recv_size;
- // TODO: For large sizes being written to a pointer in global memory, we
- // should be able to initiate a H2D memcpy via a separate RPC call at high
- // bandwidth.
- rpc::Client::Port port = rpc::client.open<RPC_READ_FROM_STREAM>();
- port.send([=](rpc::Buffer *buffer) {
- buffer->data[0] = size;
- buffer->data[1] = file;
- });
- port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; });
- port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; });
- port.close();
- return ret;
-}
-
-FileIOResult read_func(File *f, void *buf, size_t size) {
- auto *gpu_file = reinterpret_cast<GPUFile *>(f);
- int ret = 0;
- if (gpu_file == stdin)
- ret = read_from_stdin(buf, size);
- else
- ret = read_from_stream(gpu_file->get_file(), buf, size);
- if (ret < 0)
- return {0, -ret};
- return ret;
-}
-
-int close_func(File *file) {
- int ret = 0;
- GPUFile *gpu_file = reinterpret_cast<GPUFile *>(file);
- rpc::Client::Port port = rpc::client.open<RPC_CLOSE_FILE>();
- port.send_and_recv(
- [=](rpc::Buffer *buffer) { buffer->data[0] = gpu_file->get_file(); },
- [&](rpc::Buffer *buffer) { ret = buffer->data[0]; });
- port.close();
-
- return ret;
-}
-
-} // namespace
-
-void *ptr;
-
-ErrorOr<File *> openfile(const char *path, const char *mode) {
- auto modeflags = File::mode_flags(mode);
- if (modeflags == 0)
- return Error(EINVAL);
-
- uintptr_t file;
- rpc::Client::Port port = rpc::client.open<RPC_OPEN_FILE>();
- port.send_n(path, internal::string_length(path) + 1);
- port.send_and_recv(
- [=](rpc::Buffer *buffer) {
- inline_memcpy(buffer->data, mode, internal::string_length(mode) + 1);
- },
- [&](rpc::Buffer *buffer) { file = buffer->data[0]; });
- port.close();
-
- static GPUFile gpu_file(0, 0);
- gpu_file = GPUFile(file, modeflags);
- return &gpu_file;
-}
-
-static GPUFile StdIn(0UL, File::ModeFlags(File::OpenMode::READ));
-File *stdin = &StdIn;
-
-static GPUFile StdOut(0UL, File::ModeFlags(File::OpenMode::APPEND));
-File *stdout = &StdOut;
-
-static GPUFile StdErr(0UL, File::ModeFlags(File::OpenMode::APPEND));
-File *stderr = &StdErr;
-
-} // namespace __llvm_libc
-
-// Provide the external defintitions of the standard IO streams.
-extern "C" {
-FILE *stdin = reinterpret_cast<FILE *>(&__llvm_libc::StdIn);
-FILE *stderr = reinterpret_cast<FILE *>(&__llvm_libc::StdErr);
-FILE *stdout = reinterpret_cast<FILE *>(&__llvm_libc::StdOut);
-}
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 6cd4f2c..9d25326 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -1,3 +1,23 @@
+# Helper function that creates an alias if a target specific implementation
+# exists, otherwise it uses a generic one.
+function(add_stdio_entrypoint_object name)
+ if(TARGET libc.src.stdio.${LIBC_TARGET_OS}.${name})
+ add_entrypoint_object(
+ ${name}
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.${name}
+ )
+ elseif(TARGET libc.src.stdio.generic_${name})
+ add_entrypoint_object(
+ ${name}
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.${name}
+ )
+ endif()
+endfunction(add_stdio_entrypoint_object)
+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
endif()
@@ -322,7 +342,7 @@ add_entrypoint_object(
)
add_entrypoint_object(
- fputs
+ generic_fputs
SRCS
fputs.cpp
HDRS
@@ -336,7 +356,7 @@ add_entrypoint_object(
add_entrypoint_object(
- puts
+ generic_puts
SRCS
puts.cpp
HDRS
@@ -386,7 +406,7 @@ add_entrypoint_object(
)
add_entrypoint_object(
- stdin
+ generic_stdin
SRCS
stdin.cpp
HDRS
@@ -398,7 +418,7 @@ add_entrypoint_object(
)
add_entrypoint_object(
- stdout
+ generic_stdout
SRCS
stdout.cpp
HDRS
@@ -410,7 +430,7 @@ add_entrypoint_object(
)
add_entrypoint_object(
- stderr
+ generic_stderr
SRCS
stderr.cpp
HDRS
@@ -608,3 +628,10 @@ add_entrypoint_object(
DEPENDS
.${LIBC_TARGET_OS}.remove
)
+
+# These entrypoints have multiple potential implementations.
+add_stdio_entrypoint_object(puts)
+add_stdio_entrypoint_object(fputs)
+add_stdio_entrypoint_object(stdin)
+add_stdio_entrypoint_object(stdout)
+add_stdio_entrypoint_object(stderr)
diff --git a/libc/src/stdio/gpu/CMakeLists.txt b/libc/src/stdio/gpu/CMakeLists.txt
new file mode 100644
index 0000000..f679557
--- /dev/null
+++ b/libc/src/stdio/gpu/CMakeLists.txt
@@ -0,0 +1,61 @@
+add_header_library(
+ gpu_file
+ HDRS
+ file.h
+ DEPENDS
+ libc.src.__support.common
+ libc.src.__support.CPP.string_view
+ libc.src.__support.RPC.rpc_client
+)
+
+add_entrypoint_object(
+ puts
+ SRCS
+ puts.cpp
+ HDRS
+ ../puts.h
+ DEPENDS
+ libc.include.stdio
+ .gpu_file
+)
+
+add_entrypoint_object(
+ fputs
+ SRCS
+ fputs.cpp
+ HDRS
+ ../fputs.h
+ DEPENDS
+ libc.include.stdio
+ .gpu_file
+)
+
+add_entrypoint_object(
+ stdin
+ SRCS
+ stdin.cpp
+ HDRS
+ ../stdin.h
+ DEPENDS
+ libc.include.stdio
+)
+
+add_entrypoint_object(
+ stdout
+ SRCS
+ stdout.cpp
+ HDRS
+ ../stdout.h
+ DEPENDS
+ libc.include.stdio
+)
+
+add_entrypoint_object(
+ stderr
+ SRCS
+ stderr.cpp
+ HDRS
+ ../stderr.h
+ DEPENDS
+ libc.include.stdio
+)
diff --git a/libc/src/stdio/gpu/file.h b/libc/src/stdio/gpu/file.h
new file mode 100644
index 0000000..c7b2559
--- /dev/null
+++ b/libc/src/stdio/gpu/file.h
@@ -0,0 +1,89 @@
+//===--- GPU helper functions--------------------===//
+//
+// 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/__support/RPC/rpc_client.h"
+#include "src/string/string_utils.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+namespace file {
+
+LIBC_INLINE uint64_t write_to_stdout(const void *data, size_t size) {
+ uint64_t ret = 0;
+ rpc::Client::Port port = rpc::client.open<RPC_WRITE_TO_STDOUT>();
+ port.send_n(data, size);
+ port.recv([&](rpc::Buffer *buffer) {
+ ret = reinterpret_cast<uint64_t *>(buffer->data)[0];
+ });
+ port.close();
+ return ret;
+}
+
+LIBC_INLINE uint64_t write_to_stderr(const void *data, size_t size) {
+ uint64_t ret = 0;
+ rpc::Client::Port port = rpc::client.open<RPC_WRITE_TO_STDERR>();
+ port.send_n(data, size);
+ port.recv([&](rpc::Buffer *buffer) {
+ ret = reinterpret_cast<uint64_t *>(buffer->data)[0];
+ });
+ port.close();
+ return ret;
+}
+
+LIBC_INLINE uint64_t write_to_stream(uintptr_t file, const void *data,
+ size_t size) {
+ uint64_t ret = 0;
+ rpc::Client::Port port = rpc::client.open<RPC_WRITE_TO_STREAM>();
+ port.send([&](rpc::Buffer *buffer) {
+ reinterpret_cast<uintptr_t *>(buffer->data)[0] = file;
+ });
+ port.send_n(data, size);
+ port.recv([&](rpc::Buffer *buffer) {
+ ret = reinterpret_cast<uint64_t *>(buffer->data)[0];
+ });
+ port.close();
+ return ret;
+}
+
+LIBC_INLINE uint64_t write(FILE *f, const void *data, size_t size) {
+ if (f == stdout)
+ return write_to_stdout(data, size);
+ else if (f == stderr)
+ return write_to_stderr(data, size);
+ else
+ return write_to_stream(reinterpret_cast<uintptr_t>(f), data, size);
+}
+
+LIBC_INLINE uint64_t read_from_stdin(void *buf, size_t size) {
+ uint64_t ret = 0;
+ uint64_t recv_size;
+ rpc::Client::Port port = rpc::client.open<RPC_READ_FROM_STDIN>();
+ port.send([=](rpc::Buffer *buffer) { buffer->data[0] = size; });
+ port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; });
+ port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; });
+ port.close();
+ return ret;
+}
+
+LIBC_INLINE uint64_t read_from_stream(uintptr_t file, void *buf, size_t size) {
+ uint64_t ret = 0;
+ uint64_t recv_size;
+ rpc::Client::Port port = rpc::client.open<RPC_READ_FROM_STREAM>();
+ port.send([=](rpc::Buffer *buffer) {
+ buffer->data[0] = size;
+ buffer->data[1] = file;
+ });
+ port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; });
+ port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; });
+ port.close();
+ return ret;
+}
+
+} // namespace file
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/gpu/fputs.cpp b/libc/src/stdio/gpu/fputs.cpp
new file mode 100644
index 0000000..c5919e8
--- /dev/null
+++ b/libc/src/stdio/gpu/fputs.cpp
@@ -0,0 +1,27 @@
+//===-- GPU Implementation of fputs ---------------------------------------===//
+//
+// 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/fputs.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/errno/libc_errno.h"
+#include "src/stdio/gpu/file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, fputs,
+ (const char *__restrict str, ::FILE *__restrict stream)) {
+ cpp::string_view str_view(str);
+ auto written = file::write(stream, str, str_view.size());
+ if (written != str_view.size())
+ return EOF;
+ return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/gpu/puts.cpp b/libc/src/stdio/gpu/puts.cpp
new file mode 100644
index 0000000..58a3534
--- /dev/null
+++ b/libc/src/stdio/gpu/puts.cpp
@@ -0,0 +1,29 @@
+//===-- GPU Implementation of puts ----------------------------------------===//
+//
+// 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/puts.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/errno/libc_errno.h"
+#include "src/stdio/gpu/file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, puts, (const char *__restrict str)) {
+ cpp::string_view str_view(str);
+ auto written = file::write(stdout, str, str_view.size());
+ if (written != str_view.size())
+ return EOF;
+ written = file::write(stdout, "\n", 1);
+ if (written != 1)
+ return EOF;
+ return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/gpu/stderr.cpp b/libc/src/stdio/gpu/stderr.cpp
new file mode 100644
index 0000000..5e7b79e
--- /dev/null
+++ b/libc/src/stdio/gpu/stderr.cpp
@@ -0,0 +1,16 @@
+//===-- Definition of the global stderr object ----------------------------===//
+//
+// 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 <stdio.h>
+
+namespace __llvm_libc {
+static struct {
+} stub;
+FILE *stderr = reinterpret_cast<FILE *>(&stub);
+} // namespace __llvm_libc
+extern "C" FILE *stderr = reinterpret_cast<FILE *>(&__llvm_libc::stub);
diff --git a/libc/src/__support/File/gpu/dir.cpp b/libc/src/stdio/gpu/stdin.cpp
index 31fdf7c..408852b 100644
--- a/libc/src/__support/File/gpu/dir.cpp
+++ b/libc/src/stdio/gpu/stdin.cpp
@@ -1,4 +1,4 @@
-//===--- GPU implementation of the Dir helpers ----------------------------===//
+//===-- Definition of the global stdin object -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,11 @@
//
//===----------------------------------------------------------------------===//
-#include "src/__support/File/dir.h"
+#include <stdio.h>
-#include "src/__support/error_or.h"
-
-namespace __llvm_libc {} // namespace __llvm_libc
+namespace __llvm_libc {
+static struct {
+} stub;
+FILE *stdin = reinterpret_cast<FILE *>(&stub);
+} // namespace __llvm_libc
+extern "C" FILE *stdin = reinterpret_cast<FILE *>(&__llvm_libc::stub);
diff --git a/libc/src/stdio/gpu/stdout.cpp b/libc/src/stdio/gpu/stdout.cpp
new file mode 100644
index 0000000..02425d3
--- /dev/null
+++ b/libc/src/stdio/gpu/stdout.cpp
@@ -0,0 +1,16 @@
+//===-- Definition of the global stdout object ----------------------------===//
+//
+// 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 <stdio.h>
+
+namespace __llvm_libc {
+static struct {
+} stub;
+FILE *stdout = reinterpret_cast<FILE *>(&stub);
+} // namespace __llvm_libc
+extern "C" FILE *stdout = reinterpret_cast<FILE *>(&__llvm_libc::stub);
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index af87aa9..e3d30ec 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -260,6 +260,8 @@ add_libc_test(
fputs_test.cpp
DEPENDS
libc.src.stdio.fputs
+ libc.src.stdio.stdout
+ libc.src.stdio.stderr
)
add_libc_test(