On Thu, 16 Jul 2026, Tamar Christina wrote:
> Optimize the common DFS NFA shape repeat -> match -> repeat, which is produced
> by repeated character classes such as [\w]+, [^\s?#]+, and #+.
>
> Since the second patch the DFS continuation already avoids pushing a separate
> _S_fopcode_next frame for many states. This patch adds a small improvement
> for greedy repeats.
>
> After creating the same fallback and repeat bookkeeping frames as before, if
> the repeated body is a single match state that returns to the repeat state,
> consume that match state immediately and continue at the repeat.
>
> Backtracking behavior is unchanged. _M_rep_once_more still creates the
> restore/decrement frames, and the repeat exit fallback is still saved before
> trying the body. If the body match fails, the helper returns
> _S_invalid_state_id so the normal frame loop restores repeat state and tries
> pending fallbacks.
>
> Benchmark improvements compared to:
> GCC 16:
> email: 57.4%
> URI: 57.2%
> IPv4: 68.0%
These are the cumulative improvements right? The previous patch had:
email: 55.5%
URI: 55.2%
IPv4: 68.0%
So the marginal improvement of this patch is around 2% for email / URI?
Not sure the increased complexity and _M_handle_match logic duplication
is worth it.
>
> GCC 15:
> email: 6.7%
> URI: 3.8%
> IPv4: 23.2%
>
> Bootstrapped Regtested on aarch64-none-linux-gnu,
> arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
> -m32, -m64 and no issues.
>
> Ok for master?
>
> Thanks,
> Tamar
>
> libstdc++-v3/ChangeLog:
>
> PR libstdc++/126274
> * include/bits/regex_executor.tcc (_M_dfs_next): Inline consume matches
> on _S_opcode_repeat.
>
> ---
> diff --git a/libstdc++-v3/include/bits/regex_executor.tcc
> b/libstdc++-v3/include/bits/regex_executor.tcc
> index
> 3a3bad8ecac485d452970c0aa26e6db34cabd1e5..558d29feaef1eecd9ec13ba395a53f3ede990178
> 100644
> --- a/libstdc++-v3/include/bits/regex_executor.tcc
> +++ b/libstdc++-v3/include/bits/regex_executor.tcc
> @@ -796,7 +796,28 @@ namespace __detail
> {
> _M_frames.emplace_back(_S_fopcode_fallback_next,
> __state._M_next, _M_current);
> - return _M_rep_once_more(__match_mode, __i);
> +
> + // Consume the match state here so the straight-line loop avoids
> + // one extra _M_dfs_next dispatch per repeated character. The
> + // repeat bookkeeping and fallback frames are still produced by
> + // _M_rep_once_more, so backtracking order is unchanged.
> + _StateIdT __next = _M_rep_once_more(__match_mode, __i);
> + if (__next != _S_invalid_state_id)
> + {
> + const auto& __alt_state = _M_nfa[__next];
> + if (__alt_state._M_opcode() == _S_opcode_match
> + && __alt_state._M_next == __i)
> + {
> + if (_M_current != _M_end
> + && __alt_state._M_matches(*_M_current))
> + {
> + ++_M_current;
> + return __i;
> + }
> + return _S_invalid_state_id;
> + }
> + }
> + return __next;
> }
> else
> {
>
>
> --
>