Skip to content

[libc] Add baremetal printf #94078

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

Merged
merged 3 commits into from
Jun 7, 2024

Conversation

michaelrj-google
Copy link
Contributor

For baremetal targets that don't support FILE, this version of printf
just writes directly to a function provided by a vendor. To do this both
printf and vprintf were moved to /generic (vprintf since they need the
same flags and cmake gets funky about setting variables in one file and
reading them in another).

For baremetal targets that don't support FILE, this version of printf
just writes directly to a function provided by a vendor. To do this both
printf and vprintf were moved to /generic (vprintf since they need the
same flags and cmake gets funky about setting variables in one file and
reading them in another).
@llvmbot
Copy link
Member

llvmbot commented May 31, 2024

@llvm/pr-subscribers-libc

Author: Michael Jones (michaelrj-google)

Changes

For baremetal targets that don't support FILE, this version of printf
just writes directly to a function provided by a vendor. To do this both
printf and vprintf were moved to /generic (vprintf since they need the
same flags and cmake gets funky about setting variables in one file and
reading them in another).


Full diff: https://github.com/llvm/llvm-project/pull/94078.diff

6 Files Affected:

  • (modified) libc/src/stdio/CMakeLists.txt (+2-33)
  • (modified) libc/src/stdio/baremetal/CMakeLists.txt (+12)
  • (added) libc/src/stdio/baremetal/printf.cpp (+51)
  • (modified) libc/src/stdio/generic/CMakeLists.txt (+35)
  • (renamed) libc/src/stdio/generic/printf.cpp ()
  • (renamed) libc/src/stdio/generic/vprintf.cpp ()
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 1056f38fc7513..ee48e441d1c59 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -159,29 +159,6 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.writer
 )
 
-list(APPEND printf_deps
-      libc.src.__support.arg_list
-      libc.src.stdio.printf_core.vfprintf_internal
-)
-
-if(LLVM_LIBC_FULL_BUILD)
-  list(APPEND printf_deps
-      libc.src.__support.File.file
-      libc.src.__support.File.platform_file
-      libc.src.__support.File.platform_stdout
-  )
-endif()
-
-add_entrypoint_object(
-  printf
-  SRCS
-    printf.cpp
-  HDRS
-    printf.h
-  DEPENDS
-    ${printf_deps}
-)
-
 add_entrypoint_object(
   fprintf
   SRCS
@@ -215,16 +192,6 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.writer
 )
 
-add_entrypoint_object(
-  vprintf
-  SRCS
-    vprintf.cpp
-  HDRS
-    vprintf.h
-  DEPENDS
-    ${printf_deps}
-)
-
 add_entrypoint_object(
   vfprintf
   SRCS
@@ -286,6 +253,7 @@ add_stdio_entrypoint_object(fwrite)
 add_stdio_entrypoint_object(fputc)
 add_stdio_entrypoint_object(putc)
 add_stdio_entrypoint_object(putchar)
+add_stdio_entrypoint_object(printf)
 add_stdio_entrypoint_object(fgetc)
 add_stdio_entrypoint_object(fgetc_unlocked)
 add_stdio_entrypoint_object(getc)
@@ -297,3 +265,4 @@ add_stdio_entrypoint_object(ungetc)
 add_stdio_entrypoint_object(stdin)
 add_stdio_entrypoint_object(stdout)
 add_stdio_entrypoint_object(stderr)
+add_stdio_entrypoint_object(vprintf)
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt
index 65089274050bd..a1a5ba5faaf3d 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -7,3 +7,15 @@ 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
+)
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
new file mode 100644
index 0000000000000..f14f2c1afb790
--- /dev/null
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -0,0 +1,51 @@
+//===-- 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/arg_list.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>
+
+// This is intended to be provided by the vendor. Name TBD
+extern "C" size_t __llvm_libc_raw_write(const char *s, size_t size);
+
+namespace LIBC_NAMESPACE {
+
+LIBC_INLINE int raw_write_hook(cpp::string_view new_str, void *) {
+  size_t written = __llvm_libc_raw_write(new_str.data(), new_str.size());
+  if (written != new_str.size())
+    return printf_core::FILE_WRITE_ERROR;
+  return printf_core::WRITE_OK;
+}
+
+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);
+  constexpr size_t BUFF_SIZE = 1024;
+  char buffer[BUFF_SIZE];
+
+  printf_core::WriteBuffer wb(buffer, BUFF_SIZE, &raw_write_hook, nullptr);
+  printf_core::Writer 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
diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt
index ae255917adfe3..65f052b841958 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -363,6 +363,41 @@ add_entrypoint_object(
     libc.src.__support.File.platform_file
 )
 
+list(APPEND printf_deps
+      libc.src.__support.arg_list
+      libc.src.stdio.printf_core.vfprintf_internal
+)
+
+if(LLVM_LIBC_FULL_BUILD)
+  list(APPEND printf_deps
+      libc.src.__support.File.file
+      libc.src.__support.File.platform_file
+      libc.src.__support.File.platform_stdout
+  )
+endif()
+
+add_entrypoint_object(
+  printf
+  SRCS
+    printf.cpp
+  HDRS
+    ../printf.h
+  DEPENDS
+    ${printf_deps}
+)
+
+add_entrypoint_object(
+  vprintf
+  SRCS
+    vprintf.cpp
+  HDRS
+    ../vprintf.h
+  DEPENDS
+    ${printf_deps}
+)
+
+message(STATUS "the printf deps are ${printf_deps}")
+
 add_entrypoint_object(
   fgets
   SRCS
diff --git a/libc/src/stdio/printf.cpp b/libc/src/stdio/generic/printf.cpp
similarity index 100%
rename from libc/src/stdio/printf.cpp
rename to libc/src/stdio/generic/printf.cpp
diff --git a/libc/src/stdio/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
similarity index 100%
rename from libc/src/stdio/vprintf.cpp
rename to libc/src/stdio/generic/vprintf.cpp

#include <stdarg.h>

// This is intended to be provided by the vendor. Name TBD
extern "C" size_t __llvm_libc_raw_write(const char *s, size_t size);
Copy link
Contributor

Choose a reason for hiding this comment

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

How is this different from the write_to_stderr hook we already use? I guess the size is different. For the GPU I use FILE as a simple opaque handle basically, don't know if that's relevant here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd say raw_write and write_to_stderr are (in my mind) different in the same way stdout and stderr are. The intent for stdout is for normal output, whereas the intent for stderr is error messages. That being said, I should probably move this to OSUtil so that it's in the same place as write_to_stderr.

As for using FILE as an opaque handle, that probably would work, but the request from @petrhosek was for a printf that only writes to the provided function.

Copy link
Member

Choose a reason for hiding this comment

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

write_to_stderr is a private C++ API, for vendor integration we need an internal C API. So far we only have __llvm_libc_log_write defined in baremetal OSUtil, and this change introduces __llvm_libc_raw_write. It's a question whether this API should be specific only to baremetal target or should be made more generic, but I'm fine starting with just baremetal and generalizing it as needed. It'd be also helpful to define all internal API in a single header that's extensively documented. There are also open questions around naming, versioning, prior art, etc. This is a broader topic that we should probably discuss on Discourse, but I'm fine landing this change in the meantime and refining the API later.

Regarding opaque FILE, for baremetal I think we'll need both. Most baremetal targets have some support for writing out characters, typically over serial. This would typically be used to implement printf (or similar API for logging). Many baremetal targets also support semihosting which allows file system access and I'd like to implement FILE for baremetal through semihosting. We could also use semihosting to implement printf (using SYS_WRITE syscall) but that has a downside of requiring a debugger and so I don't think it should be the default implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

having thought about it some more, I think the best solution might be to add a write_to_stdout that can be used by not just printf but also puts and putchar in future. I'll update this PR with what I currently have and then try making a version with this new design so that we can compare.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I messed around with the design a bit and realized it's out of scope for this patch. I've filed an issue to track this further (#94685), but for now I'm going to land this patch as-is.

@michaelrj-google michaelrj-google merged commit 11d643f into llvm:main Jun 7, 2024
6 checks passed
@michaelrj-google michaelrj-google deleted the libcBaremetalPrintf branch June 7, 2024 18:37
keith added a commit to keith/llvm-project that referenced this pull request Jun 7, 2024
keith added a commit that referenced this pull request Jun 7, 2024
nekoshirro pushed a commit to nekoshirro/Alchemist-LLVM that referenced this pull request Jun 9, 2024
petrhosek added a commit to petrhosek/llvm-project that referenced this pull request Jun 13, 2024
This is similar to baremetal printf that was implemented in llvm#94078.
@HerrCai0907 HerrCai0907 mentioned this pull request Jun 13, 2024
petrhosek added a commit that referenced this pull request Jun 14, 2024
This is similar to baremetal printf that was implemented in #94078.
EthanLuisMcDonough pushed a commit to EthanLuisMcDonough/llvm-project that referenced this pull request Aug 13, 2024
This is similar to baremetal printf that was implemented in llvm#94078.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants