Skip to content

[libc] Migrate from baremetal stdio.h to generic stdio.h #152748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

saturn691
Copy link
Contributor

@saturn691 saturn691 commented Aug 8, 2025

This is a follow up to the RFC here:
https://discourse.llvm.org/t/rfc-implementation-of-stdio-on-baremetal/86944

This provides the stdout/stderr/stdin symbols (which now don't have to provided by the user). This allows the user to have access to all functions, currently I've only tested fprintf but in theory everything that works in the generic folder should work in the baremetal configuration.

All streams are non-buffered, which does NOT require flushing. It is based on the CookieFile that already existed

@saturn691 saturn691 requested review from lntue and petrhosek and removed request for lntue August 8, 2025 15:47
This is a follow up to the RFC here:
https://discourse.llvm.org/t/rfc-implementation-of-stdio-on-baremetal/86944

Currently, this is very rough and this PR is only for feedback. This provides
the stdout/stderr/stdin symbols (which now don't have to provided by the user).
This allows the user to have access to all functions, currently I've only
tested `fprintf` but in theory everything that works in the generic folder
should work in the baremetal configuration.

The most important part of the implementation is:
- where we want to put `std*`. Here it is a part of `libc.a`
- if we want it here, what should we set it to? I have aliased
all the streams to a _non-buffered_ stream, which does NOT
require flushing. It is based on the CookieFile that already existed
Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with the fopencookie refactor, I'm still intending to rewrite FILE but since this is just moving the code it seems fine to me.

I was under the impression that baremetal systems wanted to avoid function pointers due to performance concerns, and that was why the system was set up with direct writes. Is that still the case?

@saturn691 saturn691 marked this pull request as ready for review August 14, 2025 15:13
@llvmbot
Copy link
Member

llvmbot commented Aug 14, 2025

@llvm/pr-subscribers-backend-risc-v

Author: William Huynh (saturn691)

Changes

This is a follow up to the RFC here:
https://discourse.llvm.org/t/rfc-implementation-of-stdio-on-baremetal/86944

Currently, this is very rough and this PR is only for feedback. This provides the stdout/stderr/stdin symbols (which now don't have to provided by the user). This allows the user to have access to all functions, currently I've only tested fprintf but in theory everything that works in the generic folder should work in the baremetal configuration.

The most important part of the implementation is:

  • where we want to put std*. Here it is a part of libc.a
  • if we want it here, what should we set it to? I have aliased all the streams to a non-buffered stream, which does NOT require flushing. It is based on the CookieFile that already existed

Patch is 26.72 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/152748.diff

18 Files Affected:

  • (modified) libc/config/baremetal/aarch64/entrypoints.txt (+2)
  • (modified) libc/config/baremetal/arm/entrypoints.txt (+2)
  • (modified) libc/config/baremetal/riscv/entrypoints.txt (+2)
  • (modified) libc/src/__support/File/CMakeLists.txt (+17-1)
  • (added) libc/src/__support/File/baremetal/CMakeLists.txt (+32)
  • (added) libc/src/__support/File/baremetal/stderr.cpp (+34)
  • (added) libc/src/__support/File/baremetal/stdin.cpp (+34)
  • (added) libc/src/__support/File/baremetal/stdout.cpp (+34)
  • (added) libc/src/__support/File/cookie_file.h (+75)
  • (modified) libc/src/__support/OSUtil/baremetal/io.cpp (-39)
  • (modified) libc/src/__support/OSUtil/baremetal/io.h (+38)
  • (modified) libc/src/stdio/CMakeLists.txt (+1)
  • (modified) libc/src/stdio/baremetal/CMakeLists.txt (-96)
  • (removed) libc/src/stdio/baremetal/printf.cpp (-54)
  • (removed) libc/src/stdio/baremetal/putchar.cpp (-24)
  • (removed) libc/src/stdio/baremetal/puts.cpp (-26)
  • (removed) libc/src/stdio/baremetal/vprintf.cpp (-52)
  • (modified) libc/src/stdio/fopencookie.cpp (+4-65)
diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt
index e24e2b9e2a7bb..787fa5b6eb519 100644
--- a/libc/config/baremetal/aarch64/entrypoints.txt
+++ b/libc/config/baremetal/aarch64/entrypoints.txt
@@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # stdio.h entrypoints
     libc.src.stdio.asprintf
+    libc.src.stdio.fopencookie
+    libc.src.stdio.fprintf
     libc.src.stdio.getchar
     libc.src.stdio.printf
     libc.src.stdio.putchar
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 44e9c3edac353..4b19b4267e20d 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # stdio.h entrypoints
     libc.src.stdio.asprintf
+    libc.src.stdio.fopencookie
+    libc.src.stdio.fprintf
     libc.src.stdio.getchar
     libc.src.stdio.printf
     libc.src.stdio.putchar
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index 29cf322a2e33f..7198dab044532 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # stdio.h entrypoints
     libc.src.stdio.asprintf
+    libc.src.stdio.fopencookie
+    libc.src.stdio.fprintf
     libc.src.stdio.getchar
     libc.src.stdio.printf
     libc.src.stdio.putchar
diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt
index 253243ff40ded..bcc6cef2163a1 100644
--- a/libc/src/__support/File/CMakeLists.txt
+++ b/libc/src/__support/File/CMakeLists.txt
@@ -22,6 +22,18 @@ add_object_library(
     libc.src.__support.error_or
 )
 
+add_header_library(
+  cookie_file
+  HDRS
+    cookie_file.h
+  DEPENDS
+    .file
+    libc.hdr.errno_macros
+    libc.hdr.types.cookie_io_functions_t
+    libc.hdr.types.off_t
+    libc.src.__support.CPP.new
+)
+
 add_object_library(
   dir
   SRCS
@@ -41,7 +53,11 @@ endif()
 
 add_subdirectory(${LIBC_TARGET_OS})
 
-set(target_file libc.src.__support.File.${LIBC_TARGET_OS}.file)
+if(LIBC_TARGET_OS_IS_BAREMETAL)
+  set(target_file libc.src.__support.File.cookie_file)
+else()
+  set(target_file libc.src.__support.File.${LIBC_TARGET_OS}.file)
+endif()
 set(target_stdout libc.src.__support.File.${LIBC_TARGET_OS}.stdout)
 set(target_stderr libc.src.__support.File.${LIBC_TARGET_OS}.stderr)
 set(target_stdin libc.src.__support.File.${LIBC_TARGET_OS}.stdin)
diff --git a/libc/src/__support/File/baremetal/CMakeLists.txt b/libc/src/__support/File/baremetal/CMakeLists.txt
new file mode 100644
index 0000000000000..a789948368f02
--- /dev/null
+++ b/libc/src/__support/File/baremetal/CMakeLists.txt
@@ -0,0 +1,32 @@
+add_object_library(
+  stdout
+  SRCS
+    stdout.cpp
+  DEPENDS
+    libc.hdr.stdio_macros
+    libc.src.__support.File.cookie_file
+    libc.src.__support.File.file
+    libc.src.__support.OSUtil.osutil
+)
+
+add_object_library(
+  stdin
+  SRCS
+    stdin.cpp
+  DEPENDS
+    libc.hdr.stdio_macros
+    libc.src.__support.File.cookie_file
+    libc.src.__support.File.file
+    libc.src.__support.OSUtil.osutil
+)
+
+add_object_library(
+  stderr
+  SRCS
+    stderr.cpp
+  DEPENDS
+    libc.hdr.stdio_macros
+    libc.src.__support.File.cookie_file
+    libc.src.__support.File.file
+    libc.src.__support.OSUtil.osutil
+)
diff --git a/libc/src/__support/File/baremetal/stderr.cpp b/libc/src/__support/File/baremetal/stderr.cpp
new file mode 100644
index 0000000000000..0a67771f034aa
--- /dev/null
+++ b/libc/src/__support/File/baremetal/stderr.cpp
@@ -0,0 +1,34 @@
+//===--- Definition of baremetal stderr -----------------------------------===//
+//
+// 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 "hdr/stdio_macros.h"
+#include "src/__support/File/cookie_file.h"
+#include "src/__support/File/file.h"
+#include "src/__support/OSUtil/baremetal/io.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+cookie_io_functions_t io_func = {.read = nullptr,
+                                 .write = __llvm_libc_stdio_write,
+                                 .seek = nullptr,
+                                 .close = nullptr};
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+// Buffering is implementation defined. Therefore to save RAM, we use no
+// buffering
+static CookieFile StdErr(&__llvm_libc_stderr_cookie, io_func, nullptr, 0,
+                         _IONBF, File::mode_flags("w"));
+#pragma clang diagnostic pop
+File *stderr = &StdErr;
+
+} // namespace LIBC_NAMESPACE_DECL
+
+extern "C" {
+FILE *stderr = reinterpret_cast<FILE *>(&LIBC_NAMESPACE::StdErr);
+}
diff --git a/libc/src/__support/File/baremetal/stdin.cpp b/libc/src/__support/File/baremetal/stdin.cpp
new file mode 100644
index 0000000000000..569d924749eec
--- /dev/null
+++ b/libc/src/__support/File/baremetal/stdin.cpp
@@ -0,0 +1,34 @@
+//===--- Definition of baremetal stdin ------------------------------------===//
+//
+// 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 "hdr/stdio_macros.h"
+#include "src/__support/File/cookie_file.h"
+#include "src/__support/File/file.h"
+#include "src/__support/OSUtil/baremetal/io.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+cookie_io_functions_t io_func = {.read = __llvm_libc_stdio_read,
+                                 .write = nullptr,
+                                 .seek = nullptr,
+                                 .close = nullptr};
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+// Buffering is implementation defined. Therefore to save RAM, we use no
+// buffering
+static CookieFile StdIn(&__llvm_libc_stdin_cookie, io_func, nullptr, 0, _IONBF,
+                        File::mode_flags("r"));
+#pragma clang diagnostic pop
+File *stdin = &StdIn;
+
+} // namespace LIBC_NAMESPACE_DECL
+
+extern "C" {
+FILE *stdin = reinterpret_cast<FILE *>(&LIBC_NAMESPACE::StdIn);
+}
diff --git a/libc/src/__support/File/baremetal/stdout.cpp b/libc/src/__support/File/baremetal/stdout.cpp
new file mode 100644
index 0000000000000..ee8535c0d7643
--- /dev/null
+++ b/libc/src/__support/File/baremetal/stdout.cpp
@@ -0,0 +1,34 @@
+//===--- Definition of baremetal stdout -----------------------------------===//
+//
+// 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 "hdr/stdio_macros.h"
+#include "src/__support/File/cookie_file.h"
+#include "src/__support/File/file.h"
+#include "src/__support/OSUtil/baremetal/io.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+cookie_io_functions_t io_func = {.read = nullptr,
+                                 .write = __llvm_libc_stdio_write,
+                                 .seek = nullptr,
+                                 .close = nullptr};
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+// Buffering is implementation defined. Therefore to save RAM, we use no
+// buffering
+static CookieFile StdOut(&__llvm_libc_stdout_cookie, io_func, nullptr, 0,
+                         _IONBF, File::mode_flags("w"));
+#pragma clang diagnostic pop
+File *stdout = &StdOut;
+
+} // namespace LIBC_NAMESPACE_DECL
+
+extern "C" {
+FILE *stdout = reinterpret_cast<FILE *>(&LIBC_NAMESPACE::StdOut);
+}
diff --git a/libc/src/__support/File/cookie_file.h b/libc/src/__support/File/cookie_file.h
new file mode 100644
index 0000000000000..b7e2dde23bbb3
--- /dev/null
+++ b/libc/src/__support/File/cookie_file.h
@@ -0,0 +1,75 @@
+//===--- A platform independent cookie file class -------------------------===//
+//
+// 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 "hdr/errno_macros.h"
+#include "hdr/types/cookie_io_functions_t.h"
+#include "hdr/types/off_t.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/File/file.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+class CookieFile : public LIBC_NAMESPACE::File {
+  void *cookie;
+  cookie_io_functions_t ops;
+
+  static FileIOResult cookie_write(File *f, const void *data, size_t size);
+  static FileIOResult cookie_read(File *f, void *data, size_t size);
+  static ErrorOr<off_t> cookie_seek(File *f, off_t offset, int whence);
+  static int cookie_close(File *f);
+
+public:
+  CookieFile(void *c, cookie_io_functions_t cops, uint8_t *buffer,
+             size_t bufsize, int buffer_mode, File::ModeFlags mode)
+      : File(&cookie_write, &cookie_read, &CookieFile::cookie_seek,
+             &cookie_close, buffer, bufsize, buffer_mode,
+             true /* File owns buffer */, mode),
+        cookie(c), ops(cops) {}
+};
+
+FileIOResult CookieFile::cookie_write(File *f, const void *data, size_t size) {
+  auto cookie_file = reinterpret_cast<CookieFile *>(f);
+  if (cookie_file->ops.write == nullptr)
+    return 0;
+  return static_cast<size_t>(cookie_file->ops.write(
+      cookie_file->cookie, reinterpret_cast<const char *>(data), size));
+}
+
+FileIOResult CookieFile::cookie_read(File *f, void *data, size_t size) {
+  auto cookie_file = reinterpret_cast<CookieFile *>(f);
+  if (cookie_file->ops.read == nullptr)
+    return 0;
+  return static_cast<size_t>(cookie_file->ops.read(
+      cookie_file->cookie, reinterpret_cast<char *>(data), size));
+}
+
+ErrorOr<off_t> CookieFile::cookie_seek(File *f, off_t offset, int whence) {
+  auto cookie_file = reinterpret_cast<CookieFile *>(f);
+  if (cookie_file->ops.seek == nullptr) {
+    return Error(EINVAL);
+  }
+  off64_t offset64 = offset;
+  int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence);
+  if (result == 0)
+    return offset64;
+  return -1;
+}
+
+int CookieFile::cookie_close(File *f) {
+  auto cookie_file = reinterpret_cast<CookieFile *>(f);
+  if (cookie_file->ops.close == nullptr)
+    return 0;
+  int retval = cookie_file->ops.close(cookie_file->cookie);
+  if (retval != 0)
+    return retval;
+  delete cookie_file;
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/OSUtil/baremetal/io.cpp b/libc/src/__support/OSUtil/baremetal/io.cpp
index 2a9ef6bfa6579..6b475209d421c 100644
--- a/libc/src/__support/OSUtil/baremetal/io.cpp
+++ b/libc/src/__support/OSUtil/baremetal/io.cpp
@@ -13,45 +13,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// These are intended to be provided by the vendor.
-//
-// The signature of these types and functions intentionally match `fopencookie`
-// which allows the following:
-//
-// ```
-// struct __llvm_libc_stdio_cookie { ... };
-// ...
-// struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
-// cookie_io_functions_t stdin_func = { .read = __llvm_libc_stdio_read };
-// FILE *stdin = fopencookie(&__llvm_libc_stdin_cookie, "r", stdin_func);
-// ...
-// struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
-// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write };
-// FILE *stdout = fopencookie(&__llvm_libc_stdout_cookie, "w", stdout_func);
-// ...
-// struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
-// cookie_io_functions_t stderr_func = { .write = __llvm_libc_stdio_write };
-// FILE *stderr = fopencookie(&__llvm_libc_stderr_cookie, "w", stderr_func);
-// ```
-//
-// At the same time, implementation of functions like `printf` and `scanf` can
-// use `__llvm_libc_stdio_read` and `__llvm_libc_stdio_write` directly to avoid
-// the extra indirection.
-//
-// All three symbols `__llvm_libc_stdin_cookie`, `__llvm_libc_stdout_cookie`,
-// and `__llvm_libc_stderr_cookie` must be provided, even if they don't point
-// at anything.
-
-struct __llvm_libc_stdio_cookie;
-
-extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
-extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
-extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
-
-extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size);
-extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf,
-                                           size_t size);
-
 ssize_t read_from_stdin(char *buf, size_t size) {
   return __llvm_libc_stdio_read(static_cast<void *>(&__llvm_libc_stdin_cookie),
                                 buf, size);
diff --git a/libc/src/__support/OSUtil/baremetal/io.h b/libc/src/__support/OSUtil/baremetal/io.h
index aed34ec7e62e3..b6c5ffb21c1a8 100644
--- a/libc/src/__support/OSUtil/baremetal/io.h
+++ b/libc/src/__support/OSUtil/baremetal/io.h
@@ -14,8 +14,46 @@
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/macros/config.h"
 
+// These are intended to be provided by the vendor.
+//
+// The signature of these types and functions intentionally match `fopencookie`
+// which allows the following:
+//
+// ```
+// struct __llvm_libc_stdio_cookie { ... };
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
+// cookie_io_functions_t stdin_func = { .read = __llvm_libc_stdio_read };
+// FILE *stdin = fopencookie(&__llvm_libc_stdin_cookie, "r", stdin_func);
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
+// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write };
+// FILE *stdout = fopencookie(&__llvm_libc_stdout_cookie, "w", stdout_func);
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
+// cookie_io_functions_t stderr_func = { .write = __llvm_libc_stdio_write };
+// FILE *stderr = fopencookie(&__llvm_libc_stderr_cookie, "w", stderr_func);
+// ```
+//
+// At the same time, implementation of functions like `printf` and `scanf` can
+// use `__llvm_libc_stdio_read` and `__llvm_libc_stdio_write` directly to avoid
+// the extra indirection.
+//
+// All three symbols `__llvm_libc_stdin_cookie`, `__llvm_libc_stdout_cookie`,
+// and `__llvm_libc_stderr_cookie` must be provided, even if they don't point
+// at anything.
 namespace LIBC_NAMESPACE_DECL {
 
+struct __llvm_libc_stdio_cookie;
+
+extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
+extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
+extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
+
+extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size);
+extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf,
+                                           size_t size);
+
 ssize_t read_from_stdin(char *buf, size_t size);
 void write_to_stderr(cpp::string_view msg);
 void write_to_stdout(cpp::string_view msg);
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index b0a6ef1e291b5..273619ae2a570 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -63,6 +63,7 @@ add_entrypoint_object(
     libc.hdr.types.cookie_io_functions_t
     libc.hdr.types.FILE
     libc.src.__support.CPP.new
+    libc.src.__support.File.cookie_file
     libc.src.__support.File.file
 )
 
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt
index 548938f885c94..65089274050bd 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -1,15 +1,3 @@
-add_entrypoint_object(
-  getchar
-  SRCS
-    getchar.cpp
-  HDRS
-    ../getchar.h
-  DEPENDS
-    libc.hdr.stdio_macros
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.CPP.string_view
-)
-
 add_entrypoint_object(
   remove
   SRCS
@@ -19,87 +7,3 @@ add_entrypoint_object(
   DEPENDS
     libc.include.stdio
 )
-
-add_entrypoint_object(
-  printf
-  SRCS
-    printf.cpp
-  HDRS
-    ../printf.h
-  DEPENDS
-    libc.src.stdio.printf_core.printf_main
-    libc.src.stdio.printf_core.writer
-    libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-)
-
-add_entrypoint_object(
-  putchar
-  SRCS
-    putchar.cpp
-  HDRS
-    ../putchar.h
-  DEPENDS
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.CPP.string_view
-)
-
-add_entrypoint_object(
-  puts
-  SRCS
-    puts.cpp
-  HDRS
-    ../puts.h
-  DEPENDS
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.CPP.string_view
-)
-
-add_header_library(
-  scanf_internal
-  HDRS
-    scanf_internal.h
-  DEPENDS
-    libc.src.stdio.scanf_core.reader
-    libc.src.__support.OSUtil.osutil
-)
-
-add_entrypoint_object(
-  scanf
-  SRCS
-    scanf.cpp
-  HDRS
-    ../scanf.h
-  DEPENDS
-    .scanf_internal
-    libc.include.inttypes
-    libc.src.stdio.scanf_core.scanf_main
-    libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-)
-
-add_entrypoint_object(
-  vprintf
-  SRCS
-    vprintf.cpp
-  HDRS
-    ../vprintf.h
-  DEPENDS
-    libc.src.stdio.printf_core.printf_main
-    libc.src.stdio.printf_core.writer
-    libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-)
-
-add_entrypoint_object(
-  vscanf
-  SRCS
-    vscanf.cpp
-  HDRS
-    ../vscanf.h
-  DEPENDS
-    .scanf_internal
-    libc.src.stdio.scanf_core.scanf_main
-    libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-)
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
deleted file mode 100644
index 7253c6549a4e4..0000000000000
--- a/libc/src/stdio/baremetal/printf.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===-- Implementation of printf for baremetal ------------------*- C++ -*-===//
-//
-// 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/printf.h"
-#include "src/__support/OSUtil/io.h"
-#include "src/__support/arg_list.h"
-#include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/printf_main.h"
-#include "src/stdio/printf_core/writer.h"
-
-#include <stdarg.h>
-#include <stddef.h>
-
-namespace LIBC_NAMESPACE_DECL {
-
-namespace {
-
-LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) {
-  write_to_stdout(new_str);
-  return printf_core::WRITE_OK;
-}
-
-} // namespace
-
-LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
-  va_list vlist;
-  va_start(vlist, format);
-  internal::ArgList args(vlist); // This holder class allows for easier copying
-                                 // and pointer semantics, as well as handling
-                                 // destruction automatically.
-  va_end(vlist);
-  static constexpr size_t BUFF_SIZE = 1024;
-  char buffer[BUFF_SIZE];
-
-  printf_core::WriteBuffer<printf_core::WriteMode::FLUSH_TO_STREAM> wb(
-      buffer, BUFF_SIZE, &stdout_write_hook, nullptr);
-  printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
-
-  int retval = printf_core::printf_main(&writer, format, args);
-
-  int flushval = wb.overflow_write("");
-  if (flushval != printf_core::WRITE_OK)
-    retval = flushval;
-
-  return retval;
-}
-
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/putchar.cpp b/libc/src/stdio/baremetal/putchar.cpp
deleted file mode 100644
index ac21e6e783b01..0000000000000
--- a/libc/src/stdio/baremetal/putchar.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-//===-- Baremetal Implementation of putchar -------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://l...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Aug 14, 2025

@llvm/pr-subscribers-libc

Author: William Huynh (saturn691)

Changes

This is a follow up to the RFC here:
https://discourse.llvm.org/t/rfc-implementation-of-stdio-on-baremetal/86944

Currently, this is very rough and this PR is only for feedback. This provides the stdout/stderr/stdin symbols (which now don't have to provided by the user). This allows the user to have access to all functions, currently I've only tested fprintf but in theory everything that works in the generic folder should work in the baremetal configuration.

The most important part of the implementation is:

  • where we want to put std*. Here it is a part of libc.a
  • if we want it here, what should we set it to? I have aliased all the streams to a non-buffered stream, which does NOT require flushing. It is based on the CookieFile that already existed

Patch is 26.72 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/152748.diff

18 Files Affected:

  • (modified) libc/config/baremetal/aarch64/entrypoints.txt (+2)
  • (modified) libc/config/baremetal/arm/entrypoints.txt (+2)
  • (modified) libc/config/baremetal/riscv/entrypoints.txt (+2)
  • (modified) libc/src/__support/File/CMakeLists.txt (+17-1)
  • (added) libc/src/__support/File/baremetal/CMakeLists.txt (+32)
  • (added) libc/src/__support/File/baremetal/stderr.cpp (+34)
  • (added) libc/src/__support/File/baremetal/stdin.cpp (+34)
  • (added) libc/src/__support/File/baremetal/stdout.cpp (+34)
  • (added) libc/src/__support/File/cookie_file.h (+75)
  • (modified) libc/src/__support/OSUtil/baremetal/io.cpp (-39)
  • (modified) libc/src/__support/OSUtil/baremetal/io.h (+38)
  • (modified) libc/src/stdio/CMakeLists.txt (+1)
  • (modified) libc/src/stdio/baremetal/CMakeLists.txt (-96)
  • (removed) libc/src/stdio/baremetal/printf.cpp (-54)
  • (removed) libc/src/stdio/baremetal/putchar.cpp (-24)
  • (removed) libc/src/stdio/baremetal/puts.cpp (-26)
  • (removed) libc/src/stdio/baremetal/vprintf.cpp (-52)
  • (modified) libc/src/stdio/fopencookie.cpp (+4-65)
diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt
index e24e2b9e2a7bb..787fa5b6eb519 100644
--- a/libc/config/baremetal/aarch64/entrypoints.txt
+++ b/libc/config/baremetal/aarch64/entrypoints.txt
@@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # stdio.h entrypoints
     libc.src.stdio.asprintf
+    libc.src.stdio.fopencookie
+    libc.src.stdio.fprintf
     libc.src.stdio.getchar
     libc.src.stdio.printf
     libc.src.stdio.putchar
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 44e9c3edac353..4b19b4267e20d 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # stdio.h entrypoints
     libc.src.stdio.asprintf
+    libc.src.stdio.fopencookie
+    libc.src.stdio.fprintf
     libc.src.stdio.getchar
     libc.src.stdio.printf
     libc.src.stdio.putchar
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index 29cf322a2e33f..7198dab044532 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -124,6 +124,8 @@ set(TARGET_LIBC_ENTRYPOINTS
 
     # stdio.h entrypoints
     libc.src.stdio.asprintf
+    libc.src.stdio.fopencookie
+    libc.src.stdio.fprintf
     libc.src.stdio.getchar
     libc.src.stdio.printf
     libc.src.stdio.putchar
diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt
index 253243ff40ded..bcc6cef2163a1 100644
--- a/libc/src/__support/File/CMakeLists.txt
+++ b/libc/src/__support/File/CMakeLists.txt
@@ -22,6 +22,18 @@ add_object_library(
     libc.src.__support.error_or
 )
 
+add_header_library(
+  cookie_file
+  HDRS
+    cookie_file.h
+  DEPENDS
+    .file
+    libc.hdr.errno_macros
+    libc.hdr.types.cookie_io_functions_t
+    libc.hdr.types.off_t
+    libc.src.__support.CPP.new
+)
+
 add_object_library(
   dir
   SRCS
@@ -41,7 +53,11 @@ endif()
 
 add_subdirectory(${LIBC_TARGET_OS})
 
-set(target_file libc.src.__support.File.${LIBC_TARGET_OS}.file)
+if(LIBC_TARGET_OS_IS_BAREMETAL)
+  set(target_file libc.src.__support.File.cookie_file)
+else()
+  set(target_file libc.src.__support.File.${LIBC_TARGET_OS}.file)
+endif()
 set(target_stdout libc.src.__support.File.${LIBC_TARGET_OS}.stdout)
 set(target_stderr libc.src.__support.File.${LIBC_TARGET_OS}.stderr)
 set(target_stdin libc.src.__support.File.${LIBC_TARGET_OS}.stdin)
diff --git a/libc/src/__support/File/baremetal/CMakeLists.txt b/libc/src/__support/File/baremetal/CMakeLists.txt
new file mode 100644
index 0000000000000..a789948368f02
--- /dev/null
+++ b/libc/src/__support/File/baremetal/CMakeLists.txt
@@ -0,0 +1,32 @@
+add_object_library(
+  stdout
+  SRCS
+    stdout.cpp
+  DEPENDS
+    libc.hdr.stdio_macros
+    libc.src.__support.File.cookie_file
+    libc.src.__support.File.file
+    libc.src.__support.OSUtil.osutil
+)
+
+add_object_library(
+  stdin
+  SRCS
+    stdin.cpp
+  DEPENDS
+    libc.hdr.stdio_macros
+    libc.src.__support.File.cookie_file
+    libc.src.__support.File.file
+    libc.src.__support.OSUtil.osutil
+)
+
+add_object_library(
+  stderr
+  SRCS
+    stderr.cpp
+  DEPENDS
+    libc.hdr.stdio_macros
+    libc.src.__support.File.cookie_file
+    libc.src.__support.File.file
+    libc.src.__support.OSUtil.osutil
+)
diff --git a/libc/src/__support/File/baremetal/stderr.cpp b/libc/src/__support/File/baremetal/stderr.cpp
new file mode 100644
index 0000000000000..0a67771f034aa
--- /dev/null
+++ b/libc/src/__support/File/baremetal/stderr.cpp
@@ -0,0 +1,34 @@
+//===--- Definition of baremetal stderr -----------------------------------===//
+//
+// 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 "hdr/stdio_macros.h"
+#include "src/__support/File/cookie_file.h"
+#include "src/__support/File/file.h"
+#include "src/__support/OSUtil/baremetal/io.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+cookie_io_functions_t io_func = {.read = nullptr,
+                                 .write = __llvm_libc_stdio_write,
+                                 .seek = nullptr,
+                                 .close = nullptr};
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+// Buffering is implementation defined. Therefore to save RAM, we use no
+// buffering
+static CookieFile StdErr(&__llvm_libc_stderr_cookie, io_func, nullptr, 0,
+                         _IONBF, File::mode_flags("w"));
+#pragma clang diagnostic pop
+File *stderr = &StdErr;
+
+} // namespace LIBC_NAMESPACE_DECL
+
+extern "C" {
+FILE *stderr = reinterpret_cast<FILE *>(&LIBC_NAMESPACE::StdErr);
+}
diff --git a/libc/src/__support/File/baremetal/stdin.cpp b/libc/src/__support/File/baremetal/stdin.cpp
new file mode 100644
index 0000000000000..569d924749eec
--- /dev/null
+++ b/libc/src/__support/File/baremetal/stdin.cpp
@@ -0,0 +1,34 @@
+//===--- Definition of baremetal stdin ------------------------------------===//
+//
+// 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 "hdr/stdio_macros.h"
+#include "src/__support/File/cookie_file.h"
+#include "src/__support/File/file.h"
+#include "src/__support/OSUtil/baremetal/io.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+cookie_io_functions_t io_func = {.read = __llvm_libc_stdio_read,
+                                 .write = nullptr,
+                                 .seek = nullptr,
+                                 .close = nullptr};
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+// Buffering is implementation defined. Therefore to save RAM, we use no
+// buffering
+static CookieFile StdIn(&__llvm_libc_stdin_cookie, io_func, nullptr, 0, _IONBF,
+                        File::mode_flags("r"));
+#pragma clang diagnostic pop
+File *stdin = &StdIn;
+
+} // namespace LIBC_NAMESPACE_DECL
+
+extern "C" {
+FILE *stdin = reinterpret_cast<FILE *>(&LIBC_NAMESPACE::StdIn);
+}
diff --git a/libc/src/__support/File/baremetal/stdout.cpp b/libc/src/__support/File/baremetal/stdout.cpp
new file mode 100644
index 0000000000000..ee8535c0d7643
--- /dev/null
+++ b/libc/src/__support/File/baremetal/stdout.cpp
@@ -0,0 +1,34 @@
+//===--- Definition of baremetal stdout -----------------------------------===//
+//
+// 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 "hdr/stdio_macros.h"
+#include "src/__support/File/cookie_file.h"
+#include "src/__support/File/file.h"
+#include "src/__support/OSUtil/baremetal/io.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+cookie_io_functions_t io_func = {.read = nullptr,
+                                 .write = __llvm_libc_stdio_write,
+                                 .seek = nullptr,
+                                 .close = nullptr};
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+// Buffering is implementation defined. Therefore to save RAM, we use no
+// buffering
+static CookieFile StdOut(&__llvm_libc_stdout_cookie, io_func, nullptr, 0,
+                         _IONBF, File::mode_flags("w"));
+#pragma clang diagnostic pop
+File *stdout = &StdOut;
+
+} // namespace LIBC_NAMESPACE_DECL
+
+extern "C" {
+FILE *stdout = reinterpret_cast<FILE *>(&LIBC_NAMESPACE::StdOut);
+}
diff --git a/libc/src/__support/File/cookie_file.h b/libc/src/__support/File/cookie_file.h
new file mode 100644
index 0000000000000..b7e2dde23bbb3
--- /dev/null
+++ b/libc/src/__support/File/cookie_file.h
@@ -0,0 +1,75 @@
+//===--- A platform independent cookie file class -------------------------===//
+//
+// 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 "hdr/errno_macros.h"
+#include "hdr/types/cookie_io_functions_t.h"
+#include "hdr/types/off_t.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/File/file.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+class CookieFile : public LIBC_NAMESPACE::File {
+  void *cookie;
+  cookie_io_functions_t ops;
+
+  static FileIOResult cookie_write(File *f, const void *data, size_t size);
+  static FileIOResult cookie_read(File *f, void *data, size_t size);
+  static ErrorOr<off_t> cookie_seek(File *f, off_t offset, int whence);
+  static int cookie_close(File *f);
+
+public:
+  CookieFile(void *c, cookie_io_functions_t cops, uint8_t *buffer,
+             size_t bufsize, int buffer_mode, File::ModeFlags mode)
+      : File(&cookie_write, &cookie_read, &CookieFile::cookie_seek,
+             &cookie_close, buffer, bufsize, buffer_mode,
+             true /* File owns buffer */, mode),
+        cookie(c), ops(cops) {}
+};
+
+FileIOResult CookieFile::cookie_write(File *f, const void *data, size_t size) {
+  auto cookie_file = reinterpret_cast<CookieFile *>(f);
+  if (cookie_file->ops.write == nullptr)
+    return 0;
+  return static_cast<size_t>(cookie_file->ops.write(
+      cookie_file->cookie, reinterpret_cast<const char *>(data), size));
+}
+
+FileIOResult CookieFile::cookie_read(File *f, void *data, size_t size) {
+  auto cookie_file = reinterpret_cast<CookieFile *>(f);
+  if (cookie_file->ops.read == nullptr)
+    return 0;
+  return static_cast<size_t>(cookie_file->ops.read(
+      cookie_file->cookie, reinterpret_cast<char *>(data), size));
+}
+
+ErrorOr<off_t> CookieFile::cookie_seek(File *f, off_t offset, int whence) {
+  auto cookie_file = reinterpret_cast<CookieFile *>(f);
+  if (cookie_file->ops.seek == nullptr) {
+    return Error(EINVAL);
+  }
+  off64_t offset64 = offset;
+  int result = cookie_file->ops.seek(cookie_file->cookie, &offset64, whence);
+  if (result == 0)
+    return offset64;
+  return -1;
+}
+
+int CookieFile::cookie_close(File *f) {
+  auto cookie_file = reinterpret_cast<CookieFile *>(f);
+  if (cookie_file->ops.close == nullptr)
+    return 0;
+  int retval = cookie_file->ops.close(cookie_file->cookie);
+  if (retval != 0)
+    return retval;
+  delete cookie_file;
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/__support/OSUtil/baremetal/io.cpp b/libc/src/__support/OSUtil/baremetal/io.cpp
index 2a9ef6bfa6579..6b475209d421c 100644
--- a/libc/src/__support/OSUtil/baremetal/io.cpp
+++ b/libc/src/__support/OSUtil/baremetal/io.cpp
@@ -13,45 +13,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-// These are intended to be provided by the vendor.
-//
-// The signature of these types and functions intentionally match `fopencookie`
-// which allows the following:
-//
-// ```
-// struct __llvm_libc_stdio_cookie { ... };
-// ...
-// struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
-// cookie_io_functions_t stdin_func = { .read = __llvm_libc_stdio_read };
-// FILE *stdin = fopencookie(&__llvm_libc_stdin_cookie, "r", stdin_func);
-// ...
-// struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
-// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write };
-// FILE *stdout = fopencookie(&__llvm_libc_stdout_cookie, "w", stdout_func);
-// ...
-// struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
-// cookie_io_functions_t stderr_func = { .write = __llvm_libc_stdio_write };
-// FILE *stderr = fopencookie(&__llvm_libc_stderr_cookie, "w", stderr_func);
-// ```
-//
-// At the same time, implementation of functions like `printf` and `scanf` can
-// use `__llvm_libc_stdio_read` and `__llvm_libc_stdio_write` directly to avoid
-// the extra indirection.
-//
-// All three symbols `__llvm_libc_stdin_cookie`, `__llvm_libc_stdout_cookie`,
-// and `__llvm_libc_stderr_cookie` must be provided, even if they don't point
-// at anything.
-
-struct __llvm_libc_stdio_cookie;
-
-extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
-extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
-extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
-
-extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size);
-extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf,
-                                           size_t size);
-
 ssize_t read_from_stdin(char *buf, size_t size) {
   return __llvm_libc_stdio_read(static_cast<void *>(&__llvm_libc_stdin_cookie),
                                 buf, size);
diff --git a/libc/src/__support/OSUtil/baremetal/io.h b/libc/src/__support/OSUtil/baremetal/io.h
index aed34ec7e62e3..b6c5ffb21c1a8 100644
--- a/libc/src/__support/OSUtil/baremetal/io.h
+++ b/libc/src/__support/OSUtil/baremetal/io.h
@@ -14,8 +14,46 @@
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/macros/config.h"
 
+// These are intended to be provided by the vendor.
+//
+// The signature of these types and functions intentionally match `fopencookie`
+// which allows the following:
+//
+// ```
+// struct __llvm_libc_stdio_cookie { ... };
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
+// cookie_io_functions_t stdin_func = { .read = __llvm_libc_stdio_read };
+// FILE *stdin = fopencookie(&__llvm_libc_stdin_cookie, "r", stdin_func);
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
+// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write };
+// FILE *stdout = fopencookie(&__llvm_libc_stdout_cookie, "w", stdout_func);
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
+// cookie_io_functions_t stderr_func = { .write = __llvm_libc_stdio_write };
+// FILE *stderr = fopencookie(&__llvm_libc_stderr_cookie, "w", stderr_func);
+// ```
+//
+// At the same time, implementation of functions like `printf` and `scanf` can
+// use `__llvm_libc_stdio_read` and `__llvm_libc_stdio_write` directly to avoid
+// the extra indirection.
+//
+// All three symbols `__llvm_libc_stdin_cookie`, `__llvm_libc_stdout_cookie`,
+// and `__llvm_libc_stderr_cookie` must be provided, even if they don't point
+// at anything.
 namespace LIBC_NAMESPACE_DECL {
 
+struct __llvm_libc_stdio_cookie;
+
+extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
+extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
+extern "C" struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
+
+extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size);
+extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf,
+                                           size_t size);
+
 ssize_t read_from_stdin(char *buf, size_t size);
 void write_to_stderr(cpp::string_view msg);
 void write_to_stdout(cpp::string_view msg);
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index b0a6ef1e291b5..273619ae2a570 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -63,6 +63,7 @@ add_entrypoint_object(
     libc.hdr.types.cookie_io_functions_t
     libc.hdr.types.FILE
     libc.src.__support.CPP.new
+    libc.src.__support.File.cookie_file
     libc.src.__support.File.file
 )
 
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt
index 548938f885c94..65089274050bd 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -1,15 +1,3 @@
-add_entrypoint_object(
-  getchar
-  SRCS
-    getchar.cpp
-  HDRS
-    ../getchar.h
-  DEPENDS
-    libc.hdr.stdio_macros
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.CPP.string_view
-)
-
 add_entrypoint_object(
   remove
   SRCS
@@ -19,87 +7,3 @@ add_entrypoint_object(
   DEPENDS
     libc.include.stdio
 )
-
-add_entrypoint_object(
-  printf
-  SRCS
-    printf.cpp
-  HDRS
-    ../printf.h
-  DEPENDS
-    libc.src.stdio.printf_core.printf_main
-    libc.src.stdio.printf_core.writer
-    libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-)
-
-add_entrypoint_object(
-  putchar
-  SRCS
-    putchar.cpp
-  HDRS
-    ../putchar.h
-  DEPENDS
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.CPP.string_view
-)
-
-add_entrypoint_object(
-  puts
-  SRCS
-    puts.cpp
-  HDRS
-    ../puts.h
-  DEPENDS
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.CPP.string_view
-)
-
-add_header_library(
-  scanf_internal
-  HDRS
-    scanf_internal.h
-  DEPENDS
-    libc.src.stdio.scanf_core.reader
-    libc.src.__support.OSUtil.osutil
-)
-
-add_entrypoint_object(
-  scanf
-  SRCS
-    scanf.cpp
-  HDRS
-    ../scanf.h
-  DEPENDS
-    .scanf_internal
-    libc.include.inttypes
-    libc.src.stdio.scanf_core.scanf_main
-    libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-)
-
-add_entrypoint_object(
-  vprintf
-  SRCS
-    vprintf.cpp
-  HDRS
-    ../vprintf.h
-  DEPENDS
-    libc.src.stdio.printf_core.printf_main
-    libc.src.stdio.printf_core.writer
-    libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-)
-
-add_entrypoint_object(
-  vscanf
-  SRCS
-    vscanf.cpp
-  HDRS
-    ../vscanf.h
-  DEPENDS
-    .scanf_internal
-    libc.src.stdio.scanf_core.scanf_main
-    libc.src.__support.arg_list
-    libc.src.__support.OSUtil.osutil
-)
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
deleted file mode 100644
index 7253c6549a4e4..0000000000000
--- a/libc/src/stdio/baremetal/printf.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//===-- Implementation of printf for baremetal ------------------*- C++ -*-===//
-//
-// 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/printf.h"
-#include "src/__support/OSUtil/io.h"
-#include "src/__support/arg_list.h"
-#include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/printf_main.h"
-#include "src/stdio/printf_core/writer.h"
-
-#include <stdarg.h>
-#include <stddef.h>
-
-namespace LIBC_NAMESPACE_DECL {
-
-namespace {
-
-LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) {
-  write_to_stdout(new_str);
-  return printf_core::WRITE_OK;
-}
-
-} // namespace
-
-LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
-  va_list vlist;
-  va_start(vlist, format);
-  internal::ArgList args(vlist); // This holder class allows for easier copying
-                                 // and pointer semantics, as well as handling
-                                 // destruction automatically.
-  va_end(vlist);
-  static constexpr size_t BUFF_SIZE = 1024;
-  char buffer[BUFF_SIZE];
-
-  printf_core::WriteBuffer<printf_core::WriteMode::FLUSH_TO_STREAM> wb(
-      buffer, BUFF_SIZE, &stdout_write_hook, nullptr);
-  printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
-
-  int retval = printf_core::printf_main(&writer, format, args);
-
-  int flushval = wb.overflow_write("");
-  if (flushval != printf_core::WRITE_OK)
-    retval = flushval;
-
-  return retval;
-}
-
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/putchar.cpp b/libc/src/stdio/baremetal/putchar.cpp
deleted file mode 100644
index ac21e6e783b01..0000000000000
--- a/libc/src/stdio/baremetal/putchar.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-//===-- Baremetal Implementation of putchar -------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://l...
[truncated]

@saturn691
Copy link
Contributor Author

@michaelrj-google The approach that we've taken at Arm is that "premature optimisation is the root of all evil". For now, we would like to get our benchmarks working which require std::cout so once we get to that stage, we can start adding back smaller versions of these functions.

Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that premature optimization is a bad idea, but I think @petrhosek might be using this in production currently. I'd like to confirm if that's a concern before potentially breaking existing uses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants