From ac1220f860a8639881e28ba7824e90ba1155d4f4 Mon Sep 17 00:00:00 2001 From: Michael Jones Date: Tue, 10 Feb 2026 15:41:38 -0800 Subject: [PATCH] [libc] Add option to disable printf bit int (#180832) Requested as a binary size optimization. Updates the parser, converter utils, config, tests, and docs. --- .../modules/LLVMLibCCompileOptionRules.cmake | 4 ++++ libc/config/baremetal/config.json | 5 ++++- libc/config/config.json | 5 ++++- libc/docs/configure.rst | 1 + libc/docs/dev/printf_behavior.rst | 8 +++++++- libc/src/stdio/printf_core/converter_utils.h | 2 ++ libc/src/stdio/printf_core/core_structs.h | 16 +++++++++++++++- libc/src/stdio/printf_core/parser.h | 6 ++++++ libc/src/stdio/printf_core/write_int_converter.h | 2 ++ libc/test/UnitTest/PrintfMatcher.cpp | 2 ++ libc/test/src/stdio/printf_core/parser_test.cpp | 2 ++ libc/test/src/stdio/sprintf_test.cpp | 3 +++ 12 files changed, 52 insertions(+), 4 deletions(-) diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake index 98fc0206daa4..b15a9fab09c3 100644 --- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake +++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake @@ -151,6 +151,10 @@ function(_get_compile_options_from_config output_var) list(APPEND config_options "-DLIBC_COPT_PRINTF_DISABLE_WIDE") endif() + if(LIBC_COPT_PRINTF_DISABLE_BITINT) + list(APPEND config_options "-DLIBC_COPT_PRINTF_DISABLE_BITINT") + endif() + set(${output_var} ${config_options} PARENT_SCOPE) endfunction(_get_compile_options_from_config) diff --git a/libc/config/baremetal/config.json b/libc/config/baremetal/config.json index af5b6115c87e..2c9bfeb20541 100644 --- a/libc/config/baremetal/config.json +++ b/libc/config/baremetal/config.json @@ -28,7 +28,10 @@ "LIBC_CONF_PRINTF_DISABLE_STRERROR": { "value": true }, - "LIBC_CONF_PRINTF_DISABLE_WIDE" : { + "LIBC_CONF_PRINTF_DISABLE_WIDE": { + "value": true + }, + "LIBC_COPT_PRINTF_DISABLE_BITINT": { "value": true } }, diff --git a/libc/config/config.json b/libc/config/config.json index b37154e294ec..54d1282df629 100644 --- a/libc/config/config.json +++ b/libc/config/config.json @@ -40,7 +40,6 @@ "value": false, "doc": "Use an alternative printf float implementation based on 320-bit floats" }, - "LIBC_CONF_PRINTF_DISABLE_FIXED_POINT": { "value": false, "doc": "Disable printing fixed point values in printf and friends." @@ -56,6 +55,10 @@ "LIBC_CONF_PRINTF_DISABLE_WIDE": { "value": false, "doc": "Disable handling wide characters for printf and friends." + }, + "LIBC_COPT_PRINTF_DISABLE_BITINT": { + "value": false, + "doc": "Disable bitint length modifiers to reduce code size. Specifically the wNUM and wfNUM modifiers." } }, "scanf": { diff --git a/libc/docs/configure.rst b/libc/docs/configure.rst index 0813ab355421..e6bf4b20ef59 100644 --- a/libc/docs/configure.rst +++ b/libc/docs/configure.rst @@ -50,6 +50,7 @@ to learn about the defaults for your platform and target. - ``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_RUNTIME_DISPATCH``: Use dynamic dispatch for the output mechanism to reduce code size. + - ``LIBC_COPT_PRINTF_DISABLE_BITINT``: Disable bitint length modifiers to reduce code size. Specifically the wNUM and wfNUM modifiers. * **"pthread" options** - ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100). - ``LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a rwlock is in contention (default to 100). diff --git a/libc/docs/dev/printf_behavior.rst b/libc/docs/dev/printf_behavior.rst index a825da55367b..4b53e0b85f6d 100644 --- a/libc/docs/dev/printf_behavior.rst +++ b/libc/docs/dev/printf_behavior.rst @@ -72,12 +72,18 @@ invalid. This reduces code size. This has no effect if the current compiler does not support fixed point numbers. LIBC_COPT_PRINTF_DISABLE_WIDE --------------------------------- +----------------------------- When set, this flag disables support for wide characters (%lc and %ls). Any conversions will be ignored. This reduces code size. This will be set by default on windows platforms as current printf implementation does not support UTF-16 wide characters. +LIBC_COPT_PRINTF_DISABLE_BITINT +------------------------------- +When set, this flag disables the bit int length modifiers wNUM and wfNUM. The +length modifiers will be treated as if they don't exist, so conversions using +them will be treated as invalid. This reduces code size. + .. _printf_no_nullptr_checks: LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS diff --git a/libc/src/stdio/printf_core/converter_utils.h b/libc/src/stdio/printf_core/converter_utils.h index 3f25ebfd40ed..ed656ffb6e72 100644 --- a/libc/src/stdio/printf_core/converter_utils.h +++ b/libc/src/stdio/printf_core/converter_utils.h @@ -43,6 +43,7 @@ LIBC_INLINE uintmax_t apply_length_modifier(uintmax_t num, return num & cpp::numeric_limits::max(); case LengthModifier::j: return num; // j is intmax, so no mask is necessary. +#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT case LengthModifier::w: case LengthModifier::wf: { uintmax_t mask; @@ -55,6 +56,7 @@ LIBC_INLINE uintmax_t apply_length_modifier(uintmax_t num, } return num & mask; } +#endif // LIBC_COPT_PRINTF_DISABLE_BITINT } __builtin_unreachable(); } diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h index d93fa962db90..705865745db4 100644 --- a/libc/src/stdio/printf_core/core_structs.h +++ b/libc/src/stdio/printf_core/core_structs.h @@ -24,7 +24,21 @@ namespace printf_core { // These length modifiers match the length modifiers in the format string, which // is why they are formatted differently from the rest of the file. -enum class LengthModifier { hh, h, l, ll, j, z, t, L, w, wf, none }; +enum class LengthModifier { + hh, + h, + l, + ll, + j, + z, + t, + L, +#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT + w, + wf, +#endif // LIBC_COPT_PRINTF_DISABLE_BITINT + none +}; struct LengthSpec { LengthModifier lm; diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h index a3b62991bcec..d70a3aa91ce6 100644 --- a/libc/src/stdio/printf_core/parser.h +++ b/libc/src/stdio/printf_core/parser.h @@ -224,6 +224,7 @@ public: WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, ptrdiff_t, conv_index); break; +#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT case (LengthModifier::w): case (LengthModifier::wf): if (bw == 0) { @@ -238,6 +239,7 @@ public: WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, intmax_t, conv_index); } break; +#endif // LIBC_COPT_PRINTF_DISABLE_BITINT } break; #ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT @@ -360,6 +362,7 @@ private: ++*local_pos; return {LengthModifier::l, 0}; } +#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT case ('w'): { LengthModifier lm; if (str[*local_pos + 1] == 'f') { @@ -376,6 +379,7 @@ private: } return {lm, 0}; } +#endif // LIBC_COPT_PRINTF_DISABLE_BITINT case ('h'): if (str[*local_pos + 1] == 'h') { *local_pos += 2; @@ -629,6 +633,7 @@ private: case (LengthModifier::t): conv_size = type_desc_from_type(); break; +#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT case (LengthModifier::w): case (LengthModifier::wf): if (bw <= cpp::numeric_limits::digits) { @@ -641,6 +646,7 @@ private: conv_size = type_desc_from_type(); } break; +#endif // LIBC_COPT_PRINTF_DISABLE_BITINT } break; #ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT diff --git a/libc/src/stdio/printf_core/write_int_converter.h b/libc/src/stdio/printf_core/write_int_converter.h index 04b2bef05bc7..0c7efedb5f3b 100644 --- a/libc/src/stdio/printf_core/write_int_converter.h +++ b/libc/src/stdio/printf_core/write_int_converter.h @@ -57,8 +57,10 @@ LIBC_INLINE int convert_write_int(Writer *writer, *reinterpret_cast(to_conv.conv_val_ptr) = written; break; case LengthModifier::j: +#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT case LengthModifier::w: case LengthModifier::wf: +#endif // LIBC_COPT_PRINTF_DISABLE_BITINT *reinterpret_cast(to_conv.conv_val_ptr) = written; break; } diff --git a/libc/test/UnitTest/PrintfMatcher.cpp b/libc/test/UnitTest/PrintfMatcher.cpp index 2ea1bbfcc636..3a940aad8e43 100644 --- a/libc/test/UnitTest/PrintfMatcher.cpp +++ b/libc/test/UnitTest/PrintfMatcher.cpp @@ -71,8 +71,10 @@ static void display(FormatSection form) { CASE_LM(z); CASE_LM(t); CASE_LM(L); +#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT CASE_LM_BIT_WIDTH(w, form.bit_width); CASE_LM_BIT_WIDTH(wf, form.bit_width); +#endif // LIBC_COPT_PRINTF_DISABLE_BITINT } tlog << "\n"; tlog << "\tconversion name: " << form.conv_name << "\n"; diff --git a/libc/test/src/stdio/printf_core/parser_test.cpp b/libc/test/src/stdio/printf_core/parser_test.cpp index 9d192828860f..a73e3af990dd 100644 --- a/libc/test/src/stdio/printf_core/parser_test.cpp +++ b/libc/test/src/stdio/printf_core/parser_test.cpp @@ -230,6 +230,7 @@ TEST(LlvmLibcPrintfParserTest, EvalOneArgWithLongLengthModifier) { ASSERT_PFORMAT_EQ(expected, format_arr[0]); } +#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT TEST(LlvmLibcPrintfParserTest, EvalOneArgWithBitWidthLengthModifier) { LIBC_NAMESPACE::printf_core::FormatSection format_arr[10]; const char *str = "%w32d"; @@ -267,6 +268,7 @@ TEST(LlvmLibcPrintfParserTest, EvalOneArgWithFastBitWidthLengthModifier) { ASSERT_PFORMAT_EQ(expected, format_arr[0]); } +#endif // LIBC_COPT_PRINTF_DISABLE_BITINT TEST(LlvmLibcPrintfParserTest, EvalOneArgWithAllOptions) { LIBC_NAMESPACE::printf_core::FormatSection format_arr[10]; diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp index 78186abb9966..01d9c1056356 100644 --- a/libc/test/src/stdio/sprintf_test.cpp +++ b/libc/test/src/stdio/sprintf_test.cpp @@ -151,6 +151,8 @@ TEST(LlvmLibcSPrintfTest, IntConv) { written = LIBC_NAMESPACE::sprintf(buff, "%lld", -9223372036854775807ll - 1ll); ASSERT_STREQ_LEN(written, buff, "-9223372036854775808"); // ll min +#ifndef LIBC_COPT_PRINTF_DISABLE_BITINT + // Bit int width tests written = LIBC_NAMESPACE::sprintf(buff, "%w3d", 5807); ASSERT_STREQ_LEN(written, buff, "7"); @@ -218,6 +220,7 @@ TEST(LlvmLibcSPrintfTest, IntConv) { written = LIBC_NAMESPACE::sprintf(buff, "%wf999d", 9223372036854775807ll); ASSERT_STREQ_LEN(written, buff, "9223372036854775807"); +#endif // LIBC_COPT_PRINTF_DISABLE_BITINT // Min Width Tests.