[libc-commits] [libc] d04494c - [libc] Rework the file handling for the GPU

Joseph Huber via libc-commits libc-commits at lists.llvm.org
Wed Aug 9 12:42:34 PDT 2023


Author: Joseph Huber
Date: 2023-08-09T14:42:20-05:00
New Revision: d04494ccc913d0fa0aba914d2336619ad03fa3b0

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

LOG: [libc] Rework the file handling for the GPU

The GPU has much tighter requirements for handling IO functions.
Previously we attempted to define the GPU as one of the platform files.
Using a common interface allowed us to easily define these functions
without much extra work. However, it became more clear that this was a
poor fit for the GPU. The file interface uses function pointers, which
prevented inlining and caused bad perfromance and resource usage on the
GPU. Further, using an actual `FILE` type rather than referring to it as
a host stub prevented us from usin files coming from the host on the GPU
device.

After talking with @sivachandra, the approach now is to simply define
GPU specific versions of the functions we intend to support. Also, we
are ignoring `errno` for the time being as it is unlikely we will ever
care about supporting it fully.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D157427

Added: 
    libc/src/stdio/gpu/CMakeLists.txt
    libc/src/stdio/gpu/file.h
    libc/src/stdio/gpu/fputs.cpp
    libc/src/stdio/gpu/puts.cpp
    libc/src/stdio/gpu/stderr.cpp
    libc/src/stdio/gpu/stdin.cpp
    libc/src/stdio/gpu/stdout.cpp

Modified: 
    libc/config/gpu/entrypoints.txt
    libc/docs/gpu/support.rst
    libc/src/__support/File/CMakeLists.txt
    libc/src/__support/File/file.cpp
    libc/src/__support/File/file.h
    libc/src/stdio/CMakeLists.txt
    libc/test/src/stdio/CMakeLists.txt

Removed: 
    libc/src/__support/File/gpu/CMakeLists.txt
    libc/src/__support/File/gpu/dir.cpp
    libc/src/__support/File/gpu/file.cpp


################################################################################
diff  --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index f7a9f43dae9e71..8239bd0e877073 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 1c7b2538fffd53..f172c9b0ec36ea 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 53b2171deb6d7c..9644a5b633cd6b 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 65bfb585da7b11..f106d51f897ac4 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 2c6aff3b3be4d8..c1c6563fe7b1a7 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 @@ class File {
         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 9e32af5efc9a7c..00000000000000
--- 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 80cf072d0aa03c..00000000000000
--- 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 6cd4f2c1c5f5f9..9d2532614ed44c 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 00000000000000..f6795571c3164d
--- /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 00000000000000..c7b25592a30c4a
--- /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 00000000000000..c5919e8913746f
--- /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 00000000000000..58a3534c57ef99
--- /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 00000000000000..5e7b79e80e8a19
--- /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
similarity index 52%
rename from libc/src/__support/File/gpu/dir.cpp
rename to libc/src/stdio/gpu/stdin.cpp
index 31fdf7cf2223c3..408852b461de4d 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 00000000000000..02425d31e1e251
--- /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 af87aa96059bca..e3d30ecc5d5146 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(


        


More information about the libc-commits mailing list