Skip to content

Commit 0db3132

Browse files
committed
[libc++] Add basic constant folding for std::format
1 parent 956cfa6 commit 0db3132

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

libcxx/include/__config

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,13 @@ typedef __char32_t char32_t;
556556
# define _LIBCPP_CLANG_DIAGNOSTIC_IGNORED_CXX23_EXTENSION _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wc++2b-extensions")
557557
# endif
558558

559+
# if __has_warning("-Wc++23-lambda-attributes")
560+
# define _LIBCPP_CLANG_DIANGOSTIC_INGORED_CXX23_LAMBDA_ATTRIBUTES \
561+
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wc++23-lambda-attributes")
562+
# else
563+
# define _LIBCPP_CLANG_DIANGOSTIC_INGORED_CXX23_LAMBDA_ATTRIBUTES
564+
# endif
565+
559566
// Clang modules take a significant compile time hit when pushing and popping diagnostics.
560567
// Since all the headers are marked as system headers in the modulemap, we can simply disable this
561568
// pushing and popping when building with clang modules.
@@ -567,6 +574,7 @@ typedef __char32_t char32_t;
567574
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wc++17-extensions") \
568575
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wc++20-extensions") \
569576
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED_CXX23_EXTENSION \
577+
_LIBCPP_CLANG_DIANGOSTIC_INGORED_CXX23_LAMBDA_ATTRIBUTES \
570578
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wc++14-extensions") \
571579
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wc++17-extensions") \
572580
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wc++20-extensions") \

libcxx/include/__format/format_functions.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#define _LIBCPP___FORMAT_FORMAT_FUNCTIONS
1212

1313
#include <__algorithm/clamp.h>
14+
#include <__algorithm/ranges_find_first_of.h>
15+
#include <__chrono/statically_widen.h>
1416
#include <__concepts/convertible_to.h>
1517
#include <__concepts/same_as.h>
1618
#include <__config>
@@ -447,10 +449,46 @@ format_to(_OutIt __out_it, wformat_string<_Args...> __fmt, _Args&&... __args) {
447449
}
448450
# endif
449451

452+
// Try constant folding the format string instead of going through the whole formatting machinery. If there is no
453+
// constant folding no extra code should be emitted (with optimizations enabled) and the function returns nullopt. When
454+
// constant folding is successful, the formatting is performed and the resulting string is returned.
455+
template <class _CharT>
456+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<basic_string<_CharT>> __try_constant_folding_format(
457+
basic_string_view<_CharT> __fmt,
458+
basic_format_args<basic_format_context<back_insert_iterator<__format::__output_buffer<_CharT>>, _CharT>> __args) {
459+
460+
// Fold strings not containing '{' or '}' to just return the string
461+
if (bool __is_identity = [&] [[__gnu__::__pure__]] // Make sure the compiler knows this call can be eliminated
462+
{ return std::ranges::find_first_of(__fmt, array{'{', '}'}) == __fmt.end(); }();
463+
__builtin_constant_p(__is_identity) && __is_identity)
464+
return basic_string<_CharT>{__fmt};
465+
466+
// Fold '{}' to the appropriate conversion function
467+
if (auto __only_first_arg = __fmt == _LIBCPP_STATICALLY_WIDEN(_CharT, "{}");
468+
__builtin_constant_p(__only_first_arg) && __only_first_arg) {
469+
if (auto __arg = __args.get(0); __builtin_constant_p(__arg.__type_)) {
470+
return std::__visit_format_arg(
471+
[]<class _Tp>(_Tp&& __argument) -> optional<basic_string<_CharT>> {
472+
if constexpr (is_same_v<remove_cvref_t<_Tp>, basic_string_view<_CharT>>) {
473+
return basic_string<_CharT>{__argument};
474+
} else {
475+
return nullopt;
476+
}
477+
},
478+
__arg);
479+
}
480+
}
481+
482+
return nullopt;
483+
}
484+
450485
// TODO FMT This needs to be a template or std::to_chars(floating-point) availability markup
451486
// fires too eagerly, see http://llvm.org/PR61563.
452487
template <class = void>
453488
[[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI string vformat(string_view __fmt, format_args __args) {
489+
auto __result = std::__try_constant_folding_format(__fmt, __args);
490+
if (__result.has_value())
491+
return *__result;
454492
__format::__allocating_buffer<char> __buffer;
455493
std::vformat_to(__buffer.__make_output_iterator(), __fmt, __args);
456494
return string{__buffer.__view()};
@@ -462,6 +500,9 @@ template <class = void>
462500
template <class = void>
463501
[[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI wstring
464502
vformat(wstring_view __fmt, wformat_args __args) {
503+
auto __result = std::__try_constant_folding_format(__fmt, __args);
504+
if (__result.has_value())
505+
return *__result;
465506
__format::__allocating_buffer<wchar_t> __buffer;
466507
std::vformat_to(__buffer.__make_output_iterator(), __fmt, __args);
467508
return wstring{__buffer.__view()};

libcxx/test/benchmarks/format/format.bench.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,13 @@ BENCHMARK(BM_format_string<char>)->RangeMultiplier(2)->Range(1, 1 << 20);
3535
BENCHMARK(BM_format_string<wchar_t>)->RangeMultiplier(2)->Range(1, 1 << 20);
3636
#endif
3737

38+
template <class CharT>
39+
static void BM_string_without_formatting(benchmark::State& state) {
40+
for (auto _ : state) {
41+
benchmark::DoNotOptimize(std::format(CSTR("Hello, World!")));
42+
}
43+
}
44+
BENCHMARK(BM_string_without_formatting<char>);
45+
BENCHMARK(BM_string_without_formatting<wchar_t>);
46+
3847
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)