[libc-commits] [libc] [libc] Modular printf option (float only) (PR #147426)
Daniel Thornburgh via libc-commits
libc-commits at lists.llvm.org
Wed Jan 28 14:46:37 PST 2026
https://github.com/mysterymath updated https://github.com/llvm/llvm-project/pull/147426
>From 1d82299331f9a3d4ff3293ea559fcdb6c4f19d33 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 19 Dec 2024 11:57:27 -0800
Subject: [PATCH] [libc] Modular printf option (float only)
This adds LIBC_CONF_PRINTF_MODULAR, which causes floating point support
(later, others) to be weakly linked into the implementation.
__printf_modular becomes the main entry point of the implementaiton, an
printf itself wraps __printf_modular. printf it also contains a
BFD_RELOC_NONE relocation to bring in the float aspect.
See issue #146159 for context.
---
libc/config/config.json | 4 ++
libc/docs/configure.rst | 1 +
libc/include/CMakeLists.txt | 1 +
libc/include/llvm-libc-macros/CMakeLists.txt | 22 ++++++-
.../_LIBC_MODULAR_FORMAT_PRINTF-disable.h | 14 +++++
.../_LIBC_MODULAR_FORMAT_PRINTF.h | 15 +++++
libc/include/stdio.yaml | 16 +++++
libc/src/stdio/CMakeLists.txt | 40 +++++++++++--
libc/src/stdio/asprintf.cpp | 5 ++
libc/src/stdio/asprintf.h | 1 +
libc/src/stdio/asprintf_modular.cpp | 43 ++++++++++++++
libc/src/stdio/baremetal/CMakeLists.txt | 12 +++-
libc/src/stdio/baremetal/printf.cpp | 5 ++
libc/src/stdio/baremetal/printf_modular.cpp | 32 ++++++++++
libc/src/stdio/baremetal/vfprintf.cpp | 5 ++
libc/src/stdio/baremetal/vfprintf_internal.h | 5 +-
libc/src/stdio/baremetal/vprintf.cpp | 5 ++
libc/src/stdio/baremetal/vprintf_modular.cpp | 30 ++++++++++
libc/src/stdio/generic/CMakeLists.txt | 4 ++
libc/src/stdio/gpu/CMakeLists.txt | 4 ++
libc/src/stdio/printf.h | 1 +
libc/src/stdio/printf_core/CMakeLists.txt | 7 ++-
.../stdio/printf_core/float_dec_converter.h | 13 +++++
.../printf_core/float_dec_converter_limited.h | 13 +++++
.../stdio/printf_core/float_hex_converter.h | 6 ++
libc/src/stdio/printf_core/float_impl.cpp | 56 ++++++++++++++++++
libc/src/stdio/printf_core/parser.h | 58 ++++++++++++++-----
libc/src/stdio/printf_core/printf_config.h | 7 +++
libc/src/stdio/printf_core/printf_main.h | 16 ++++-
.../stdio/printf_core/vasprintf_internal.h | 5 +-
.../src/stdio/printf_core/vfprintf_internal.h | 18 ++++--
libc/src/stdio/snprintf.cpp | 5 ++
libc/src/stdio/snprintf.h | 2 +
libc/src/stdio/snprintf_modular.cpp | 54 +++++++++++++++++
libc/src/stdio/sprintf.cpp | 5 ++
libc/src/stdio/sprintf.h | 2 +
libc/src/stdio/sprintf_modular.cpp | 54 +++++++++++++++++
libc/src/stdio/vasprintf.cpp | 5 ++
libc/src/stdio/vasprintf.h | 2 +
libc/src/stdio/vasprintf_modular.cpp | 38 ++++++++++++
libc/src/stdio/vprintf.h | 1 +
libc/src/stdio/vsnprintf.cpp | 5 ++
libc/src/stdio/vsnprintf.h | 2 +
libc/src/stdio/vsnprintf_modular.cpp | 51 ++++++++++++++++
libc/src/stdio/vsprintf.cpp | 5 ++
libc/src/stdio/vsprintf.h | 2 +
libc/src/stdio/vsprintf_modular.cpp | 50 ++++++++++++++++
libc/utils/hdrgen/hdrgen/header.py | 2 +-
.../tests/expected_output/test_header.h | 3 +-
.../tests/expected_output/test_small.json | 1 +
libc/utils/hdrgen/tests/input/merge1.yaml | 1 +
51 files changed, 720 insertions(+), 34 deletions(-)
create mode 100644 libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h
create mode 100644 libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h
create mode 100644 libc/src/stdio/asprintf_modular.cpp
create mode 100644 libc/src/stdio/baremetal/printf_modular.cpp
create mode 100644 libc/src/stdio/baremetal/vprintf_modular.cpp
create mode 100644 libc/src/stdio/printf_core/float_impl.cpp
create mode 100644 libc/src/stdio/snprintf_modular.cpp
create mode 100644 libc/src/stdio/sprintf_modular.cpp
create mode 100644 libc/src/stdio/vasprintf_modular.cpp
create mode 100644 libc/src/stdio/vsnprintf_modular.cpp
create mode 100644 libc/src/stdio/vsprintf_modular.cpp
diff --git a/libc/config/config.json b/libc/config/config.json
index 296d2e539c23d..e598be7ee0479 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -56,6 +56,10 @@
"LIBC_CONF_PRINTF_DISABLE_WIDE": {
"value": false,
"doc": "Disable handling wide characters for printf and friends."
+ },
+ "LIBC_CONF_PRINTF_MODULAR": {
+ "value": false,
+ "doc": "Split printf implementation into modules that can be lazily linked in."
}
},
"scanf": {
diff --git a/libc/docs/configure.rst b/libc/docs/configure.rst
index 68bacfd74fd33..56a9ef4574db9 100644
--- a/libc/docs/configure.rst
+++ b/libc/docs/configure.rst
@@ -49,6 +49,7 @@ to learn about the defaults for your platform and target.
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT``: Use dyadic float for faster and smaller but less accurate printf doubles.
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_FLOAT320``: Use an alternative printf float implementation based on 320-bit floats
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
+ - ``LIBC_CONF_PRINTF_MODULAR``: Split printf implementation into modules that can be lazily linked in.
- ``LIBC_CONF_PRINTF_RUNTIME_DISPATCH``: Use dynamic dispatch for the output mechanism to reduce code size.
* **"pthread" options**
- ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 1bc9c2ca1a51d..f609693ce5ddc 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -359,6 +359,7 @@ add_header_macro(
../libc/include/stdio.yaml
stdio.h
DEPENDS
+ .llvm-libc-macros._LIBC_MODULAR_FORMAT_PRINTF
.llvm-libc-macros.file_seek_macros
.llvm-libc-macros.null_macro
.llvm-libc-macros.stdio_macros
diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt
index b16337cccd58b..120277edcb3a7 100644
--- a/libc/include/llvm-libc-macros/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/CMakeLists.txt
@@ -3,15 +3,19 @@ function(add_macro_header name)
cmake_parse_arguments(
"MACRO_HEADER"
"" # Optional arguments
- "HDR" # Single value arguments
+ "HDR;DEST_HDR" # Single value arguments
"DEPENDS" # Multi-value arguments
${ARGN}
)
+ if (MACRO_HEADER_DEST_HDR)
+ set(dest_header_arg DEST_HDR ${MACRO_HEADER_DEST_HDR})
+ endif()
if(TARGET libc.include.llvm-libc-macros.${LIBC_TARGET_OS}.${name})
add_header(
${name}
HDR
${MACRO_HEADER_HDR}
+ ${dest_header_arg}
DEPENDS
.${LIBC_TARGET_OS}.${name}
${MACRO_HEADER_DEPENDS}
@@ -21,6 +25,7 @@ function(add_macro_header name)
${name}
HDR
${MACRO_HEADER_HDR}
+ ${dest_header_arg}
DEPENDS
${MACRO_HEADER_DEPENDS}
)
@@ -383,3 +388,18 @@ add_macro_header(
sysexits-macros.h
)
+if (LIBC_CONF_MODULAR_FORMAT)
+ add_macro_header(
+ _LIBC_MODULAR_FORMAT_PRINTF
+ HDR
+ _LIBC_MODULAR_FORMAT_PRINTF.h
+ )
+else()
+ add_macro_header(
+ _LIBC_MODULAR_FORMAT_PRINTF
+ HDR
+ _LIBC_MODULAR_FORMAT_PRINTF-disable.h
+ DEST_HDR
+ _LIBC_MODULAR_FORMAT_PRINTF.h
+ )
+endif()
diff --git a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h
new file mode 100644
index 0000000000000..e3238161b3808
--- /dev/null
+++ b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF-disable.h
@@ -0,0 +1,14 @@
+//===-- Definition to disable modular format macro for printf -------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
+#define LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
+
+#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN)
+
+#endif // LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
diff --git a/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h
new file mode 100644
index 0000000000000..918241ab8f2ec
--- /dev/null
+++ b/libc/include/llvm-libc-macros/_LIBC_MODULAR_FORMAT_PRINTF.h
@@ -0,0 +1,15 @@
+//===-- Definition of modular format macro for printf ---------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
+#define LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
+
+#define _LIBC_MODULAR_FORMAT_PRINTF(MODULAR_IMPL_FN) \
+ __attribute__((modular_format(MODULAR_IMPL_FN, "__printf", "float")))
+
+#endif // LLVM_LIBC_MACROS_LIBC_MODULAR_FORMAT_PRINTF_H
diff --git a/libc/include/stdio.yaml b/libc/include/stdio.yaml
index c50b4ecb0bf08..67eeeaf07bdd1 100644
--- a/libc/include/stdio.yaml
+++ b/libc/include/stdio.yaml
@@ -31,6 +31,8 @@ functions:
- type: char **__restrict
- type: const char *__restrict
- type: '...'
+ attributes:
+ - _LIBC_MODULAR_FORMAT_PRINTF(__asprintf_modular)
- name: clearerr
standards:
- stdc
@@ -276,6 +278,8 @@ functions:
arguments:
- type: const char *__restrict
- type: '...'
+ attributes:
+ - _LIBC_MODULAR_FORMAT_PRINTF(__printf_modular)
- name: putc
standards:
- stdc
@@ -347,6 +351,8 @@ functions:
- type: size_t
- type: const char *__restrict
- type: '...'
+ attributes:
+ - _LIBC_MODULAR_FORMAT_PRINTF(__snprintf_modular)
- name: sprintf
standards:
- stdc
@@ -355,6 +361,8 @@ functions:
- type: char *__restrict
- type: const char *__restrict
- type: '...'
+ attributes:
+ - _LIBC_MODULAR_FORMAT_PRINTF(__sprintf_modular)
- name: sscanf
standards:
- stdc
@@ -378,6 +386,8 @@ functions:
- type: char **__restrict
- type: const char *__restrict
- type: va_list
+ attributes:
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vasprintf_modular)
- name: vfprintf
standards:
- stdc
@@ -393,6 +403,8 @@ functions:
arguments:
- type: const char *__restrict
- type: va_list
+ attributes:
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vprintf_modular)
- name: vsnprintf
standards:
- stdc
@@ -402,6 +414,8 @@ functions:
- type: size_t
- type: const char *__restrict
- type: va_list
+ attributes:
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vsnprintf_modular)
- name: vsprintf
standards:
- stdc
@@ -410,6 +424,8 @@ functions:
- type: char *__restrict
- type: const char *__restrict
- type: va_list
+ attributes:
+ - _LIBC_MODULAR_FORMAT_PRINTF(__vsprintf_modular)
- name: vsscanf
standards:
- stdc
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index c75c8b11be2b5..9486a53499792 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -116,10 +116,15 @@ add_entrypoint_object(
libc.src.stdio.scanf_core.string_reader
)
+set(sprintf_srcs sprintf.cpp)
+if(LIBC_CONF_PRINTF_MODULAR)
+ list(APPEND sprintf_srcs sprintf_modular.cpp)
+endif()
+
add_entrypoint_object(
sprintf
SRCS
- sprintf.cpp
+ ${sprintf_srcs}
HDRS
sprintf.h
DEPENDS
@@ -131,10 +136,14 @@ add_entrypoint_object(
libc.src.__support.CPP.limits
)
+set(snprintf_srcs snprintf.cpp)
+if(LIBC_CONF_PRINTF_MODULAR)
+ list(APPEND snprintf_srcs snprintf_modular.cpp)
+endif()
add_entrypoint_object(
snprintf
SRCS
- snprintf.cpp
+ ${snprintf_srcs}
HDRS
snprintf.h
DEPENDS
@@ -146,10 +155,14 @@ add_entrypoint_object(
libc.src.__support.CPP.limits
)
+set(asprintf_srcs asprintf.cpp)
+if(LIBC_CONF_PRINTF_MODULAR)
+ list(APPEND asprintf_srcs asprintf_modular.cpp)
+endif()
add_entrypoint_object(
asprintf
SRCS
- asprintf.cpp
+ ${asprintf_srcs}
HDRS
asprintf.h
DEPENDS
@@ -160,10 +173,15 @@ add_entrypoint_object(
libc.src.__support.CPP.limits
)
+set(vsprintf_srcs vsprintf.cpp)
+if(LIBC_CONF_PRINTF_MODULAR)
+ list(APPEND vsprintf_srcs vsprintf_modular.cpp)
+endif()
+
add_entrypoint_object(
vsprintf
SRCS
- vsprintf.cpp
+ ${vsprintf_srcs}
HDRS
vsprintf.h
DEPENDS
@@ -175,10 +193,15 @@ add_entrypoint_object(
libc.src.__support.CPP.limits
)
+set(vsnprintf_srcs vsnprintf.cpp)
+if(LIBC_CONF_PRINTF_MODULAR)
+ list(APPEND vsnprintf_srcs vsnprintf_modular.cpp)
+endif()
+
add_entrypoint_object(
vsnprintf
SRCS
- vsnprintf.cpp
+ ${vsnprintf_srcs}
HDRS
vsnprintf.h
DEPENDS
@@ -190,10 +213,15 @@ add_entrypoint_object(
libc.src.__support.CPP.limits
)
+set(vasprintf_srcs vasprintf.cpp)
+if(LIBC_CONF_PRINTF_MODULAR)
+ list(APPEND vasprintf_srcs vasprintf_modular.cpp)
+endif()
+
add_entrypoint_object(
vasprintf
SRCS
- vasprintf.cpp
+ ${vasprintf_srcs}
HDRS
vasprintf.h
DEPENDS
diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index 0991dfca6a059..f80d2a5eb759d 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -26,7 +26,12 @@ LLVM_LIBC_FUNCTION(int, asprintf,
// and pointer semantics, as well as handling
// destruction automatically.
va_end(vlist);
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+ auto ret_val = printf_core::vasprintf_internal<true>(buffer, format, args);
+#else
auto ret_val = printf_core::vasprintf_internal(buffer, format, args);
+#endif
if (!ret_val.has_value()) {
libc_errno = printf_core::internal_error_to_errno(ret_val.error());
return -1;
diff --git a/libc/src/stdio/asprintf.h b/libc/src/stdio/asprintf.h
index 168721c4f98b9..26a1f1687aedd 100644
--- a/libc/src/stdio/asprintf.h
+++ b/libc/src/stdio/asprintf.h
@@ -14,6 +14,7 @@
namespace LIBC_NAMESPACE_DECL {
int asprintf(char **__restrict s, const char *__restrict format, ...);
+int __asprintf_modular(char **__restrict s, const char *__restrict format, ...);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/asprintf_modular.cpp b/libc/src/stdio/asprintf_modular.cpp
new file mode 100644
index 0000000000000..74b62cc0c2501
--- /dev/null
+++ b/libc/src/stdio/asprintf_modular.cpp
@@ -0,0 +1,43 @@
+//===-- Implementation of asprintf_modular ----------------------*- 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/__support/CPP/limits.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/asprintf.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_mapper.h"
+#include "src/stdio/printf_core/vasprintf_internal.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, __asprintf_modular,
+ (char **__restrict buffer, 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);
+ auto ret_val = printf_core::vasprintf_internal<true>(buffer, format, args);
+ if (!ret_val.has_value()) {
+ libc_errno = printf_core::internal_error_to_errno(ret_val.error());
+ return -1;
+ }
+ if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
+ libc_errno =
+ printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
+ return -1;
+ }
+
+ return static_cast<int>(ret_val.value());
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt
index a706accecf152..878aca8ca0fc2 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -186,10 +186,14 @@ add_entrypoint_object(
libc.include.stdio
)
+set(printf_srcs printf.cpp)
+if(LIBC_CONF_PRINTF_MODULAR)
+ list(APPEND printf_srcs printf_modular.cpp)
+endif()
add_entrypoint_object(
printf
SRCS
- printf.cpp
+ ${printf_srcs}
HDRS
../printf.h
DEPENDS
@@ -274,10 +278,14 @@ add_entrypoint_object(
libc.src.__support.arg_list
)
+set(vprintf_srcs vprintf.cpp)
+if(LIBC_CONF_PRINTF_MODULAR)
+ list(APPEND vprintf_srcs vprintf_modular.cpp)
+endif()
add_entrypoint_object(
vprintf
SRCS
- vprintf.cpp
+ ${vprintf_srcs}
HDRS
../vprintf.h
DEPENDS
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index 5010810906e24..b43bc1e92528f 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -26,7 +26,12 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
// destruction automatically.
va_end(vlist);
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+ return vfprintf_internal<true>(stdout, format, args);
+#else
return vfprintf_internal(stdout, format, args);
+#endif
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/printf_modular.cpp b/libc/src/stdio/baremetal/printf_modular.cpp
new file mode 100644
index 0000000000000..f03060a9c3ff5
--- /dev/null
+++ b/libc/src/stdio/baremetal/printf_modular.cpp
@@ -0,0 +1,32 @@
+//===-- Implementation of printf_modular 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 "hdr/stdio_macros.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/vfprintf_internal.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, __printf_modular, (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);
+
+ return vfprintf_internal<true>(stdout, format, args);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vfprintf.cpp b/libc/src/stdio/baremetal/vfprintf.cpp
index 2393ca4bcdb3e..c068a10181911 100644
--- a/libc/src/stdio/baremetal/vfprintf.cpp
+++ b/libc/src/stdio/baremetal/vfprintf.cpp
@@ -25,7 +25,12 @@ LLVM_LIBC_FUNCTION(int, vfprintf,
// and pointer semantics, as well as handling
// destruction automatically.
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+ return vfprintf_internal<true>(stream, format, args);
+#else
return vfprintf_internal(stream, format, args);
+#endif
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vfprintf_internal.h b/libc/src/stdio/baremetal/vfprintf_internal.h
index d38a56481bc37..50eaef4a4a3e2 100644
--- a/libc/src/stdio/baremetal/vfprintf_internal.h
+++ b/libc/src/stdio/baremetal/vfprintf_internal.h
@@ -38,6 +38,7 @@ LIBC_INLINE int write_hook(cpp::string_view str_view, void *cookie) {
} // namespace internal
+template <bool use_modular = false>
LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
const char *__restrict format,
internal::ArgList &args) {
@@ -48,7 +49,9 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
stream);
printf_core::Writer writer(wb);
- auto retval = printf_core::printf_main(&writer, format, args);
+ auto retval = use_modular
+ ? printf_core::printf_main_modular(&writer, format, args)
+ : printf_core::printf_main(&writer, format, args);
if (!retval.has_value()) {
libc_errno = printf_core::internal_error_to_errno(retval.error());
return -1;
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index 38f12ab09fa25..e2144b8d8175b 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -24,7 +24,12 @@ LLVM_LIBC_FUNCTION(int, vprintf,
// and pointer semantics, as well as handling
// destruction automatically.
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+ return vfprintf_internal<true>(stdout, format, args);
+#else
return vfprintf_internal(stdout, format, args);
+#endif
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vprintf_modular.cpp b/libc/src/stdio/baremetal/vprintf_modular.cpp
new file mode 100644
index 0000000000000..b14e0fae585bd
--- /dev/null
+++ b/libc/src/stdio/baremetal/vprintf_modular.cpp
@@ -0,0 +1,30 @@
+//===-- Implementation of vprintf_modular -----------------------*- 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/vprintf.h"
+
+#include "hdr/stdio_macros.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/baremetal/vfprintf_internal.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, __vprintf_modular,
+ (const char *__restrict format, va_list vlist)) {
+ internal::ArgList args(vlist); // This holder class allows for easier copying
+ // and pointer semantics, as well as handling
+ // destruction automatically.
+
+ return vfprintf_internal<true>(stdout, format, args);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt
index 71055edea3d9e..18da01b19d27a 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -416,6 +416,10 @@ if(LLVM_LIBC_FULL_BUILD)
)
endif()
+if (LIBC_CONF_PRINTF_MODULAR AND NOT LIBC_TARGET_OS_IS_BAREMETAL)
+ message(FATAL_ERROR "modular printf is only supported in baremetal stdio")
+endif()
+
add_generic_entrypoint_object(
printf
SRCS
diff --git a/libc/src/stdio/gpu/CMakeLists.txt b/libc/src/stdio/gpu/CMakeLists.txt
index 8412153bf5801..2aea66482997f 100644
--- a/libc/src/stdio/gpu/CMakeLists.txt
+++ b/libc/src/stdio/gpu/CMakeLists.txt
@@ -1,3 +1,7 @@
+if (LIBC_CONF_PRINTF_MODULAR)
+ message(FATAL_ERROR "modular printf is only supported in baremetal stdio")
+endif()
+
add_entrypoint_object(
stdin
SRCS
diff --git a/libc/src/stdio/printf.h b/libc/src/stdio/printf.h
index 9e47ad8680f9c..81b7d866a6a59 100644
--- a/libc/src/stdio/printf.h
+++ b/libc/src/stdio/printf.h
@@ -15,6 +15,7 @@
namespace LIBC_NAMESPACE_DECL {
int printf(const char *__restrict format, ...);
+int __printf_modular(const char *__restrict format, ...);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index ae93cc754299b..1a6b6383bf4d4 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -28,6 +28,9 @@ endif()
if(LIBC_CONF_PRINTF_RUNTIME_DISPATCH)
list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_RUNTIME_DISPATCH")
endif()
+if(LIBC_CONF_PRINTF_MODULAR)
+ list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_MODULAR")
+endif()
if(printf_config_copts)
list(PREPEND printf_config_copts "COMPILE_OPTIONS")
endif()
@@ -146,10 +149,12 @@ add_header_library(
${wchar_deps}
)
-add_header_library(
+add_object_library(
printf_main
HDRS
printf_main.h
+ SRCS
+ float_impl.cpp
DEPENDS
.parser
.converter
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index ed004f9a26a13..1b0a02ed426c1 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -1122,6 +1122,18 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
}
}
+template <WriteMode write_mode>
+LIBC_PRINTF_MODULAR_DECL int
+convert_float_decimal(Writer<write_mode> *writer, const FormatSection &to_conv);
+template <WriteMode write_mode>
+LIBC_PRINTF_MODULAR_DECL int
+convert_float_dec_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
+template <WriteMode write_mode>
+LIBC_PRINTF_MODULAR_DECL int
+convert_float_dec_auto(Writer<write_mode> *writer,
+ const FormatSection &to_conv);
+
+#ifdef LIBC_PRINTF_DEFINE_MODULAR
// TODO: unify the float converters to remove the duplicated checks for inf/nan.
template <WriteMode write_mode>
@@ -1189,6 +1201,7 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
return convert_inf_nan(writer, to_conv);
}
+#endif
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/float_dec_converter_limited.h b/libc/src/stdio/printf_core/float_dec_converter_limited.h
index 0f85d0a8d26b4..2fc2a180ae2b9 100644
--- a/libc/src/stdio/printf_core/float_dec_converter_limited.h
+++ b/libc/src/stdio/printf_core/float_dec_converter_limited.h
@@ -676,6 +676,18 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
return convert_float_typed<T>(writer, to_conv, float_bits, ConversionType::G);
}
+template <WriteMode write_mode>
+LIBC_PRINTF_MODULAR_DECL int
+convert_float_decimal(Writer<write_mode> *writer, const FormatSection &to_conv);
+template <WriteMode write_mode>
+LIBC_PRINTF_MODULAR_DECL int
+convert_float_dec_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
+template <WriteMode write_mode>
+LIBC_PRINTF_MODULAR_DECL int
+convert_float_dec_auto(Writer<write_mode> *writer,
+ const FormatSection &to_conv);
+
+#ifdef LIBC_PRINTF_DEFINE_MODULAR
template <WriteMode write_mode>
LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
const FormatSection &to_conv) {
@@ -693,6 +705,7 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
const FormatSection &to_conv) {
return convert_float_outer(writer, to_conv, ConversionType::G);
}
+#endif
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index 9b57f1d803e74..d2be294dc47ab 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -25,6 +25,11 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
+template <WriteMode write_mode>
+LIBC_PRINTF_MODULAR_DECL int
+convert_float_hex_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
+
+#ifdef LIBC_PRINTF_DEFINE_MODULAR
template <WriteMode write_mode>
LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
const FormatSection &to_conv) {
@@ -253,6 +258,7 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
}
return WRITE_OK;
}
+#endif
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/float_impl.cpp b/libc/src/stdio/printf_core/float_impl.cpp
new file mode 100644
index 0000000000000..2215ac101f47d
--- /dev/null
+++ b/libc/src/stdio/printf_core/float_impl.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file instantiates the functionality needed for supporting floating
+/// point arguments in modular printf builds. Non-modular printf builds
+/// implicitly instantiate these functions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifdef LIBC_COPT_PRINTF_MODULAR
+#include "src/__support/arg_list.h"
+
+#define LIBC_PRINTF_DEFINE_MODULAR
+#include "src/stdio/printf_core/float_dec_converter.h"
+#include "src/stdio/printf_core/float_hex_converter.h"
+#include "src/stdio/printf_core/parser.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace printf_core {
+template class Parser<internal::ArgList>;
+template class Parser<internal::DummyArgList<false>>;
+template class Parser<internal::DummyArgList<true>>;
+template class Parser<internal::StructArgList<false>>;
+template class Parser<internal::StructArgList<true>>;
+
+#define INSTANTIATE_CONVERT_FN(NAME) \
+ template int NAME<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>( \
+ Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> * writer, \
+ const FormatSection &to_conv); \
+ template int NAME<WriteMode::FLUSH_TO_STREAM>( \
+ Writer<WriteMode::FLUSH_TO_STREAM> * writer, \
+ const FormatSection &to_conv); \
+ template int NAME<WriteMode::RESIZE_AND_FILL_BUFF>( \
+ Writer<WriteMode::RESIZE_AND_FILL_BUFF> * writer, \
+ const FormatSection &to_conv); \
+ template int NAME<WriteMode::RUNTIME_DISPATCH>( \
+ Writer<WriteMode::RUNTIME_DISPATCH> * writer, \
+ const FormatSection &to_conv)
+
+INSTANTIATE_CONVERT_FN(convert_float_decimal);
+INSTANTIATE_CONVERT_FN(convert_float_dec_exp);
+INSTANTIATE_CONVERT_FN(convert_float_dec_auto);
+INSTANTIATE_CONVERT_FN(convert_float_hex_exp);
+
+} // namespace printf_core
+} // namespace LIBC_NAMESPACE_DECL
+
+// Bring this file into the link if __printf_float is referenced.
+extern "C" void __printf_float() {}
+#endif
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index a3b62991bcec9..37a05aed9a131 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -249,11 +249,7 @@ template <typename ArgProvider> class Parser {
case ('A'):
case ('g'):
case ('G'):
- if (lm != LengthModifier::L) {
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index);
- } else {
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index);
- }
+ write_float_arg_val(section, lm, conv_index);
break;
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
@@ -312,6 +308,12 @@ template <typename ArgProvider> class Parser {
return section;
}
+ LIBC_PRINTF_MODULAR_DECL void write_float_arg_val(FormatSection §ion,
+ LengthModifier lm,
+ size_t conv_index);
+ LIBC_PRINTF_MODULAR_DECL TypeDesc float_type_desc(LengthModifier lm);
+ LIBC_PRINTF_MODULAR_DECL bool advance_arg_if_float(TypeDesc cur_type_desc);
+
private:
// parse_flags parses the flags inside a format string. It assumes that
// str[*local_pos] is inside a format specifier, and parses any flags it
@@ -487,10 +489,9 @@ template <typename ArgProvider> class Parser {
args_cur.template next_var<uint64_t>();
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
// Floating point numbers are stored separately from the other arguments.
- else if (cur_type_desc == type_desc_from_type<double>())
- args_cur.template next_var<double>();
- else if (cur_type_desc == type_desc_from_type<long double>())
- args_cur.template next_var<long double>();
+ else if (&Parser::advance_arg_if_float &&
+ advance_arg_if_float(cur_type_desc))
+ ;
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
// Floating point numbers may be stored separately from the other
@@ -652,10 +653,7 @@ template <typename ArgProvider> class Parser {
case ('A'):
case ('g'):
case ('G'):
- if (lm != LengthModifier::L)
- conv_size = type_desc_from_type<double>();
- else
- conv_size = type_desc_from_type<long double>();
+ conv_size = float_type_desc(lm);
break;
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
@@ -704,6 +702,40 @@ template <typename ArgProvider> class Parser {
#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
};
+#ifdef LIBC_PRINTF_DEFINE_MODULAR
+template <typename ArgParser>
+LIBC_INLINE void
+Parser<ArgParser>::write_float_arg_val(FormatSection §ion,
+ LengthModifier lm,
+ [[maybe_unused]] size_t conv_index) {
+ if (lm != LengthModifier::L) {
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index);
+ } else {
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index);
+ }
+}
+
+template <typename ArgParser>
+LIBC_INLINE TypeDesc Parser<ArgParser>::float_type_desc(LengthModifier lm) {
+ if (lm != LengthModifier::L)
+ return type_desc_from_type<double>();
+ else
+ return type_desc_from_type<long double>();
+}
+
+template <typename ArgParser>
+LIBC_INLINE bool
+Parser<ArgParser>::advance_arg_if_float(TypeDesc cur_type_desc) {
+ if (cur_type_desc == type_desc_from_type<double>())
+ args_cur.template next_var<double>();
+ else if (cur_type_desc == type_desc_from_type<long double>())
+ args_cur.template next_var<long double>();
+ else
+ return false;
+ return true;
+}
+#endif
+
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/printf_config.h b/libc/src/stdio/printf_core/printf_config.h
index 8a48abdd170ec..8f6ae8b41bc92 100644
--- a/libc/src/stdio/printf_core/printf_config.h
+++ b/libc/src/stdio/printf_core/printf_config.h
@@ -48,4 +48,11 @@
// LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+#ifdef LIBC_COPT_PRINTF_MODULAR
+#define LIBC_PRINTF_MODULAR_DECL [[gnu::weak]]
+#else
+#define LIBC_PRINTF_MODULAR_DECL LIBC_INLINE
+#define LIBC_PRINTF_DEFINE_MODULAR
+#endif
+
#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PRINTF_CONFIG_H
diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index 1c7a7237c097d..874c24464ed52 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -23,9 +23,9 @@ namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
template <WriteMode write_mode>
-ErrorOr<size_t> printf_main(Writer<write_mode> *writer,
- const char *__restrict str,
- internal::ArgList &args) {
+ErrorOr<size_t> printf_main_modular(Writer<write_mode> *writer,
+ const char *__restrict str,
+ internal::ArgList &args) {
Parser<internal::ArgList> parser(str, args);
int result = 0;
for (FormatSection cur_section = parser.get_next_section();
@@ -42,6 +42,16 @@ ErrorOr<size_t> printf_main(Writer<write_mode> *writer,
return writer->get_chars_written();
}
+template <WriteMode write_mode>
+ErrorOr<size_t> printf_main(Writer<write_mode> *writer,
+ const char *__restrict str,
+ internal::ArgList &args) {
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+#endif
+ return printf_main_modular(writer, str, args);
+}
+
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index db6b95d49aaca..1dbf5787717f9 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -41,6 +41,7 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str,
constexpr size_t DEFAULT_BUFFER_SIZE = 200;
+template <bool use_modular = false>
LIBC_INLINE ErrorOr<size_t> vasprintf_internal(char **ret,
const char *__restrict format,
internal::ArgList args) {
@@ -49,7 +50,9 @@ LIBC_INLINE ErrorOr<size_t> vasprintf_internal(char **ret,
resize_overflow_hook);
printf_core::Writer writer(wb);
- auto ret_val = printf_core::printf_main(&writer, format, args);
+ auto ret_val = use_modular
+ ? printf_core::printf_main_modular(&writer, format, args)
+ : printf_core::printf_main(&writer, format, args);
if (!ret_val.has_value()) {
*ret = nullptr;
return ret_val;
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 321b0693ad339..6ce4ab3d62d4a 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -81,16 +81,17 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
return WRITE_OK;
}
-LIBC_INLINE ErrorOr<size_t> vfprintf_internal(::FILE *__restrict stream,
- const char *__restrict format,
- internal::ArgList &args) {
+LIBC_INLINE ErrorOr<size_t>
+vfprintf_internal_modular(::FILE *__restrict stream,
+ const char *__restrict format,
+ internal::ArgList &args) {
constexpr size_t BUFF_SIZE = 1024;
char buffer[BUFF_SIZE];
printf_core::FlushingBuffer wb(buffer, BUFF_SIZE, &file_write_hook,
reinterpret_cast<void *>(stream));
Writer writer(wb);
internal::flockfile(stream);
- auto retval = printf_main(&writer, format, args);
+ auto retval = printf_main_modular(&writer, format, args);
if (!retval.has_value()) {
internal::funlockfile(stream);
return retval;
@@ -102,6 +103,15 @@ LIBC_INLINE ErrorOr<size_t> vfprintf_internal(::FILE *__restrict stream,
return retval;
}
+LIBC_INLINE ErrorOr<size_t> vfprintf_internal(::FILE *__restrict stream,
+ const char *__restrict format,
+ internal::ArgList &args) {
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ __asm__ __volatile__(".reloc ., BFD_RELOC_NONE, __printf_float");
+#endif
+ return vfprintf_internal_modular(stream, format, args);
+}
+
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index 8364e8d59b278..d8fb1b1ad3577 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -34,7 +34,12 @@ LLVM_LIBC_FUNCTION(int, snprintf,
printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(wb);
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+ auto ret_val = printf_core::printf_main_modular(&writer, format, args);
+#else
auto ret_val = printf_core::printf_main(&writer, format, args);
+#endif
if (!ret_val.has_value()) {
libc_errno = printf_core::internal_error_to_errno(ret_val.error());
return -1;
diff --git a/libc/src/stdio/snprintf.h b/libc/src/stdio/snprintf.h
index 92a6529704076..9d350d2712803 100644
--- a/libc/src/stdio/snprintf.h
+++ b/libc/src/stdio/snprintf.h
@@ -16,6 +16,8 @@ namespace LIBC_NAMESPACE_DECL {
int snprintf(char *__restrict buffer, size_t buffsz,
const char *__restrict format, ...);
+int __snprintf_modular(char *__restrict buffer, size_t buffsz,
+ const char *__restrict format, ...);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/snprintf_modular.cpp b/libc/src/stdio/snprintf_modular.cpp
new file mode 100644
index 0000000000000..2e7683d56de75
--- /dev/null
+++ b/libc/src/stdio/snprintf_modular.cpp
@@ -0,0 +1,54 @@
+//===-- Implementation of snprintf_modular ----------------------*- 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/snprintf.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_mapper.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 {
+
+LLVM_LIBC_FUNCTION(int, __snprintf_modular,
+ (char *__restrict buffer, size_t buffsz,
+ 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);
+ printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
+ printf_core::Writer writer(wb);
+
+ auto ret_val = printf_core::printf_main_modular(&writer, format, args);
+ if (!ret_val.has_value()) {
+ libc_errno = printf_core::internal_error_to_errno(ret_val.error());
+ return -1;
+ }
+ if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
+ wb.buff[wb.buff_cur] = '\0';
+
+ if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
+ libc_errno =
+ printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
+ return -1;
+ }
+
+ return static_cast<int>(ret_val.value());
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index d340096bb6d2b..bbb3f64ea7b15 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -35,7 +35,12 @@ LLVM_LIBC_FUNCTION(int, sprintf,
cpp::numeric_limits<size_t>::max());
printf_core::Writer writer(wb);
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+ auto ret_val = printf_core::printf_main_modular(&writer, format, args);
+#else
auto ret_val = printf_core::printf_main(&writer, format, args);
+#endif
if (!ret_val.has_value()) {
libc_errno = printf_core::internal_error_to_errno(ret_val.error());
return -1;
diff --git a/libc/src/stdio/sprintf.h b/libc/src/stdio/sprintf.h
index ef65de399dc6d..4008858aaaa63 100644
--- a/libc/src/stdio/sprintf.h
+++ b/libc/src/stdio/sprintf.h
@@ -14,6 +14,8 @@
namespace LIBC_NAMESPACE_DECL {
int sprintf(char *__restrict buffer, const char *__restrict format, ...);
+int __sprintf_modular(char *__restrict buffer, const char *__restrict format,
+ ...);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/sprintf_modular.cpp b/libc/src/stdio/sprintf_modular.cpp
new file mode 100644
index 0000000000000..cfed3fe4a4f4d
--- /dev/null
+++ b/libc/src/stdio/sprintf_modular.cpp
@@ -0,0 +1,54 @@
+//===-- Implementation of sprintf_modular -----------------------*- 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/sprintf.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_mapper.h"
+#include "src/stdio/printf_core/printf_main.h"
+#include "src/stdio/printf_core/writer.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, __sprintf_modular,
+ (char *__restrict buffer, 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);
+
+ printf_core::DropOverflowBuffer wb(buffer,
+ cpp::numeric_limits<size_t>::max());
+ printf_core::Writer writer(wb);
+
+ auto ret_val = printf_core::printf_main_modular(&writer, format, args);
+ if (!ret_val.has_value()) {
+ libc_errno = printf_core::internal_error_to_errno(ret_val.error());
+ return -1;
+ }
+ wb.buff[wb.buff_cur] = '\0';
+
+ if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
+ libc_errno =
+ printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
+ return -1;
+ }
+
+ return static_cast<int>(ret_val.value());
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index bd77cd8864312..64d959c6edcae 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -22,7 +22,12 @@ LLVM_LIBC_FUNCTION(int, vasprintf,
internal::ArgList args(vlist); // This holder class allows for easier copying
// and pointer semantics, as well as handling
// destruction automatically.
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+ auto ret_val = printf_core::vasprintf_internal<true>(ret, format, args);
+#else
auto ret_val = printf_core::vasprintf_internal(ret, format, args);
+#endif
if (!ret_val.has_value()) {
libc_errno = printf_core::internal_error_to_errno(ret_val.error());
return -1;
diff --git a/libc/src/stdio/vasprintf.h b/libc/src/stdio/vasprintf.h
index 7a98568edbc07..9f6ad87deac81 100644
--- a/libc/src/stdio/vasprintf.h
+++ b/libc/src/stdio/vasprintf.h
@@ -16,6 +16,8 @@ namespace LIBC_NAMESPACE_DECL {
int vasprintf(char **__restrict s, const char *__restrict format,
va_list vlist);
+int __vasprintf_modular(char **__restrict s, const char *__restrict format,
+ va_list vlist);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vasprintf_modular.cpp b/libc/src/stdio/vasprintf_modular.cpp
new file mode 100644
index 0000000000000..98620bb8a440c
--- /dev/null
+++ b/libc/src/stdio/vasprintf_modular.cpp
@@ -0,0 +1,38 @@
+//===-- Implementation of vasprintf_modular ---------------------*- 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/__support/CPP/limits.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_mapper.h"
+#include "src/stdio/printf_core/vasprintf_internal.h"
+#include "src/stdio/vasprintf.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, __vasprintf_modular,
+ (char **__restrict ret, const char *__restrict format,
+ va_list vlist)) {
+ internal::ArgList args(vlist); // This holder class allows for easier copying
+ // and pointer semantics, as well as handling
+ // destruction automatically.
+ auto ret_val = printf_core::vasprintf_internal<true>(ret, format, args);
+ if (!ret_val.has_value()) {
+ libc_errno = printf_core::internal_error_to_errno(ret_val.error());
+ return -1;
+ }
+ if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
+ libc_errno =
+ printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
+ return -1;
+ }
+ return static_cast<int>(ret_val.value());
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vprintf.h b/libc/src/stdio/vprintf.h
index 63f0f3ae62d6e..70cf03f69a767 100644
--- a/libc/src/stdio/vprintf.h
+++ b/libc/src/stdio/vprintf.h
@@ -16,6 +16,7 @@
namespace LIBC_NAMESPACE_DECL {
int vprintf(const char *__restrict format, va_list vlist);
+int __vprintf_modular(const char *__restrict format, va_list vlist);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index b65343dfefc75..bea980d6b20ec 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -31,7 +31,12 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(wb);
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+ auto ret_val = printf_core::printf_main_modular(&writer, format, args);
+#else
auto ret_val = printf_core::printf_main(&writer, format, args);
+#endif
if (!ret_val.has_value()) {
libc_errno = printf_core::internal_error_to_errno(ret_val.error());
return -1;
diff --git a/libc/src/stdio/vsnprintf.h b/libc/src/stdio/vsnprintf.h
index 27ae763f7746a..0b8af0ac6ff36 100644
--- a/libc/src/stdio/vsnprintf.h
+++ b/libc/src/stdio/vsnprintf.h
@@ -17,6 +17,8 @@ namespace LIBC_NAMESPACE_DECL {
int vsnprintf(char *__restrict buffer, size_t buffsz,
const char *__restrict format, va_list vlist);
+int __vsnprintf_modular(char *__restrict buffer, size_t buffsz,
+ const char *__restrict format, va_list vlist);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vsnprintf_modular.cpp b/libc/src/stdio/vsnprintf_modular.cpp
new file mode 100644
index 0000000000000..7225311601281
--- /dev/null
+++ b/libc/src/stdio/vsnprintf_modular.cpp
@@ -0,0 +1,51 @@
+//===-- Implementation of vsnprintf_modular ---------------------*- 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/vsnprintf.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_mapper.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 {
+
+LLVM_LIBC_FUNCTION(int, __vsnprintf_modular,
+ (char *__restrict buffer, size_t buffsz,
+ const char *__restrict format, va_list vlist)) {
+ internal::ArgList args(vlist); // This holder class allows for easier copying
+ // and pointer semantics, as well as handling
+ // destruction automatically.
+ printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
+ printf_core::Writer writer(wb);
+
+ auto ret_val = printf_core::printf_main_modular(&writer, format, args);
+ if (!ret_val.has_value()) {
+ libc_errno = printf_core::internal_error_to_errno(ret_val.error());
+ return -1;
+ }
+ if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
+ wb.buff[wb.buff_cur] = '\0';
+
+ if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
+ libc_errno =
+ printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
+ return -1;
+ }
+
+ return static_cast<int>(ret_val.value());
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index 8affb88d2b807..3dae05fbbaadd 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -32,7 +32,12 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
cpp::numeric_limits<size_t>::max());
printf_core::Writer writer(wb);
+#ifdef LIBC_COPT_PRINTF_MODULAR
+ LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
+ auto ret_val = printf_core::printf_main_modular(&writer, format, args);
+#else
auto ret_val = printf_core::printf_main(&writer, format, args);
+#endif
if (!ret_val.has_value()) {
libc_errno = printf_core::internal_error_to_errno(ret_val.error());
return -1;
diff --git a/libc/src/stdio/vsprintf.h b/libc/src/stdio/vsprintf.h
index abb89ba76eae5..54eb6120098a4 100644
--- a/libc/src/stdio/vsprintf.h
+++ b/libc/src/stdio/vsprintf.h
@@ -16,6 +16,8 @@ namespace LIBC_NAMESPACE_DECL {
int vsprintf(char *__restrict buffer, const char *__restrict format,
va_list vlist);
+int __vsprintf_modular(char *__restrict buffer, const char *__restrict format,
+ va_list vlist);
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vsprintf_modular.cpp b/libc/src/stdio/vsprintf_modular.cpp
new file mode 100644
index 0000000000000..844d1e3cdd550
--- /dev/null
+++ b/libc/src/stdio/vsprintf_modular.cpp
@@ -0,0 +1,50 @@
+//===-- Implementation of vsprintf_modular ----------------------*- 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/vsprintf.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_mapper.h"
+#include "src/stdio/printf_core/printf_main.h"
+#include "src/stdio/printf_core/writer.h"
+
+#include <stdarg.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, __vsprintf_modular,
+ (char *__restrict buffer, const char *__restrict format,
+ va_list vlist)) {
+ internal::ArgList args(vlist); // This holder class allows for easier copying
+ // and pointer semantics, as well as handling
+ // destruction automatically.
+
+ printf_core::DropOverflowBuffer wb(buffer,
+ cpp::numeric_limits<size_t>::max());
+ printf_core::Writer writer(wb);
+
+ auto ret_val = printf_core::printf_main_modular(&writer, format, args);
+ if (!ret_val.has_value()) {
+ libc_errno = printf_core::internal_error_to_errno(ret_val.error());
+ return -1;
+ }
+ wb.buff[wb.buff_cur] = '\0';
+
+ if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
+ libc_errno =
+ printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
+ return -1;
+ }
+ return static_cast<int>(ret_val.value());
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/utils/hdrgen/hdrgen/header.py b/libc/utils/hdrgen/hdrgen/header.py
index b69d22494a7ac..1af741844bd01 100644
--- a/libc/utils/hdrgen/hdrgen/header.py
+++ b/libc/utils/hdrgen/hdrgen/header.py
@@ -177,7 +177,7 @@ def includes(self):
for typ in self.all_types()
}
| {
- PurePosixPath("llvm-libc-macros") / f"{attr}.h"
+ PurePosixPath("llvm-libc-macros") / f"{attr.split('(')[0]}.h"
for attr in self.all_attributes() - COMMON_ATTRIBUTES
}
)
diff --git a/libc/utils/hdrgen/tests/expected_output/test_header.h b/libc/utils/hdrgen/tests/expected_output/test_header.h
index 49112a353f7b6..7704176bbc3d7 100644
--- a/libc/utils/hdrgen/tests/expected_output/test_header.h
+++ b/libc/utils/hdrgen/tests/expected_output/test_header.h
@@ -13,6 +13,7 @@
#include "llvm-libc-macros/float16-macros.h"
#include "llvm-libc-macros/CONST_FUNC_A.h"
+#include "llvm-libc-macros/MACRO_ATTR.h"
#include "llvm-libc-macros/test_more-macros.h"
#include "llvm-libc-macros/test_small-macros.h"
#include "llvm-libc-types/float128.h"
@@ -32,7 +33,7 @@ enum {
__BEGIN_C_DECLS
-CONST_FUNC_A void func_a(void) __NOEXCEPT;
+CONST_FUNC_A MACRO_ATTR(A) void func_a(void) __NOEXCEPT;
#ifdef LIBC_TYPES_HAS_FLOAT128
float128 func_b(void) __NOEXCEPT;
diff --git a/libc/utils/hdrgen/tests/expected_output/test_small.json b/libc/utils/hdrgen/tests/expected_output/test_small.json
index 8502df23b9a41..e62fce1043ee5 100644
--- a/libc/utils/hdrgen/tests/expected_output/test_small.json
+++ b/libc/utils/hdrgen/tests/expected_output/test_small.json
@@ -5,6 +5,7 @@
"includes": [
"__llvm-libc-common.h",
"llvm-libc-macros/CONST_FUNC_A.h",
+ "llvm-libc-macros/MACRO_ATTR.h",
"llvm-libc-macros/test_more-macros.h",
"llvm-libc-macros/test_small-macros.h",
"llvm-libc-types/float128.h",
diff --git a/libc/utils/hdrgen/tests/input/merge1.yaml b/libc/utils/hdrgen/tests/input/merge1.yaml
index 950abd1770320..9abcf7f266710 100644
--- a/libc/utils/hdrgen/tests/input/merge1.yaml
+++ b/libc/utils/hdrgen/tests/input/merge1.yaml
@@ -17,3 +17,4 @@ functions:
- stdc
attributes:
- CONST_FUNC_A
+ - MACRO_ATTR(A)
More information about the libc-commits
mailing list