Skip to content

Commit 44e0a17

Browse files
committed
[libc++] Add basic constant folding for std::format
1 parent fb14f1d commit 44e0a17

File tree

2 files changed

+51
-6
lines changed

2 files changed

+51
-6
lines changed

libcxx/include/__format/format_functions.h

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

1313
#include <__algorithm/clamp.h>
14+
#include <__algorithm/ranges_find_first_of.h>
1415
#include <__concepts/convertible_to.h>
1516
#include <__concepts/same_as.h>
1617
#include <__config>
@@ -448,13 +449,44 @@ format_to(_OutIt __out_it, wformat_string<_Args...> __fmt, _Args&&... __args) {
448449
}
449450
# endif
450451

452+
template <class _CharT>
453+
[[nodiscard]] optional<basic_string<_CharT>> __try_constant_folding(
454+
basic_string_view<_CharT> __fmt,
455+
basic_format_args<basic_format_context<back_insert_iterator<__format::__output_buffer<_CharT>>, _CharT>> __args) {
456+
return nullopt;
457+
if (bool __is_identity = std::ranges::find_first_of(__fmt, array{'{', '}'}) == __fmt.end();
458+
__builtin_constant_p(__is_identity) && __is_identity)
459+
return basic_string<_CharT>{__fmt};
460+
461+
if (auto __only_first_arg = __fmt == _LIBCPP_STATICALLY_WIDEN(_CharT, "{}");
462+
__builtin_constant_p(__only_first_arg) && __only_first_arg) {
463+
if (auto __arg = __args.get(0); __builtin_constant_p(__arg.__type_)) {
464+
return std::__visit_format_arg(
465+
[]<class _Tp>(_Tp&& __argument) -> optional<basic_string<_CharT>> {
466+
if constexpr (is_same_v<remove_cvref_t<_Tp>, basic_string_view<_CharT>>) {
467+
return basic_string<_CharT>{__argument};
468+
} else {
469+
return nullopt;
470+
}
471+
},
472+
__arg);
473+
}
474+
}
475+
476+
return nullopt;
477+
}
478+
451479
// TODO FMT This needs to be a template or std::to_chars(floating-point) availability markup
452480
// fires too eagerly, see http://llvm.org/PR61563.
453481
template <class = void>
454482
[[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI string vformat(string_view __fmt, format_args __args) {
455-
string __res;
456-
std::vformat_to(std::back_inserter(__res), __fmt, __args);
457-
return __res;
483+
return std::__try_constant_folding(__fmt, __args)
484+
.or_else([&]() -> optional<string> {
485+
string __res;
486+
std::vformat_to(std::back_inserter(__res), __fmt, __args);
487+
return __res;
488+
})
489+
.value();
458490
}
459491

460492
# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
@@ -463,9 +495,13 @@ template <class = void>
463495
template <class = void>
464496
[[nodiscard]] _LIBCPP_ALWAYS_INLINE inline _LIBCPP_HIDE_FROM_ABI wstring
465497
vformat(wstring_view __fmt, wformat_args __args) {
466-
wstring __res;
467-
std::vformat_to(std::back_inserter(__res), __fmt, __args);
468-
return __res;
498+
return std::__try_constant_folding(__fmt, __args)
499+
.or_else([&]() -> optional<wstring> {
500+
wstring __res;
501+
std::vformat_to(std::back_inserter(__res), __fmt, __args);
502+
return __res;
503+
})
504+
.value();
469505
}
470506
# endif
471507

libcxx/test/benchmarks/format.bench.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,13 @@ static void BM_format_string(benchmark::State& state) {
2828
BENCHMARK(BM_format_string<char>)->RangeMultiplier(2)->Range(1, 1 << 20);
2929
BENCHMARK(BM_format_string<wchar_t>)->RangeMultiplier(2)->Range(1, 1 << 20);
3030

31+
template <class CharT>
32+
static void BM_string_without_formatting(benchmark::State& state) {
33+
for (auto _ : state) {
34+
benchmark::DoNotOptimize(std::format(CSTR("Hello, World!")));
35+
}
36+
}
37+
BENCHMARK(BM_string_without_formatting<char>);
38+
BENCHMARK(BM_string_without_formatting<wchar_t>);
39+
3140
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)