[clang] [libc] [libc] Modular printf option (float only) (PR #147426)
Daniel Thornburgh via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 5 14:56:01 PST 2026
https://github.com/mysterymath updated https://github.com/llvm/llvm-project/pull/147426
>From f58a20d34997107a2849c0c2111d850341f82fdf 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 1/8] [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 b37154e294ec9..3d0cd2b64ad61 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 0813ab3554216..524d2baafa3fc 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 c6254aafe3849..ca59c548d6948 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)
>From 0ef729ccfbc435c3e7cda8c146513fb4dfd1f2e7 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 28 Jan 2026 14:47:51 -0800
Subject: [PATCH 2/8] Attr Docs typos
---
clang/include/clang/Basic/AttrDocs.td | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index b3dcd4410de95..429ce266d5c79 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -9726,9 +9726,9 @@ not all aspects of the implementation are needed for a given call, the compiler
may redirect the call to the identifier given as the first argument to the
attribute (the modular implementation function).
-The second argument is a implementation name, and the remaining arguments are
+The second argument is an implementation name, and the remaining arguments are
aspects of the format string for the compiler to report. The implementation
-name is an unevaluated identifier be in the C namespace.
+name is an unevaluated identifier in the C namespace.
The compiler reports that a call requires an aspect by issuing a relocation for
the symbol ``<impl_name>_<aspect>`` at the point of the call. This arranges for
>From 4f9c3657e09c6f9594d21689b639beea3a46474d Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Wed, 28 Jan 2026 15:03:26 -0800
Subject: [PATCH 3/8] Use constexpr for switching on printf core version
---
libc/src/stdio/baremetal/vfprintf_internal.h | 9 ++++++---
libc/src/stdio/printf_core/vasprintf_internal.h | 9 ++++++---
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/libc/src/stdio/baremetal/vfprintf_internal.h b/libc/src/stdio/baremetal/vfprintf_internal.h
index 50eaef4a4a3e2..c4cd8b8ad8a83 100644
--- a/libc/src/stdio/baremetal/vfprintf_internal.h
+++ b/libc/src/stdio/baremetal/vfprintf_internal.h
@@ -49,9 +49,12 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
stream);
printf_core::Writer writer(wb);
- auto retval = use_modular
- ? printf_core::printf_main_modular(&writer, format, args)
- : printf_core::printf_main(&writer, format, args);
+ auto retval = [&] {
+ if constexpr (use_modular)
+ return printf_core::printf_main_modular(&writer, format, args);
+ else
+ return 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/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index 1dbf5787717f9..fe5428e6118dc 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -50,9 +50,12 @@ LIBC_INLINE ErrorOr<size_t> vasprintf_internal(char **ret,
resize_overflow_hook);
printf_core::Writer writer(wb);
- auto ret_val = use_modular
- ? printf_core::printf_main_modular(&writer, format, args)
- : printf_core::printf_main(&writer, format, args);
+ auto ret_val = [&] {
+ if constexpr (use_modular)
+ return printf_core::printf_main_modular(&writer, format, args);
+ else
+ return printf_core::printf_main(&writer, format, args);
+ }();
if (!ret_val.has_value()) {
*ret = nullptr;
return ret_val;
>From 5c011f49987223afccba9d441031ccd6f74a99eb Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 29 Jan 2026 14:52:47 -0800
Subject: [PATCH 4/8] Undo changes to core vfprintf_internal; now altering
baremetal only
---
libc/src/stdio/printf_core/vfprintf_internal.h | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 6ce4ab3d62d4a..321b0693ad339 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -81,17 +81,16 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
return WRITE_OK;
}
-LIBC_INLINE ErrorOr<size_t>
-vfprintf_internal_modular(::FILE *__restrict stream,
- const char *__restrict format,
- internal::ArgList &args) {
+LIBC_INLINE ErrorOr<size_t> vfprintf_internal(::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_modular(&writer, format, args);
+ auto retval = printf_main(&writer, format, args);
if (!retval.has_value()) {
internal::funlockfile(stream);
return retval;
@@ -103,15 +102,6 @@ vfprintf_internal_modular(::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
>From 442680a78c5ce9e622432b302643ce44dfb182f4 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 29 Jan 2026 16:16:48 -0800
Subject: [PATCH 5/8] Clarify logic about definitions; change MODULAR->MODULE
---
libc/src/stdio/printf_core/float_dec_converter.h | 8 ++++----
libc/src/stdio/printf_core/float_dec_converter_limited.h | 8 ++++----
libc/src/stdio/printf_core/float_hex_converter.h | 4 ++--
libc/src/stdio/printf_core/float_impl.cpp | 3 ++-
libc/src/stdio/printf_core/parser.h | 8 ++++----
libc/src/stdio/printf_core/printf_config.h | 5 ++---
6 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index 1b0a02ed426c1..0f63f666a8ccb 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -1123,17 +1123,17 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
}
template <WriteMode write_mode>
-LIBC_PRINTF_MODULAR_DECL int
+LIBC_PRINTF_MODULE_DECL int
convert_float_decimal(Writer<write_mode> *writer, const FormatSection &to_conv);
template <WriteMode write_mode>
-LIBC_PRINTF_MODULAR_DECL int
+LIBC_PRINTF_MODULE_DECL int
convert_float_dec_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
template <WriteMode write_mode>
-LIBC_PRINTF_MODULAR_DECL int
+LIBC_PRINTF_MODULE_DECL int
convert_float_dec_auto(Writer<write_mode> *writer,
const FormatSection &to_conv);
-#ifdef LIBC_PRINTF_DEFINE_MODULAR
+#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
// TODO: unify the float converters to remove the duplicated checks for inf/nan.
template <WriteMode write_mode>
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 2fc2a180ae2b9..491b74ebd758a 100644
--- a/libc/src/stdio/printf_core/float_dec_converter_limited.h
+++ b/libc/src/stdio/printf_core/float_dec_converter_limited.h
@@ -677,17 +677,17 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
}
template <WriteMode write_mode>
-LIBC_PRINTF_MODULAR_DECL int
+LIBC_PRINTF_MODULE_DECL int
convert_float_decimal(Writer<write_mode> *writer, const FormatSection &to_conv);
template <WriteMode write_mode>
-LIBC_PRINTF_MODULAR_DECL int
+LIBC_PRINTF_MODULE_DECL int
convert_float_dec_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
template <WriteMode write_mode>
-LIBC_PRINTF_MODULAR_DECL int
+LIBC_PRINTF_MODULE_DECL int
convert_float_dec_auto(Writer<write_mode> *writer,
const FormatSection &to_conv);
-#ifdef LIBC_PRINTF_DEFINE_MODULAR
+#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
template <WriteMode write_mode>
LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
const FormatSection &to_conv) {
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index d2be294dc47ab..15d21b1ef9b9e 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -26,10 +26,10 @@ namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
template <WriteMode write_mode>
-LIBC_PRINTF_MODULAR_DECL int
+LIBC_PRINTF_MODULE_DECL int
convert_float_hex_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
-#ifdef LIBC_PRINTF_DEFINE_MODULAR
+#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
template <WriteMode write_mode>
LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
const FormatSection &to_conv) {
diff --git a/libc/src/stdio/printf_core/float_impl.cpp b/libc/src/stdio/printf_core/float_impl.cpp
index 2215ac101f47d..47827cb0c3084 100644
--- a/libc/src/stdio/printf_core/float_impl.cpp
+++ b/libc/src/stdio/printf_core/float_impl.cpp
@@ -16,7 +16,8 @@
#ifdef LIBC_COPT_PRINTF_MODULAR
#include "src/__support/arg_list.h"
-#define LIBC_PRINTF_DEFINE_MODULAR
+
+#define LIBC_PRINTF_DEFINE_MODULES
#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"
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index 37a05aed9a131..90cb4fd03a147 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -308,11 +308,11 @@ template <typename ArgProvider> class Parser {
return section;
}
- LIBC_PRINTF_MODULAR_DECL void write_float_arg_val(FormatSection §ion,
+ LIBC_PRINTF_MODULE_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);
+ LIBC_PRINTF_MODULE_DECL TypeDesc float_type_desc(LengthModifier lm);
+ LIBC_PRINTF_MODULE_DECL bool advance_arg_if_float(TypeDesc cur_type_desc);
private:
// parse_flags parses the flags inside a format string. It assumes that
@@ -702,7 +702,7 @@ template <typename ArgProvider> class Parser {
#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
};
-#ifdef LIBC_PRINTF_DEFINE_MODULAR
+#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
template <typename ArgParser>
LIBC_INLINE void
Parser<ArgParser>::write_float_arg_val(FormatSection §ion,
diff --git a/libc/src/stdio/printf_core/printf_config.h b/libc/src/stdio/printf_core/printf_config.h
index 8f6ae8b41bc92..bb4be8bdd6b1c 100644
--- a/libc/src/stdio/printf_core/printf_config.h
+++ b/libc/src/stdio/printf_core/printf_config.h
@@ -49,10 +49,9 @@
// LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
#ifdef LIBC_COPT_PRINTF_MODULAR
-#define LIBC_PRINTF_MODULAR_DECL [[gnu::weak]]
+#define LIBC_PRINTF_MODULE_DECL [[gnu::weak]]
#else
-#define LIBC_PRINTF_MODULAR_DECL LIBC_INLINE
-#define LIBC_PRINTF_DEFINE_MODULAR
+#define LIBC_PRINTF_MODULE_DECL LIBC_INLINE
#endif
#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PRINTF_CONFIG_H
>From 428416e014901eb3d6698ce5f651323e15ad321d Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 29 Jan 2026 16:07:19 -0800
Subject: [PATCH 6/8] clang-format
---
libc/src/stdio/baremetal/printf_modular.cpp | 3 ++-
libc/src/stdio/printf_core/float_dec_converter.h | 8 ++++----
libc/src/stdio/printf_core/float_dec_converter_limited.h | 8 ++++----
libc/src/stdio/printf_core/float_hex_converter.h | 4 ++--
libc/src/stdio/printf_core/float_impl.cpp | 1 -
libc/src/stdio/printf_core/parser.h | 4 ++--
6 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/libc/src/stdio/baremetal/printf_modular.cpp b/libc/src/stdio/baremetal/printf_modular.cpp
index f03060a9c3ff5..cb4b9ddcbcdbb 100644
--- a/libc/src/stdio/baremetal/printf_modular.cpp
+++ b/libc/src/stdio/baremetal/printf_modular.cpp
@@ -18,7 +18,8 @@
namespace LIBC_NAMESPACE_DECL {
-LLVM_LIBC_FUNCTION(int, __printf_modular, (const char *__restrict format, ...)) {
+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
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index 0f63f666a8ccb..6221f465a09c4 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -1123,11 +1123,11 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
}
template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int
-convert_float_decimal(Writer<write_mode> *writer, const FormatSection &to_conv);
+LIBC_PRINTF_MODULE_DECL int convert_float_decimal(Writer<write_mode> *writer,
+ const FormatSection &to_conv);
template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int
-convert_float_dec_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
+LIBC_PRINTF_MODULE_DECL int convert_float_dec_exp(Writer<write_mode> *writer,
+ const FormatSection &to_conv);
template <WriteMode write_mode>
LIBC_PRINTF_MODULE_DECL int
convert_float_dec_auto(Writer<write_mode> *writer,
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 491b74ebd758a..183a09397166a 100644
--- a/libc/src/stdio/printf_core/float_dec_converter_limited.h
+++ b/libc/src/stdio/printf_core/float_dec_converter_limited.h
@@ -677,11 +677,11 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
}
template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int
-convert_float_decimal(Writer<write_mode> *writer, const FormatSection &to_conv);
+LIBC_PRINTF_MODULE_DECL int convert_float_decimal(Writer<write_mode> *writer,
+ const FormatSection &to_conv);
template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int
-convert_float_dec_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
+LIBC_PRINTF_MODULE_DECL int convert_float_dec_exp(Writer<write_mode> *writer,
+ const FormatSection &to_conv);
template <WriteMode write_mode>
LIBC_PRINTF_MODULE_DECL int
convert_float_dec_auto(Writer<write_mode> *writer,
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index 15d21b1ef9b9e..9ae9f0e76477c 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -26,8 +26,8 @@ namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int
-convert_float_hex_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
+LIBC_PRINTF_MODULE_DECL int convert_float_hex_exp(Writer<write_mode> *writer,
+ const FormatSection &to_conv);
#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
template <WriteMode write_mode>
diff --git a/libc/src/stdio/printf_core/float_impl.cpp b/libc/src/stdio/printf_core/float_impl.cpp
index 47827cb0c3084..1efea9d52dedb 100644
--- a/libc/src/stdio/printf_core/float_impl.cpp
+++ b/libc/src/stdio/printf_core/float_impl.cpp
@@ -16,7 +16,6 @@
#ifdef LIBC_COPT_PRINTF_MODULAR
#include "src/__support/arg_list.h"
-
#define LIBC_PRINTF_DEFINE_MODULES
#include "src/stdio/printf_core/float_dec_converter.h"
#include "src/stdio/printf_core/float_hex_converter.h"
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index 90cb4fd03a147..2d9d70775628a 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -309,8 +309,8 @@ template <typename ArgProvider> class Parser {
}
LIBC_PRINTF_MODULE_DECL void write_float_arg_val(FormatSection §ion,
- LengthModifier lm,
- size_t conv_index);
+ LengthModifier lm,
+ size_t conv_index);
LIBC_PRINTF_MODULE_DECL TypeDesc float_type_desc(LengthModifier lm);
LIBC_PRINTF_MODULE_DECL bool advance_arg_if_float(TypeDesc cur_type_desc);
>From 5ccc53e76753225e48e297bf87a8a0242308fe30 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 29 Jan 2026 16:18:44 -0800
Subject: [PATCH 7/8] Move float_impl.cpp responsibilities to header files
---
libc/src/__support/arg_list_types.def | 13 +++++++
.../stdio/printf_core/float_dec_converter.h | 22 ++++++++++++
.../printf_core/float_dec_converter_limited.h | 22 ++++++++++++
.../stdio/printf_core/float_hex_converter.h | 10 ++++++
libc/src/stdio/printf_core/float_impl.cpp | 35 ++-----------------
libc/src/stdio/printf_core/parser.h | 22 ++++++++++++
libc/src/stdio/printf_core/write_modes.def | 12 +++++++
libc/src/stdio/printf_core/writer.h | 7 ++--
8 files changed, 106 insertions(+), 37 deletions(-)
create mode 100644 libc/src/__support/arg_list_types.def
create mode 100644 libc/src/stdio/printf_core/write_modes.def
diff --git a/libc/src/__support/arg_list_types.def b/libc/src/__support/arg_list_types.def
new file mode 100644
index 0000000000000..33693bbe65ae6
--- /dev/null
+++ b/libc/src/__support/arg_list_types.def
@@ -0,0 +1,13 @@
+//===-- argument list type list ----------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+HANDLE_ARG_LIST_TYPE(ArgList)
+HANDLE_ARG_LIST_TYPE(DummyArgList<false>)
+HANDLE_ARG_LIST_TYPE(DummyArgList<true>)
+HANDLE_ARG_LIST_TYPE(StructArgList<false>)
+HANDLE_ARG_LIST_TYPE(StructArgList<true>)
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index 6221f465a09c4..583570b1e9236 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -1201,6 +1201,28 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
return convert_inf_nan(writer, to_conv);
}
+
+#ifdef LIBC_PRINTF_DEFINE_MODULES
+
+#define HANDLE_WRITE_MODE(MODE) \
+ template int convert_float_decimal<WriteMode::MODE>( \
+ Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
+#include "src/stdio/printf_core/write_modes.def"
+#undef HANDLE_WRITE_MODE
+
+#define HANDLE_WRITE_MODE(MODE) \
+ template int convert_float_dec_exp<WriteMode::MODE>( \
+ Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
+#include "src/stdio/printf_core/write_modes.def"
+#undef HANDLE_WRITE_MODE
+
+#define HANDLE_WRITE_MODE(MODE) \
+ template int convert_float_dec_auto<WriteMode::MODE>( \
+ Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
+#include "src/stdio/printf_core/write_modes.def"
+#undef HANDLE_WRITE_MODE
+
+#endif
#endif
} // namespace printf_core
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 183a09397166a..e63b5a884bb54 100644
--- a/libc/src/stdio/printf_core/float_dec_converter_limited.h
+++ b/libc/src/stdio/printf_core/float_dec_converter_limited.h
@@ -705,6 +705,28 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
const FormatSection &to_conv) {
return convert_float_outer(writer, to_conv, ConversionType::G);
}
+
+#ifdef LIBC_PRINTF_DEFINE_MODULES
+
+#define HANDLE_WRITE_MODE(MODE) \
+ template int convert_float_decimal<WriteMode::MODE>( \
+ Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
+#include "src/stdio/printf_core/write_modes.def"
+#undef HANDLE_WRITE_MODE
+
+#define HANDLE_WRITE_MODE(MODE) \
+ template int convert_float_dec_exp<WriteMode::MODE>( \
+ Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
+#include "src/stdio/printf_core/write_modes.def"
+#undef HANDLE_WRITE_MODE
+
+#define HANDLE_WRITE_MODE(MODE) \
+ template int convert_float_dec_auto<WriteMode::MODE>( \
+ Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
+#include "src/stdio/printf_core/write_modes.def"
+#undef HANDLE_WRITE_MODE
+
+#endif
#endif
} // namespace printf_core
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index 9ae9f0e76477c..1cd4bdeefd6c8 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -258,6 +258,16 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
}
return WRITE_OK;
}
+
+#ifdef LIBC_PRINTF_DEFINE_MODULES
+
+#define HANDLE_WRITE_MODE(MODE) \
+ template int convert_float_hex_exp<WriteMode::MODE>( \
+ Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
+#include "src/stdio/printf_core/write_modes.def"
+#undef HANDLE_WRITE_MODE
+
+#endif
#endif
} // namespace printf_core
diff --git a/libc/src/stdio/printf_core/float_impl.cpp b/libc/src/stdio/printf_core/float_impl.cpp
index 1efea9d52dedb..5b17d7c6ae894 100644
--- a/libc/src/stdio/printf_core/float_impl.cpp
+++ b/libc/src/stdio/printf_core/float_impl.cpp
@@ -14,43 +14,12 @@
//===----------------------------------------------------------------------===//
#ifdef LIBC_COPT_PRINTF_MODULAR
-#include "src/__support/arg_list.h"
#define LIBC_PRINTF_DEFINE_MODULES
-#include "src/stdio/printf_core/float_dec_converter.h"
-#include "src/stdio/printf_core/float_hex_converter.h"
+#include "src/stdio/printf_core/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 2d9d70775628a..b461dfff02c42 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -734,6 +734,28 @@ Parser<ArgParser>::advance_arg_if_float(TypeDesc cur_type_desc) {
return false;
return true;
}
+
+#ifdef LIBC_PRINTF_DEFINE_MODULES
+
+#define HANDLE_ARG_LIST_TYPE(TYPE) \
+ template void Parser<internal::TYPE>::write_float_arg_val( \
+ FormatSection §ion, LengthModifier lm, size_t conv_index);
+#include "src/__support/arg_list_types.def"
+#undef HANDLE_ARG_LIST_TYPE
+
+#define HANDLE_ARG_LIST_TYPE(TYPE) \
+ template TypeDesc Parser<internal::TYPE>::float_type_desc(LengthModifier lm);
+#include "src/__support/arg_list_types.def"
+#undef HANDLE_ARG_LIST_TYPE
+
+#define HANDLE_ARG_LIST_TYPE(TYPE) \
+ template bool Parser<internal::TYPE>::advance_arg_if_float( \
+ TypeDesc cur_type_desc);
+#include "src/__support/arg_list_types.def"
+#undef HANDLE_ARG_LIST_TYPE
+
+#endif
+
#endif
} // namespace printf_core
diff --git a/libc/src/stdio/printf_core/write_modes.def b/libc/src/stdio/printf_core/write_modes.def
new file mode 100644
index 0000000000000..fdce4d0241f99
--- /dev/null
+++ b/libc/src/stdio/printf_core/write_modes.def
@@ -0,0 +1,12 @@
+//===-- printf write mode definitions ----------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+HANDLE_WRITE_MODE(FILL_BUFF_AND_DROP_OVERFLOW)
+HANDLE_WRITE_MODE(FLUSH_TO_STREAM)
+HANDLE_WRITE_MODE(RESIZE_AND_FILL_BUFF)
+HANDLE_WRITE_MODE(RUNTIME_DISPATCH)
diff --git a/libc/src/stdio/printf_core/writer.h b/libc/src/stdio/printf_core/writer.h
index cb45b105597d1..b82792e4d5e02 100644
--- a/libc/src/stdio/printf_core/writer.h
+++ b/libc/src/stdio/printf_core/writer.h
@@ -21,12 +21,11 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
+#define HANDLE_WRITE_MODE(MODE) MODE,
enum class WriteMode {
- FILL_BUFF_AND_DROP_OVERFLOW,
- FLUSH_TO_STREAM,
- RESIZE_AND_FILL_BUFF,
- RUNTIME_DISPATCH,
+#include "src/stdio/printf_core/write_modes.def"
};
+#undef HANDLE_WRITE_MODE
// Helper to omit the template argument if we are using runtime dispatch and
// avoid multiple copies of the converter functions.
>From c7dd4c6c24002fca057bf75d45a656c5748419a5 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Thu, 5 Feb 2026 13:48:24 -0800
Subject: [PATCH 8/8] LIBC_PRINTF_MODULE macro to declare *and* define modules
---
.../stdio/printf_core/float_dec_converter.h | 157 +++---
.../printf_core/float_dec_converter_limited.h | 55 +-
.../stdio/printf_core/float_hex_converter.h | 469 +++++++++---------
libc/src/stdio/printf_core/parser.h | 70 ++-
libc/src/stdio/printf_core/printf_config.h | 16 +-
5 files changed, 376 insertions(+), 391 deletions(-)
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index 583570b1e9236..99cc38adab512 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -1122,88 +1122,87 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
}
}
-template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int convert_float_decimal(Writer<write_mode> *writer,
- const FormatSection &to_conv);
-template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int convert_float_dec_exp(Writer<write_mode> *writer,
- const FormatSection &to_conv);
-template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int
-convert_float_dec_auto(Writer<write_mode> *writer,
- const FormatSection &to_conv);
-
-#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
// TODO: unify the float converters to remove the duplicated checks for inf/nan.
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
- const FormatSection &to_conv) {
- if (to_conv.length_modifier == LengthModifier::L) {
- fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
- fputil::FPBits<long double> float_bits(float_raw);
- if (!float_bits.is_inf_or_nan()) {
- return convert_float_decimal_typed<long double>(writer, to_conv,
- float_bits);
- }
- } else {
- fputil::FPBits<double>::StorageType float_raw =
- static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);
- fputil::FPBits<double> float_bits(float_raw);
- if (!float_bits.is_inf_or_nan()) {
- return convert_float_decimal_typed<double>(writer, to_conv, float_bits);
- }
- }
-
- return convert_inf_nan(writer, to_conv);
-}
-
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
- const FormatSection &to_conv) {
- if (to_conv.length_modifier == LengthModifier::L) {
- fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
- fputil::FPBits<long double> float_bits(float_raw);
- if (!float_bits.is_inf_or_nan()) {
- return convert_float_dec_exp_typed<long double>(writer, to_conv,
- float_bits);
- }
- } else {
- fputil::FPBits<double>::StorageType float_raw =
- static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);
- fputil::FPBits<double> float_bits(float_raw);
- if (!float_bits.is_inf_or_nan()) {
- return convert_float_dec_exp_typed<double>(writer, to_conv, float_bits);
- }
- }
-
- return convert_inf_nan(writer, to_conv);
-}
-
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
- const FormatSection &to_conv) {
- if (to_conv.length_modifier == LengthModifier::L) {
- fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
- fputil::FPBits<long double> float_bits(float_raw);
- if (!float_bits.is_inf_or_nan()) {
- return convert_float_dec_auto_typed<long double>(writer, to_conv,
- float_bits);
- }
- } else {
- fputil::FPBits<double>::StorageType float_raw =
- static_cast<fputil::FPBits<double>::StorageType>(to_conv.conv_val_raw);
- fputil::FPBits<double> float_bits(float_raw);
- if (!float_bits.is_inf_or_nan()) {
- return convert_float_dec_auto_typed<double>(writer, to_conv, float_bits);
- }
- }
-
- return convert_inf_nan(writer, to_conv);
-}
+LIBC_PRINTF_MODULE((template <WriteMode write_mode>
+ int convert_float_decimal(Writer<write_mode> *writer,
+ const FormatSection &to_conv)),
+ {
+ if (to_conv.length_modifier == LengthModifier::L) {
+ fputil::FPBits<long double>::StorageType float_raw =
+ to_conv.conv_val_raw;
+ fputil::FPBits<long double> float_bits(float_raw);
+ if (!float_bits.is_inf_or_nan()) {
+ return convert_float_decimal_typed<long double>(
+ writer, to_conv, float_bits);
+ }
+ } else {
+ fputil::FPBits<double>::StorageType float_raw =
+ static_cast<fputil::FPBits<double>::StorageType>(
+ to_conv.conv_val_raw);
+ fputil::FPBits<double> float_bits(float_raw);
+ if (!float_bits.is_inf_or_nan()) {
+ return convert_float_decimal_typed<double>(
+ writer, to_conv, float_bits);
+ }
+ }
+
+ return convert_inf_nan(writer, to_conv);
+ })
+
+LIBC_PRINTF_MODULE((template <WriteMode write_mode>
+ int convert_float_dec_exp(Writer<write_mode> *writer,
+ const FormatSection &to_conv)),
+ {
+ if (to_conv.length_modifier == LengthModifier::L) {
+ fputil::FPBits<long double>::StorageType float_raw =
+ to_conv.conv_val_raw;
+ fputil::FPBits<long double> float_bits(float_raw);
+ if (!float_bits.is_inf_or_nan()) {
+ return convert_float_dec_exp_typed<long double>(
+ writer, to_conv, float_bits);
+ }
+ } else {
+ fputil::FPBits<double>::StorageType float_raw =
+ static_cast<fputil::FPBits<double>::StorageType>(
+ to_conv.conv_val_raw);
+ fputil::FPBits<double> float_bits(float_raw);
+ if (!float_bits.is_inf_or_nan()) {
+ return convert_float_dec_exp_typed<double>(
+ writer, to_conv, float_bits);
+ }
+ }
+
+ return convert_inf_nan(writer, to_conv);
+ })
+
+LIBC_PRINTF_MODULE((template <WriteMode write_mode>
+ int convert_float_dec_auto(Writer<write_mode> *writer,
+ const FormatSection &to_conv)),
+ {
+ if (to_conv.length_modifier == LengthModifier::L) {
+ fputil::FPBits<long double>::StorageType float_raw =
+ to_conv.conv_val_raw;
+ fputil::FPBits<long double> float_bits(float_raw);
+ if (!float_bits.is_inf_or_nan()) {
+ return convert_float_dec_auto_typed<long double>(
+ writer, to_conv, float_bits);
+ }
+ } else {
+ fputil::FPBits<double>::StorageType float_raw =
+ static_cast<fputil::FPBits<double>::StorageType>(
+ to_conv.conv_val_raw);
+ fputil::FPBits<double> float_bits(float_raw);
+ if (!float_bits.is_inf_or_nan()) {
+ return convert_float_dec_auto_typed<double>(
+ writer, to_conv, float_bits);
+ }
+ }
+
+ return convert_inf_nan(writer, to_conv);
+ })
#ifdef LIBC_PRINTF_DEFINE_MODULES
-
#define HANDLE_WRITE_MODE(MODE) \
template int convert_float_decimal<WriteMode::MODE>( \
Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
@@ -1221,8 +1220,6 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
#include "src/stdio/printf_core/write_modes.def"
#undef HANDLE_WRITE_MODE
-
-#endif
#endif
} // namespace printf_core
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 e63b5a884bb54..6d393a7f95ce4 100644
--- a/libc/src/stdio/printf_core/float_dec_converter_limited.h
+++ b/libc/src/stdio/printf_core/float_dec_converter_limited.h
@@ -676,38 +676,31 @@ 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_MODULE_DECL int convert_float_decimal(Writer<write_mode> *writer,
- const FormatSection &to_conv);
-template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int convert_float_dec_exp(Writer<write_mode> *writer,
- const FormatSection &to_conv);
-template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int
-convert_float_dec_auto(Writer<write_mode> *writer,
- const FormatSection &to_conv);
-
-#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
- const FormatSection &to_conv) {
- return convert_float_outer(writer, to_conv, ConversionType::F);
-}
-
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
- const FormatSection &to_conv) {
- return convert_float_outer(writer, to_conv, ConversionType::E);
-}
-
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
- const FormatSection &to_conv) {
- return convert_float_outer(writer, to_conv, ConversionType::G);
-}
+LIBC_PRINTF_MODULE((template <WriteMode write_mode>
+ int convert_float_decimal(Writer<write_mode> *writer,
+ const FormatSection &to_conv)),
+ {
+ return convert_float_outer(writer, to_conv,
+ ConversionType::F);
+ })
+
+LIBC_PRINTF_MODULE((template <WriteMode write_mode>
+ int convert_float_dec_exp(Writer<write_mode> *writer,
+ const FormatSection &to_conv)),
+ {
+ return convert_float_outer(writer, to_conv,
+ ConversionType::E);
+ })
+
+LIBC_PRINTF_MODULE((template <WriteMode write_mode>
+ int convert_float_dec_auto(Writer<write_mode> *writer,
+ const FormatSection &to_conv)),
+ {
+ return convert_float_outer(writer, to_conv,
+ ConversionType::G);
+ })
#ifdef LIBC_PRINTF_DEFINE_MODULES
-
#define HANDLE_WRITE_MODE(MODE) \
template int convert_float_decimal<WriteMode::MODE>( \
Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
@@ -725,8 +718,6 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
#include "src/stdio/printf_core/write_modes.def"
#undef HANDLE_WRITE_MODE
-
-#endif
#endif
} // namespace printf_core
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index 1cd4bdeefd6c8..de42c4d72676b 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -25,249 +25,246 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
-template <WriteMode write_mode>
-LIBC_PRINTF_MODULE_DECL int convert_float_hex_exp(Writer<write_mode> *writer,
- const FormatSection &to_conv);
-
-#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
- const FormatSection &to_conv) {
- using LDBits = fputil::FPBits<long double>;
- using StorageType = LDBits::StorageType;
-
- bool is_negative;
- int exponent;
- StorageType mantissa;
- bool is_inf_or_nan;
- uint32_t fraction_bits;
- if (to_conv.length_modifier == LengthModifier::L) {
- fraction_bits = LDBits::FRACTION_LEN;
- LDBits::StorageType float_raw = to_conv.conv_val_raw;
- LDBits float_bits(float_raw);
- is_negative = float_bits.is_neg();
- exponent = float_bits.get_explicit_exponent();
- mantissa = float_bits.get_explicit_mantissa();
- is_inf_or_nan = float_bits.is_inf_or_nan();
- } else {
- using LBits = fputil::FPBits<double>;
- fraction_bits = LBits::FRACTION_LEN;
- LBits::StorageType float_raw =
- static_cast<LBits::StorageType>(to_conv.conv_val_raw);
- LBits float_bits(float_raw);
- is_negative = float_bits.is_neg();
- exponent = float_bits.get_explicit_exponent();
- mantissa = float_bits.get_explicit_mantissa();
- is_inf_or_nan = float_bits.is_inf_or_nan();
- }
-
- if (is_inf_or_nan)
- return convert_inf_nan(writer, to_conv);
-
- char sign_char = 0;
-
- if (is_negative)
- sign_char = '-';
- else if ((to_conv.flags & FormatFlags::FORCE_SIGN) == FormatFlags::FORCE_SIGN)
- sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
- else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) ==
- FormatFlags::SPACE_PREFIX)
- sign_char = ' ';
-
- constexpr size_t BITS_IN_HEX_DIGIT = 4;
-
- // This is to handle situations where the mantissa isn't an even number of hex
- // digits. This is primarily relevant for x86 80 bit long doubles, which have
- // 63 bit mantissas. In the case where the mantissa is 0, however, the
- // exponent should stay as 0.
- if (fraction_bits % BITS_IN_HEX_DIGIT != 0 && mantissa > 0) {
- exponent -= fraction_bits % BITS_IN_HEX_DIGIT;
- }
-
- // This is the max number of digits it can take to represent the mantissa.
- // Since the number is in bits, we divide by 4, and then add one to account
- // for the extra implicit bit. We use the larger of the two possible values
- // since the size must be constant.
- constexpr size_t MANT_BUFF_LEN =
- (LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
- char mant_buffer[MANT_BUFF_LEN];
-
- size_t mant_len = (fraction_bits / BITS_IN_HEX_DIGIT) + 1;
-
- // Precision only tracks the number of digits after the hexadecimal point, so
- // we have to add one to account for the digit before the hexadecimal point.
- if (to_conv.precision + 1 < static_cast<int>(mant_len) &&
- to_conv.precision + 1 > 0) {
- const size_t intended_digits = to_conv.precision + 1;
- const size_t shift_amount =
- (mant_len - intended_digits) * BITS_IN_HEX_DIGIT;
-
- const StorageType truncated_bits =
- mantissa & ((StorageType(1) << shift_amount) - 1);
- const StorageType halfway_const = StorageType(1) << (shift_amount - 1);
-
- mantissa >>= shift_amount;
-
- switch (fputil::quick_get_round()) {
- case FE_TONEAREST:
- // Round to nearest, if it's exactly halfway then round to even.
- if (truncated_bits > halfway_const)
- ++mantissa;
- else if (truncated_bits == halfway_const)
- mantissa = mantissa + (mantissa & 1);
- break;
- case FE_DOWNWARD:
- if (truncated_bits > 0 && is_negative)
- ++mantissa;
- break;
- case FE_UPWARD:
- if (truncated_bits > 0 && !is_negative)
- ++mantissa;
- break;
- case FE_TOWARDZERO:
- break;
- }
-
- // If the rounding caused an overflow, shift the mantissa and adjust the
- // exponent to match.
- if (mantissa >= (StorageType(1) << (intended_digits * BITS_IN_HEX_DIGIT))) {
- mantissa >>= BITS_IN_HEX_DIGIT;
- exponent += BITS_IN_HEX_DIGIT;
- }
-
- mant_len = intended_digits;
- }
-
- size_t mant_cur = mant_len;
- size_t first_non_zero = 1;
- for (; mant_cur > 0; --mant_cur, mantissa >>= 4) {
- char mant_mod_16 = static_cast<char>(mantissa % 16);
- char new_digit = internal::int_to_b36_char(mant_mod_16);
- if (internal::isupper(to_conv.conv_name))
- new_digit = internal::toupper(new_digit);
- mant_buffer[mant_cur - 1] = new_digit;
- if (new_digit != '0' && first_non_zero < mant_cur)
- first_non_zero = mant_cur;
- }
-
- size_t mant_digits = first_non_zero;
- if (to_conv.precision >= 0)
- mant_digits = mant_len;
-
- // This approximates the number of digits it will take to represent the
- // exponent. The calculation is ceil((bits * 5) / 16). Floor also works, but
- // only on exact multiples of 16. We add 1 for the sign.
- // Relevant sizes:
- // 15 -> 5
- // 11 -> 4
- // 8 -> 3
- constexpr size_t EXP_LEN = (((LDBits::EXP_LEN * 5) + 15) / 16) + 1;
- char exp_buffer[EXP_LEN];
-
- bool exp_is_negative = false;
- if (exponent < 0) {
- exp_is_negative = true;
- exponent = -exponent;
- }
-
- size_t exp_cur = EXP_LEN;
- for (; exponent > 0; --exp_cur, exponent /= 10) {
- exp_buffer[exp_cur - 1] = internal::int_to_b36_char(exponent % 10);
- }
- if (exp_cur == EXP_LEN) { // if nothing else was written, write a 0.
- exp_buffer[EXP_LEN - 1] = '0';
- exp_cur = EXP_LEN - 1;
- }
-
- exp_buffer[exp_cur - 1] = exp_is_negative ? '-' : '+';
- --exp_cur;
-
- // these are signed to prevent underflow due to negative values. The eventual
- // values will always be non-negative.
- size_t trailing_zeroes = 0;
- int padding;
-
- // prefix is "0x", and always appears.
- constexpr size_t PREFIX_LEN = 2;
- char prefix[PREFIX_LEN];
- prefix[0] = '0';
- prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X';
- const cpp::string_view prefix_str(prefix, PREFIX_LEN);
-
- // If the precision is greater than the actual result, pad with 0s
- if (to_conv.precision > static_cast<int>(mant_digits - 1))
- trailing_zeroes = to_conv.precision - (mant_digits - 1);
-
- bool has_hexadecimal_point =
- (mant_digits > 1) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) ==
- FormatFlags::ALTERNATE_FORM);
- constexpr cpp::string_view HEXADECIMAL_POINT(".");
-
- // This is for the letter 'p' before the exponent.
- const char exp_separator = internal::islower(to_conv.conv_name) ? 'p' : 'P';
- constexpr int EXP_SEPARATOR_LEN = 1;
-
- padding = static_cast<int>(to_conv.min_width - (sign_char > 0 ? 1 : 0) -
- PREFIX_LEN - mant_digits - trailing_zeroes -
- static_cast<int>(has_hexadecimal_point) -
- EXP_SEPARATOR_LEN - (EXP_LEN - exp_cur));
- if (padding < 0)
- padding = 0;
-
- if ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
- FormatFlags::LEFT_JUSTIFIED) {
- // The pattern is (sign), 0x, digit, (.), (other digits), (zeroes), p,
- // exponent, (spaces)
- if (sign_char > 0)
- RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
- RET_IF_RESULT_NEGATIVE(writer->write(prefix_str));
- RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer[0]));
- if (has_hexadecimal_point)
- RET_IF_RESULT_NEGATIVE(writer->write(HEXADECIMAL_POINT));
- if (mant_digits > 1)
- RET_IF_RESULT_NEGATIVE(writer->write({mant_buffer + 1, mant_digits - 1}));
- if (trailing_zeroes > 0)
- RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
- RET_IF_RESULT_NEGATIVE(writer->write(exp_separator));
- RET_IF_RESULT_NEGATIVE(
- writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur}));
- if (padding > 0)
- RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
- } else {
- // The pattern is (spaces), (sign), 0x, (zeroes), digit, (.), (other
- // digits), (zeroes), p, exponent
- if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) !=
- FormatFlags::LEADING_ZEROES))
- RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
- if (sign_char > 0)
- RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
- RET_IF_RESULT_NEGATIVE(writer->write(prefix_str));
- if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) ==
- FormatFlags::LEADING_ZEROES))
- RET_IF_RESULT_NEGATIVE(writer->write('0', padding));
- RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer[0]));
- if (has_hexadecimal_point)
- RET_IF_RESULT_NEGATIVE(writer->write(HEXADECIMAL_POINT));
- if (mant_digits > 1)
- RET_IF_RESULT_NEGATIVE(writer->write({mant_buffer + 1, mant_digits - 1}));
- if (trailing_zeroes > 0)
- RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
- RET_IF_RESULT_NEGATIVE(writer->write(exp_separator));
- RET_IF_RESULT_NEGATIVE(
- writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur}));
- }
- return WRITE_OK;
-}
+LIBC_PRINTF_MODULE(
+ (template <WriteMode write_mode>
+ int convert_float_hex_exp(Writer<write_mode> *writer,
+ const FormatSection &to_conv)),
+ {
+ using LDBits = fputil::FPBits<long double>;
+ using StorageType = LDBits::StorageType;
+
+ bool is_negative;
+ int exponent;
+ StorageType mantissa;
+ bool is_inf_or_nan;
+ uint32_t fraction_bits;
+ if (to_conv.length_modifier == LengthModifier::L) {
+ fraction_bits = LDBits::FRACTION_LEN;
+ LDBits::StorageType float_raw = to_conv.conv_val_raw;
+ LDBits float_bits(float_raw);
+ is_negative = float_bits.is_neg();
+ exponent = float_bits.get_explicit_exponent();
+ mantissa = float_bits.get_explicit_mantissa();
+ is_inf_or_nan = float_bits.is_inf_or_nan();
+ } else {
+ using LBits = fputil::FPBits<double>;
+ fraction_bits = LBits::FRACTION_LEN;
+ LBits::StorageType float_raw =
+ static_cast<LBits::StorageType>(to_conv.conv_val_raw);
+ LBits float_bits(float_raw);
+ is_negative = float_bits.is_neg();
+ exponent = float_bits.get_explicit_exponent();
+ mantissa = float_bits.get_explicit_mantissa();
+ is_inf_or_nan = float_bits.is_inf_or_nan();
+ }
+
+ if (is_inf_or_nan)
+ return convert_inf_nan(writer, to_conv);
+
+ char sign_char = 0;
+
+ if (is_negative)
+ sign_char = '-';
+ else if ((to_conv.flags & FormatFlags::FORCE_SIGN) ==
+ FormatFlags::FORCE_SIGN)
+ sign_char = '+'; // FORCE_SIGN has precedence over SPACE_PREFIX
+ else if ((to_conv.flags & FormatFlags::SPACE_PREFIX) ==
+ FormatFlags::SPACE_PREFIX)
+ sign_char = ' ';
+
+ constexpr size_t BITS_IN_HEX_DIGIT = 4;
+
+ // This is to handle situations where the mantissa isn't an even number of
+ // hex digits. This is primarily relevant for x86 80 bit long doubles,
+ // which have 63 bit mantissas. In the case where the mantissa is 0,
+ // however, the exponent should stay as 0.
+ if (fraction_bits % BITS_IN_HEX_DIGIT != 0 && mantissa > 0) {
+ exponent -= fraction_bits % BITS_IN_HEX_DIGIT;
+ }
+
+ // This is the max number of digits it can take to represent the mantissa.
+ // Since the number is in bits, we divide by 4, and then add one to
+ // account for the extra implicit bit. We use the larger of the two
+ // possible values since the size must be constant.
+ constexpr size_t MANT_BUFF_LEN =
+ (LDBits::FRACTION_LEN / BITS_IN_HEX_DIGIT) + 1;
+ char mant_buffer[MANT_BUFF_LEN];
+
+ size_t mant_len = (fraction_bits / BITS_IN_HEX_DIGIT) + 1;
+
+ // Precision only tracks the number of digits after the hexadecimal point,
+ // so we have to add one to account for the digit before the hexadecimal
+ // point.
+ if (to_conv.precision + 1 < static_cast<int>(mant_len) &&
+ to_conv.precision + 1 > 0) {
+ const size_t intended_digits = to_conv.precision + 1;
+ const size_t shift_amount =
+ (mant_len - intended_digits) * BITS_IN_HEX_DIGIT;
+
+ const StorageType truncated_bits =
+ mantissa & ((StorageType(1) << shift_amount) - 1);
+ const StorageType halfway_const = StorageType(1) << (shift_amount - 1);
+
+ mantissa >>= shift_amount;
+
+ switch (fputil::quick_get_round()) {
+ case FE_TONEAREST:
+ // Round to nearest, if it's exactly halfway then round to even.
+ if (truncated_bits > halfway_const)
+ ++mantissa;
+ else if (truncated_bits == halfway_const)
+ mantissa = mantissa + (mantissa & 1);
+ break;
+ case FE_DOWNWARD:
+ if (truncated_bits > 0 && is_negative)
+ ++mantissa;
+ break;
+ case FE_UPWARD:
+ if (truncated_bits > 0 && !is_negative)
+ ++mantissa;
+ break;
+ case FE_TOWARDZERO:
+ break;
+ }
+
+ // If the rounding caused an overflow, shift the mantissa and adjust the
+ // exponent to match.
+ if (mantissa >=
+ (StorageType(1) << (intended_digits * BITS_IN_HEX_DIGIT))) {
+ mantissa >>= BITS_IN_HEX_DIGIT;
+ exponent += BITS_IN_HEX_DIGIT;
+ }
+
+ mant_len = intended_digits;
+ }
+
+ size_t mant_cur = mant_len;
+ size_t first_non_zero = 1;
+ for (; mant_cur > 0; --mant_cur, mantissa >>= 4) {
+ char mant_mod_16 = static_cast<char>(mantissa % 16);
+ char new_digit = internal::int_to_b36_char(mant_mod_16);
+ if (internal::isupper(to_conv.conv_name))
+ new_digit = internal::toupper(new_digit);
+ mant_buffer[mant_cur - 1] = new_digit;
+ if (new_digit != '0' && first_non_zero < mant_cur)
+ first_non_zero = mant_cur;
+ }
+
+ size_t mant_digits = first_non_zero;
+ if (to_conv.precision >= 0)
+ mant_digits = mant_len;
+
+ // This approximates the number of digits it will take to represent the
+ // exponent. The calculation is ceil((bits * 5) / 16). Floor also works,
+ // but only on exact multiples of 16. We add 1 for the sign. Relevant
+ // sizes: 15 -> 5 11 -> 4 8 -> 3
+ constexpr size_t EXP_LEN = (((LDBits::EXP_LEN * 5) + 15) / 16) + 1;
+ char exp_buffer[EXP_LEN];
+
+ bool exp_is_negative = false;
+ if (exponent < 0) {
+ exp_is_negative = true;
+ exponent = -exponent;
+ }
+
+ size_t exp_cur = EXP_LEN;
+ for (; exponent > 0; --exp_cur, exponent /= 10) {
+ exp_buffer[exp_cur - 1] = internal::int_to_b36_char(exponent % 10);
+ }
+ if (exp_cur == EXP_LEN) { // if nothing else was written, write a 0.
+ exp_buffer[EXP_LEN - 1] = '0';
+ exp_cur = EXP_LEN - 1;
+ }
+
+ exp_buffer[exp_cur - 1] = exp_is_negative ? '-' : '+';
+ --exp_cur;
+
+ // these are signed to prevent underflow due to negative values. The
+ // eventual values will always be non-negative.
+ size_t trailing_zeroes = 0;
+ int padding;
+
+ // prefix is "0x", and always appears.
+ constexpr size_t PREFIX_LEN = 2;
+ char prefix[PREFIX_LEN];
+ prefix[0] = '0';
+ prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X';
+ const cpp::string_view prefix_str(prefix, PREFIX_LEN);
+
+ // If the precision is greater than the actual result, pad with 0s
+ if (to_conv.precision > static_cast<int>(mant_digits - 1))
+ trailing_zeroes = to_conv.precision - (mant_digits - 1);
+
+ bool has_hexadecimal_point =
+ (mant_digits > 1) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) ==
+ FormatFlags::ALTERNATE_FORM);
+ constexpr cpp::string_view HEXADECIMAL_POINT(".");
+
+ // This is for the letter 'p' before the exponent.
+ const char exp_separator =
+ internal::islower(to_conv.conv_name) ? 'p' : 'P';
+ constexpr int EXP_SEPARATOR_LEN = 1;
+
+ padding = static_cast<int>(to_conv.min_width - (sign_char > 0 ? 1 : 0) -
+ PREFIX_LEN - mant_digits - trailing_zeroes -
+ static_cast<int>(has_hexadecimal_point) -
+ EXP_SEPARATOR_LEN - (EXP_LEN - exp_cur));
+ if (padding < 0)
+ padding = 0;
+
+ if ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
+ FormatFlags::LEFT_JUSTIFIED) {
+ // The pattern is (sign), 0x, digit, (.), (other digits), (zeroes), p,
+ // exponent, (spaces)
+ if (sign_char > 0)
+ RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
+ RET_IF_RESULT_NEGATIVE(writer->write(prefix_str));
+ RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer[0]));
+ if (has_hexadecimal_point)
+ RET_IF_RESULT_NEGATIVE(writer->write(HEXADECIMAL_POINT));
+ if (mant_digits > 1)
+ RET_IF_RESULT_NEGATIVE(
+ writer->write({mant_buffer + 1, mant_digits - 1}));
+ if (trailing_zeroes > 0)
+ RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
+ RET_IF_RESULT_NEGATIVE(writer->write(exp_separator));
+ RET_IF_RESULT_NEGATIVE(
+ writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur}));
+ if (padding > 0)
+ RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
+ } else {
+ // The pattern is (spaces), (sign), 0x, (zeroes), digit, (.), (other
+ // digits), (zeroes), p, exponent
+ if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) !=
+ FormatFlags::LEADING_ZEROES))
+ RET_IF_RESULT_NEGATIVE(writer->write(' ', padding));
+ if (sign_char > 0)
+ RET_IF_RESULT_NEGATIVE(writer->write(sign_char));
+ RET_IF_RESULT_NEGATIVE(writer->write(prefix_str));
+ if ((padding > 0) && ((to_conv.flags & FormatFlags::LEADING_ZEROES) ==
+ FormatFlags::LEADING_ZEROES))
+ RET_IF_RESULT_NEGATIVE(writer->write('0', padding));
+ RET_IF_RESULT_NEGATIVE(writer->write(mant_buffer[0]));
+ if (has_hexadecimal_point)
+ RET_IF_RESULT_NEGATIVE(writer->write(HEXADECIMAL_POINT));
+ if (mant_digits > 1)
+ RET_IF_RESULT_NEGATIVE(
+ writer->write({mant_buffer + 1, mant_digits - 1}));
+ if (trailing_zeroes > 0)
+ RET_IF_RESULT_NEGATIVE(writer->write('0', trailing_zeroes));
+ RET_IF_RESULT_NEGATIVE(writer->write(exp_separator));
+ RET_IF_RESULT_NEGATIVE(
+ writer->write({exp_buffer + exp_cur, EXP_LEN - exp_cur}));
+ }
+ return WRITE_OK;
+ })
#ifdef LIBC_PRINTF_DEFINE_MODULES
-
#define HANDLE_WRITE_MODE(MODE) \
template int convert_float_hex_exp<WriteMode::MODE>( \
Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
#include "src/stdio/printf_core/write_modes.def"
#undef HANDLE_WRITE_MODE
-
-#endif
#endif
} // namespace printf_core
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index b461dfff02c42..4667992c4a8b6 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -14,6 +14,7 @@
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/type_traits.h"
+#include "src/__support/arg_list.h"
#include "src/__support/macros/config.h"
#include "src/__support/str_to_integer.h"
#include "src/stdio/printf_core/core_structs.h"
@@ -308,11 +309,33 @@ template <typename ArgProvider> class Parser {
return section;
}
- LIBC_PRINTF_MODULE_DECL void write_float_arg_val(FormatSection §ion,
- LengthModifier lm,
- size_t conv_index);
- LIBC_PRINTF_MODULE_DECL TypeDesc float_type_desc(LengthModifier lm);
- LIBC_PRINTF_MODULE_DECL bool advance_arg_if_float(TypeDesc cur_type_desc);
+ LIBC_PRINTF_MODULE(
+ (void 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);
+ }
+ })
+
+ LIBC_PRINTF_MODULE((TypeDesc float_type_desc(LengthModifier lm)), {
+ if (lm != LengthModifier::L)
+ return type_desc_from_type<double>();
+ else
+ return type_desc_from_type<long double>();
+ })
+
+ LIBC_PRINTF_MODULE((bool 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;
+ })
private:
// parse_flags parses the flags inside a format string. It assumes that
@@ -702,41 +725,7 @@ template <typename ArgProvider> class Parser {
#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
};
-#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
-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;
-}
-
#ifdef LIBC_PRINTF_DEFINE_MODULES
-
#define HANDLE_ARG_LIST_TYPE(TYPE) \
template void Parser<internal::TYPE>::write_float_arg_val( \
FormatSection §ion, LengthModifier lm, size_t conv_index);
@@ -753,9 +742,6 @@ Parser<ArgParser>::advance_arg_if_float(TypeDesc cur_type_desc) {
TypeDesc cur_type_desc);
#include "src/__support/arg_list_types.def"
#undef HANDLE_ARG_LIST_TYPE
-
-#endif
-
#endif
} // namespace printf_core
diff --git a/libc/src/stdio/printf_core/printf_config.h b/libc/src/stdio/printf_core/printf_config.h
index bb4be8bdd6b1c..5be382a34e619 100644
--- a/libc/src/stdio/printf_core/printf_config.h
+++ b/libc/src/stdio/printf_core/printf_config.h
@@ -49,9 +49,23 @@
// LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
#ifdef LIBC_COPT_PRINTF_MODULAR
-#define LIBC_PRINTF_MODULE_DECL [[gnu::weak]]
+#define LIBC_PRINTF_MODULE_DECL __attribute__((weak))
#else
#define LIBC_PRINTF_MODULE_DECL LIBC_INLINE
#endif
+// LIBC_PRINTF_MODULE: Defines/declares a printf module.
+//
+// Usage: LIBC_PRINTF_MODULE((<signature>), { <body> })
+//
+// Note that the signature is parenthesized, but the body is not.
+
+#define LIBC_PRINTF_MODULE_UNWRAP(...) __VA_ARGS__
+#if !defined(LIBC_COPT_PRINTF_MODULAR) || defined(LIBC_PRINTF_DEFINE_MODULES)
+#define LIBC_PRINTF_MODULE(SIG, ...) LIBC_PRINTF_MODULE_UNWRAP SIG __VA_ARGS__
+#else
+#define LIBC_PRINTF_MODULE(SIG, ...) \
+ LIBC_PRINTF_MODULE_UNWRAP SIG LIBC_PRINTF_MODULE_DECL;
+#endif
+
#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PRINTF_CONFIG_H
More information about the cfe-commits
mailing list