From e6dd4bd6080c48fcd6ee4f6c2ce743a907f61151 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Thu, 19 Dec 2024 11:57:27 -0800 Subject: [PATCH] [libc] Modular printf option (float only) This adds LIBC_CONF_PRINTF_MODULAR, which causes floating point support (later, others) to be weakly linked into the implementation. __printf_modular becomes the main entry point of the implementaiton, an printf itself wraps __printf_modular. printf it also contains a BFD_RELOC_NONE relocation to bring in the float aspect. See issue #146159 for context. --- libc/config/config.json | 4 ++ libc/docs/configure.rst | 1 + libc/src/stdio/generic/CMakeLists.txt | 7 ++- libc/src/stdio/generic/printf_modular.cpp | 40 +++++++++++++ libc/src/stdio/printf.h | 1 + libc/src/stdio/printf_core/CMakeLists.txt | 7 ++- .../stdio/printf_core/float_dec_converter.h | 25 +++++++-- .../printf_core/float_dec_converter_limited.h | 24 ++++++-- .../stdio/printf_core/float_hex_converter.h | 10 +++- libc/src/stdio/printf_core/float_impl.cpp | 41 ++++++++++++++ libc/src/stdio/printf_core/parser.h | 56 ++++++++++++++----- libc/src/stdio/printf_core/printf_config.h | 7 +++ libc/src/stdio/printf_core/printf_main.h | 13 ++++- .../src/stdio/printf_core/vfprintf_internal.h | 13 ++++- 14 files changed, 216 insertions(+), 33 deletions(-) create mode 100644 libc/src/stdio/generic/printf_modular.cpp create mode 100644 libc/src/stdio/printf_core/float_impl.cpp diff --git a/libc/config/config.json b/libc/config/config.json index d53b2936edb07..4278169cd5940 100644 --- a/libc/config/config.json +++ b/libc/config/config.json @@ -45,6 +45,10 @@ "LIBC_CONF_PRINTF_RUNTIME_DISPATCH": { "value": true, "doc": "Use dynamic dispatch for the output mechanism to reduce code size." + }, + "LIBC_CONF_PRINTF_MODULAR": { + "value": true, + "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 109412225634f..1998c067dc77a 100644 --- a/libc/docs/configure.rst +++ b/libc/docs/configure.rst @@ -45,6 +45,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/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt index 6361822b61999..41b18bc7195ca 100644 --- a/libc/src/stdio/generic/CMakeLists.txt +++ b/libc/src/stdio/generic/CMakeLists.txt @@ -412,10 +412,15 @@ if(LLVM_LIBC_FULL_BUILD) ) endif() +set(printf_srcs printf.cpp) +if (LIBC_CONF_PRINTF_MODULAR) + list(APPEND printf_srcs printf_modular.cpp) +endif() + add_generic_entrypoint_object( printf SRCS - printf.cpp + ${printf_srcs} HDRS ../printf.h DEPENDS diff --git a/libc/src/stdio/generic/printf_modular.cpp b/libc/src/stdio/generic/printf_modular.cpp new file mode 100644 index 0000000000000..3a6a580002062 --- /dev/null +++ b/libc/src/stdio/generic/printf_modular.cpp @@ -0,0 +1,40 @@ +//===-- Implementation of printf_modular-----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/printf.h" + +#include "src/__support/File/file.h" +#include "src/__support/arg_list.h" +#include "src/__support/macros/config.h" +#include "src/stdio/printf_core/vfprintf_internal.h" + +#include "hdr/types/FILE.h" +#include + +#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE +#define PRINTF_STDOUT LIBC_NAMESPACE::stdout +#else // LIBC_COPT_STDIO_USE_SYSTEM_FILE +#define PRINTF_STDOUT ::stdout +#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE + +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); + int ret_val = printf_core::vfprintf_internal_modular( + reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args); + return ret_val; +} + +} // namespace LIBC_NAMESPACE_DECL 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 c22f9858f3b1e..abc4cc8269d79 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() @@ -112,10 +115,12 @@ add_header_library( libc.src.__support.StringUtil.error_to_string ) -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..deeb566bd4092 100644 --- a/libc/src/stdio/printf_core/float_dec_converter.h +++ b/libc/src/stdio/printf_core/float_dec_converter.h @@ -1122,11 +1122,23 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer, } } +template +LIBC_PRINTF_MODULAR_DECL int +convert_float_decimal(Writer *writer, const FormatSection &to_conv); +template +LIBC_PRINTF_MODULAR_DECL int +convert_float_dec_exp(Writer *writer, const FormatSection &to_conv); +template +LIBC_PRINTF_MODULAR_DECL int +convert_float_dec_auto(Writer *writer, + const FormatSection &to_conv); + +#ifdef LIBC_PRINTF_DEFINE_MODULAR // TODO: unify the float converters to remove the duplicated checks for inf/nan. template -LIBC_INLINE int convert_float_decimal(Writer *writer, - const FormatSection &to_conv) { +int convert_float_decimal(Writer *writer, + const FormatSection &to_conv) { if (to_conv.length_modifier == LengthModifier::L) { fputil::FPBits::StorageType float_raw = to_conv.conv_val_raw; fputil::FPBits float_bits(float_raw); @@ -1147,8 +1159,8 @@ LIBC_INLINE int convert_float_decimal(Writer *writer, } template -LIBC_INLINE int convert_float_dec_exp(Writer *writer, - const FormatSection &to_conv) { +int convert_float_dec_exp(Writer *writer, + const FormatSection &to_conv) { if (to_conv.length_modifier == LengthModifier::L) { fputil::FPBits::StorageType float_raw = to_conv.conv_val_raw; fputil::FPBits float_bits(float_raw); @@ -1169,8 +1181,8 @@ LIBC_INLINE int convert_float_dec_exp(Writer *writer, } template -LIBC_INLINE int convert_float_dec_auto(Writer *writer, - const FormatSection &to_conv) { +int convert_float_dec_auto(Writer *writer, + const FormatSection &to_conv) { if (to_conv.length_modifier == LengthModifier::L) { fputil::FPBits::StorageType float_raw = to_conv.conv_val_raw; fputil::FPBits float_bits(float_raw); @@ -1189,6 +1201,7 @@ LIBC_INLINE int convert_float_dec_auto(Writer *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 f468dbc8e2ae8..9804a38964be0 100644 --- a/libc/src/stdio/printf_core/float_dec_converter_limited.h +++ b/libc/src/stdio/printf_core/float_dec_converter_limited.h @@ -676,22 +676,34 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer, } template -LIBC_INLINE int convert_float_decimal(Writer *writer, - const FormatSection &to_conv) { +LIBC_PRINTF_MODULAR_DECL int convert_float_decimal(Writer *writer, + const FormatSection &to_conv); +template +LIBC_PRINTF_MODULAR_DECL int convert_float_dec_exp(Writer *writer, + const FormatSection &to_conv); +template +LIBC_PRINTF_MODULAR_DECL int convert_float_dec_auto(Writer *writer, + const FormatSection &to_conv); + +#ifdef LIBC_PRINTF_DEFINE_MODULAR +template +int convert_float_decimal(Writer *writer, + const FormatSection &to_conv) { return convert_float_outer(writer, to_conv, ConversionType::F); } template -LIBC_INLINE int convert_float_dec_exp(Writer *writer, - const FormatSection &to_conv) { +int convert_float_dec_exp(Writer *writer, + const FormatSection &to_conv) { return convert_float_outer(writer, to_conv, ConversionType::E); } template -LIBC_INLINE int convert_float_dec_auto(Writer *writer, - const FormatSection &to_conv) { +int convert_float_dec_auto(Writer *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 16592e7bac932..fa724066813d7 100644 --- a/libc/src/stdio/printf_core/float_hex_converter.h +++ b/libc/src/stdio/printf_core/float_hex_converter.h @@ -26,8 +26,13 @@ namespace LIBC_NAMESPACE_DECL { namespace printf_core { template -LIBC_INLINE int convert_float_hex_exp(Writer *writer, - const FormatSection &to_conv) { +LIBC_PRINTF_MODULAR_DECL int convert_float_hex_exp(Writer *writer, + const FormatSection &to_conv); + +#ifdef LIBC_PRINTF_DEFINE_MODULAR +template +int convert_float_hex_exp(Writer *writer, + const FormatSection &to_conv) { using LDBits = fputil::FPBits; using StorageType = LDBits::StorageType; @@ -254,6 +259,7 @@ LIBC_INLINE int convert_float_hex_exp(Writer *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..e7c9ba39aa148 --- /dev/null +++ b/libc/src/stdio/printf_core/float_impl.cpp @@ -0,0 +1,41 @@ +#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; +template class Parser>; +template class Parser>; +template class Parser>; +template class Parser>; + +#define INSTANTIATE_CONVERT_FN(NAME) \ + template int NAME( \ + Writer * writer, \ + const FormatSection &to_conv); \ + template int NAME( \ + Writer * writer, \ + const FormatSection &to_conv); \ + template int NAME( \ + Writer * writer, \ + const FormatSection &to_conv); \ + template int NAME( \ + Writer * 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 cef9b1ae58fa0..5a1eea36b603f 100644 --- a/libc/src/stdio/printf_core/parser.h +++ b/libc/src/stdio/printf_core/parser.h @@ -236,11 +236,7 @@ template 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 @@ -299,6 +295,12 @@ template 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 @@ -474,10 +476,9 @@ template class Parser { args_cur.template next_var(); #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()) - args_cur.template next_var(); - else if (cur_type_desc == type_desc_from_type()) - args_cur.template next_var(); + 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 @@ -630,10 +631,7 @@ template class Parser { case ('A'): case ('g'): case ('G'): - if (lm != LengthModifier::L) - conv_size = type_desc_from_type(); - else - conv_size = type_desc_from_type(); + conv_size = float_type_desc(lm); break; #endif // LIBC_COPT_PRINTF_DISABLE_FLOAT #ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT @@ -682,6 +680,38 @@ template class Parser { #endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE }; +#ifdef LIBC_PRINTF_DEFINE_MODULAR +template +void Parser::write_float_arg_val(FormatSection §ion, + LengthModifier lm, + 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 +TypeDesc Parser::float_type_desc(LengthModifier lm) { + if (lm != LengthModifier::L) + return type_desc_from_type(); + else + return type_desc_from_type(); +} + +template +bool Parser::advance_arg_if_float(TypeDesc cur_type_desc) { + if (cur_type_desc == type_desc_from_type()) + args_cur.template next_var(); + else if (cur_type_desc == type_desc_from_type()) + args_cur.template next_var(); + 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 57f29858d5298..c77922bb4f044 100644 --- a/libc/src/stdio/printf_core/printf_main.h +++ b/libc/src/stdio/printf_core/printf_main.h @@ -22,8 +22,8 @@ namespace LIBC_NAMESPACE_DECL { namespace printf_core { template -int printf_main(Writer *writer, const char *__restrict str, - internal::ArgList &args) { +int printf_main_modular(Writer *writer, const char *__restrict str, + internal::ArgList &args) { Parser parser(str, args); int result = 0; for (FormatSection cur_section = parser.get_next_section(); @@ -41,6 +41,15 @@ int printf_main(Writer *writer, const char *__restrict str, return writer->get_chars_written(); } +template +int printf_main(Writer *writer, const char *__restrict str, + internal::ArgList &args) { +#ifdef LIBC_COPT_PRINTF_MODULAR + __asm__ __volatile__ (".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/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h index 630de9d9d43dd..c9d6fce458409 100644 --- a/libc/src/stdio/printf_core/vfprintf_internal.h +++ b/libc/src/stdio/printf_core/vfprintf_internal.h @@ -67,7 +67,7 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) { return WRITE_OK; } -LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream, +LIBC_INLINE int vfprintf_internal_modular(::FILE *__restrict stream, const char *__restrict format, internal::ArgList &args) { constexpr size_t BUFF_SIZE = 1024; @@ -76,7 +76,7 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream, buffer, BUFF_SIZE, &file_write_hook, reinterpret_cast(stream)); Writer writer(wb); internal::flockfile(stream); - int retval = printf_main(&writer, format, args); + int retval = printf_main_modular(&writer, format, args); int flushval = wb.overflow_write(""); if (flushval != WRITE_OK) retval = flushval; @@ -84,6 +84,15 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream, return retval; } +LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream, + const char *__restrict format, + internal::ArgList &args) { +#ifdef LIBC_COPT_PRINTF_SPLIT + __asm__ __volatile__(".reloc ., BFD_RELOC_NONE, __printf_float"); +#endif + return vfprintf_internal_modular(stream, format, args); +} + } // namespace printf_core } // namespace LIBC_NAMESPACE_DECL