[libc-commits] [libc] [libc] Allow using sscanf() and vsscanf() on baremetal targets. (PR #130527)
Jesse D via libc-commits
libc-commits at lists.llvm.org
Sun Mar 9 16:49:59 PDT 2025
https://github.com/jdeguire created https://github.com/llvm/llvm-project/pull/130527
In addition to the normal "cookie" definitions a baremetal user needs to provide to use printf(), a user also needs to add `FILE *stdin;` somewhere in their source. I have not updated the default baremetal entrypoints, so a user will currently need to supply their own to add sscanf() and vsscanf(). This might also work for regular sscanf(), but I have not yet tried it.
This updates LLVMCCompileOptionRules.cmake to add a macro the libc build can use to determine if it is doing a baremetal build. This also updates the baremetal getchar() function because it looked like it would return an uninitialized value if __llvm_libc_stdio_read() returned 0. The baremetal build does not provide ungetc(), so the baremetal Reader class tracks the would-be ungot character itself. From what I could see in the code, tracking only one character is sufficient.
>From e9b681a59e47dc5ee8a70ef1415dbac5173b16f5 Mon Sep 17 00:00:00 2001
From: Jesse DeGuire <jesse.a.deguire at gmail.com>
Date: Sun, 9 Mar 2025 18:39:27 -0500
Subject: [PATCH] [libc] Allow using sscanf() and vsscanf() on baremetal
targets.
In addition to the normal "cookie" definitions a baremetal user needs to provide to use printf(), a user also needs to add "FILE *stdin;" somehere in their source. I have not updated the default baremetal entrypoints, so a user will currently need to supply their own to add sscanf() and vsscanf(). This might also work for regular sscanf(), but I have not yet tried it.
This updates LLVMCCompileOptionRules.cmake to add a macro the libc build can use to determine if it is doing a baremetal build. This also updates the baremetal getchar() function because it looked like it would return an uninitialized value if __llvm_libc_stdio_read() returned 0. The baremetal build does not provide ungetc(), so the baremetal Reader class tracks the would-be ungot character itself. From what I could see in the code, tracking only one character is sufficient.
---
.../modules/LLVMLibCCompileOptionRules.cmake | 3 ++
libc/src/stdio/baremetal/getchar.cpp | 2 +-
libc/src/stdio/scanf_core/CMakeLists.txt | 18 ++++----
libc/src/stdio/scanf_core/reader.h | 42 ++++++++++++++++++-
4 files changed, 56 insertions(+), 9 deletions(-)
diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
index 0facb0b9be0c1..270a74a6949c5 100644
--- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
+++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
@@ -119,6 +119,9 @@ function(_get_common_compile_options output_var flags)
list(APPEND compile_options "-fpie")
if(LLVM_LIBC_FULL_BUILD)
+ if(LIBC_TARGET_OS_IS_BAREMETAL)
+ list(APPEND compile_options "-DLIBC_TARGET_OS_IS_BAREMETAL")
+ endif()
# Only add -ffreestanding flag in non-GPU full build mode.
if(NOT LIBC_TARGET_OS_IS_GPU)
list(APPEND compile_options "-ffreestanding")
diff --git a/libc/src/stdio/baremetal/getchar.cpp b/libc/src/stdio/baremetal/getchar.cpp
index 0a78a1ff8adf1..8fb7bc32537e7 100644
--- a/libc/src/stdio/baremetal/getchar.cpp
+++ b/libc/src/stdio/baremetal/getchar.cpp
@@ -17,7 +17,7 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, getchar, ()) {
char buf[1];
auto result = read_from_stdin(buf, sizeof(buf));
- if (result < 0)
+ if (result <= 0)
return EOF;
return buf[0];
}
diff --git a/libc/src/stdio/scanf_core/CMakeLists.txt b/libc/src/stdio/scanf_core/CMakeLists.txt
index 014413ccaa8da..79ae87bf3dd75 100644
--- a/libc/src/stdio/scanf_core/CMakeLists.txt
+++ b/libc/src/stdio/scanf_core/CMakeLists.txt
@@ -16,6 +16,10 @@ if(LIBC_TARGET_OS_IS_GPU)
libc.src.stdio.ungetc
libc.src.stdio.ferror
)
+elseif(LIBC_TARGET_OS_IS_BAREMETAL)
+ list(APPEND file_deps
+ libc.src.stdio.getchar
+ )
elseif(LLVM_LIBC_FULL_BUILD)
list(APPEND file_deps
libc.src.__support.File.file
@@ -54,13 +58,6 @@ add_header_library(
libc.src.__support.CPP.string_view
)
-if(NOT(TARGET libc.src.__support.File.file) AND LLVM_LIBC_FULL_BUILD AND
- (NOT LIBC_TARGET_OS_IS_GPU))
- # Not all platforms have a file implementation. If file is unvailable, and a
- # full build is requested, then we must skip all file based scanf sections.
- return()
-endif()
-
add_object_library(
scanf_main
SRCS
@@ -117,6 +114,13 @@ add_object_library(
${use_system_file}
)
+if(NOT(TARGET libc.src.__support.File.file) AND LLVM_LIBC_FULL_BUILD AND
+ (NOT LIBC_TARGET_OS_IS_GPU))
+ # Not all platforms have a file implementation. If file is unvailable, and a
+ # full build is requested, then we must skip all file based scanf sections.
+ return()
+endif()
+
#TODO: condense the file-related code as possible.
add_header_library(
vfscanf_internal
diff --git a/libc/src/stdio/scanf_core/reader.h b/libc/src/stdio/scanf_core/reader.h
index 1f8ec9695a314..281dabe7faf01 100644
--- a/libc/src/stdio/scanf_core/reader.h
+++ b/libc/src/stdio/scanf_core/reader.h
@@ -11,13 +11,16 @@
#include "hdr/types/FILE.h"
-#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
+#if !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) && !defined(LIBC_TARGET_OS_IS_BAREMETAL)
#include "src/__support/File/file.h"
#endif
#if defined(LIBC_TARGET_ARCH_IS_GPU)
#include "src/stdio/getc.h"
#include "src/stdio/ungetc.h"
+#elif defined(LIBC_TARGET_OS_IS_BAREMETAL)
+#include "src/stdio/getchar.h"
+#include "hdr/stdio_macros.h" // for EOF.
#endif
#include "src/__support/macros/attributes.h" // For LIBC_INLINE
@@ -47,6 +50,28 @@ LIBC_INLINE void ungetc(int c, void *f) {
LIBC_NAMESPACE::ungetc(c, reinterpret_cast<::FILE *>(f));
}
+#elif defined(LIBC_TARGET_OS_IS_BAREMETAL)
+// The baremetal build does not currently support file operations, but it does
+// declare pointers to the stanard FILE streams. The user just needs to declare
+// "FILE *stdin;" somwhere. That is not much to ask since a user already has to
+// define cookie structures for stdio.
+extern "C" FILE *stdin;
+
+LIBC_INLINE int getc(void *f) {
+ if (f == stdin) {
+ int c = getchar();
+ if (EOF == c)
+ c = '\0';
+
+ return c;
+ }
+
+ return '\0';
+}
+
+// Baremetal does not currently provide an ungetc(), so the Reader will need to
+// handle that for now.
+
#elif !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
LIBC_INLINE int getc(void *f) {
@@ -89,6 +114,10 @@ class Reader {
ReadBuffer *rb;
void *input_stream = nullptr;
size_t cur_chars_read = 0;
+#if defined(LIBC_TARGET_OS_IS_BAREMETAL)
+ // Baremetal does not provide an ungetc(), so track that ourselves for now.
+ int unget_char = EOF;
+#endif
public:
// TODO: Set buff_len with a proper constant
@@ -107,6 +136,13 @@ class Reader {
++(rb->buff_cur);
return output;
}
+#if defined(LIBC_TARGET_OS_IS_BAREMETAL)
+ if (EOF != unget_char) {
+ char output = (char)unget_char;
+ unget_char = EOF;
+ return output;
+ }
+#endif
// This should reset the buffer if applicable.
return static_cast<char>(reader_internal::getc(input_stream));
}
@@ -123,7 +159,11 @@ class Reader {
--(rb->buff_cur);
return;
}
+#if defined(LIBC_TARGET_OS_IS_BAREMETAL)
+ unget_char = c;
+#else
reader_internal::ungetc(static_cast<int>(c), input_stream);
+#endif
}
LIBC_INLINE size_t chars_read() { return cur_chars_read; }
More information about the libc-commits
mailing list