[libc] Modular printf option (float only) (#147426)

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.
This commit is contained in:
Daniel Thornburgh 2026-03-19 14:22:03 -07:00 committed by GitHub
parent 4e19eee8a6
commit 7efcd6198c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 735 additions and 27 deletions

View File

@ -9854,9 +9854,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

View File

@ -59,6 +59,10 @@
"LIBC_COPT_PRINTF_DISABLE_BITINT": {
"value": false,
"doc": "Disable bitint length modifiers to reduce code size. Specifically the wNUM and wfNUM modifiers."
},
"LIBC_CONF_PRINTF_MODULAR": {
"value": false,
"doc": "Split printf implementation into modules that can be lazily linked in."
}
},
"scanf": {

View File

@ -22,3 +22,4 @@ Navigate to the links below for information on the respective topics:
undefined_behavior
printf_behavior
syscall_wrapper_refactor
modular_format

View File

@ -0,0 +1,68 @@
.. _modular_format:
======================
Modular format strings
======================
Introduction
============
Several C standard library functions (most notably, ``printf`` and ``scanf``),
present a large amount of related features to the caller configured via a
format string. This benefits code size at the caller, since format strings are
typically quite dense, and the equivalent of many individual calls can be
performed with only one. Overall this is a benefit, since to a function calls
typically outnumber the one definition of that function.
However, the implementations of various libc features gated behind aspects of
those format strings can be large enough that they completely swamp the
programs that call them. Floating point and errno conversion in particular can
involve large tables which may be wholly dead. However, due to the format
string structure, this code is dead in a way previously invisible to the
compiler.
To address this, an clang attribute was introduced: ``modular_format(<impl_fn>,
<impl_name>, <aspects>...)``. This adds to the semantics of the existing
``format`` attribute (which must also be present, if implicitly.) The first
argument is a symbol naming a modular version of the implementation; this
version only weakly refers to "aspects" of the implementation that may not be
necessary for certain format strings. The second argument is general
"implementation name" string, and the remaining arguments are a list of handled
aspects of the format string. When the compiler sees that a given call only
needs a fixed set of aspects of the implementation, it may redirect the call to
the implementation function and emit a series of relocations to symbols named
``<impl_name>_<aspect>``. These in turn bring the needed aspects of the call
into the link. The default entrypoints fall the modular ones, except they bring
in every possible implementation aspect.
Mechanism
=========
This functionality is currently gated behind ``LIBC_COPT_PRINTF_MODULAR``. When
set, the ``printf``-family functions gain modular variants, and the regular
variants are modified to call them and emit NONE relocations against all
implementation aspects.
The implementation aspects are defined in headers using the
``LIBC_PRINTF_MODULE((<decl>), { <body> })`` macro. If
``LIBC_COPT_PRINTF_MODULAR`` is not defined, then this macro makes these
``LIBC_INLINE`` definitions as per usual. Otherwise, for normal usage, these
become weak declarations, which causes any references to the module to become
weak. The implementations are moved to a dedicated impl file for groups of
modules. These define the aspect symbol and the module impls by defining
``LIBC_PRINTF_DEFINE_MODULES`` before including the header. This causes the to
be brought into the link exactly when the aspect symbol is referenced.
Template functions present a special complication: the implementation must
instantiate them for any value that may be used. Since the purpose of the
templates is to implement a fixed interface, the possible arguments should
always be fixed and finite. Accordingly, libc contains def files to enumerate
possible arguments and provide handling for each. Templates are instantiated in
the headers whenever ``LIBC_PRINTF_DEFINE_MODULES`` is defined.
libc and the compiler may understand different sets of aspect names, but their
understanding of what an aspect name means must be identical. libc reports the
set of aspect names that it needs a verdict on, and the compiler will only
provide a verdict for those aspects. If libc asks for a verdict on an aspect
unknown to the compiler, the aspect must be summarily considered to be
required.

View File

@ -360,6 +360,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

View File

@ -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}
)
@ -395,3 +400,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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -211,10 +211,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
@ -345,10 +349,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

View File

@ -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

View File

@ -0,0 +1,33 @@
//===-- 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

View File

@ -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

View File

@ -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,12 @@ 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 = [&] {
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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()
@ -150,10 +153,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

View File

@ -28,6 +28,37 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
LIBC_PRINTF_MODULE((template <WriteMode write_mode>
int convert_float(Writer<write_mode> *writer,
const FormatSection &to_conv)),
{
switch (to_conv.conv_name) {
case 'f':
case 'F':
return convert_float_decimal(writer, to_conv);
case 'e':
case 'E':
return convert_float_dec_exp(writer, to_conv);
case 'a':
case 'A':
return convert_float_hex_exp(writer, to_conv);
case 'g':
case 'G':
return convert_float_dec_auto(writer, to_conv);
}
__builtin_unreachable();
})
#endif // not LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_PRINTF_DEFINE_MODULES
#define HANDLE_WRITE_MODE(MODE) \
template int convert_float<WriteMode::MODE>( \
Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
#include "src/stdio/printf_core/write_modes.def"
#undef HANDLE_WRITE_MODE
#endif // LIBC_PRINTF_DEFINE_MODULES
// convert will call a conversion function to convert the FormatSection into
// its string representation, and then that will write the result to the
// writer.
@ -72,16 +103,13 @@ int convert(Writer<write_mode> *writer, const FormatSection &to_conv) {
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
case 'f':
case 'F':
return convert_float_decimal(writer, to_conv);
case 'e':
case 'E':
return convert_float_dec_exp(writer, to_conv);
case 'a':
case 'A':
return convert_float_hex_exp(writer, to_conv);
case 'g':
case 'G':
return convert_float_dec_auto(writer, to_conv);
return convert_float(writer, to_conv);
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
case 'r':

View File

@ -0,0 +1,24 @@
//===----------------------------------------------------------------------===//
//
// 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
#define LIBC_PRINTF_DEFINE_MODULES
#include "src/stdio/printf_core/converter.h"
// Bring this file into the link if __printf_float is referenced.
extern "C" void __printf_float() {}
#endif // LIBC_COPT_PRINTF_MODULAR

View File

@ -48,4 +48,24 @@
// LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
#ifdef LIBC_COPT_PRINTF_MODULAR
#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

View File

@ -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

View File

@ -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,12 @@ 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 = [&] {
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;

View File

@ -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)

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -177,7 +177,7 @@ class HeaderFile:
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
}
)

View File

@ -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;

View File

@ -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",

View File

@ -12,6 +12,7 @@
#ifdef LIBC_FULL_BUILD
#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"

View File

@ -17,3 +17,4 @@ functions:
- stdc
attributes:
- CONST_FUNC_A
- MACRO_ATTR(A)