[patch v3 4/4][libstdc++]: Inline simple regex repeating matches in DFS [PR126274]

Tamar Christina Tamar.Christina@arm.com
Thu Sep 3 10:38:27 GMT 2026


Ping

> -----Original Message-----
> From: Tamar Christina
> Sent: 20 August 2026 18:14
> To: gcc-patches@gcc.gnu.org
> Cc: nd <nd@arm.com>; libstdc++@gcc.gnu.org; jwakely@redhat.com;
> tkaminsk@redhat.com; ppalka@redhat.com
> Subject: [patch v3 4/4][libstdc++]: Inline simple regex repeating matches in
> DFS [PR126274]
> 
> 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.
> 
> Benchmarks improvements compared to GCC previous patch in series:
> 
>  at -O2:
> 
>   email: +4.7%
>   URI: +5.1%
>   IPv4 -1.1%
> 
>  at -O3:
> 
>   email: +4.5%,
>   URI: +4.1%
>   IPv4: -0.2%
> 
> And finally gets us better than GCC 15.
> 
> I outlined the helper into _M_match_simple_repeat_body since that
> looks more readable.
> 
> 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.h (_M_match_simple_repeat_body):
> New.
> 	* include/bits/regex_executor.tcc (_M_dfs_next): Inline consume
> matches
> 	on _S_opcode_repeat.
> 	(_M_match_simple_repeat_body): New.
> 
> ---
> diff --git a/libstdc++-v3/include/bits/regex_executor.h b/libstdc++-
> v3/include/bits/regex_executor.h
> index
> c2dce4e24d35807b311d5a3273297f72a74cc24d..7b9f86ce315a11c2336ac
> a3e17ca05001f1fc35d 100644
> --- a/libstdc++-v3/include/bits/regex_executor.h
> +++ b/libstdc++-v3/include/bits/regex_executor.h
> @@ -140,6 +140,9 @@ _GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2)
>        _StateIdT
>        _M_rep_once_more(_Match_mode __match_mode, _StateIdT);
> 
> +      _StateIdT
> +      _M_match_simple_repeat_body(_StateIdT, _StateIdT);
> +
>        template<_Search_mode __search_mode>
>        _StateIdT
>        _M_handle_repeat(_Match_mode, _StateIdT);
> diff --git a/libstdc++-v3/include/bits/regex_executor.tcc b/libstdc++-
> v3/include/bits/regex_executor.tcc
> index
> 2c03c6bfe9377ac66bffc0f3d9350d5d5cc35468..6c7255ce6be17d8e2682e4
> 1cce67799fb9caf79a 100644
> --- a/libstdc++-v3/include/bits/regex_executor.tcc
> +++ b/libstdc++-v3/include/bits/regex_executor.tcc
> @@ -379,6 +379,31 @@ _GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2)
>        return _S_invalid_state_id;
>      }
> 
> +  // Try to consume the common repeat body shape
> +  //   repeat -> match -> repeat
> +  // without going through the generic state dispatch again.
> +  template<typename _BiIter, typename _Alloc, typename _TraitsT>
> +#ifdef __OPTIMIZE__
> +    [[__gnu__::__always_inline__]]
> +#endif
> +    inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
> +    _M_match_simple_repeat_body(_StateIdT __next, _StateIdT __repeat)
> +    {
> +      if (__next == _S_invalid_state_id)
> +	return _S_invalid_state_id;
> +
> +      const auto& __state = _M_nfa[__next];
> +      if (__state._M_opcode() != _S_opcode_match
> +	  || __state._M_next != __repeat)
> +	return __next;
> +
> +      if (_M_current == _M_end || !__state._M_matches(*_M_current))
> +	return _S_invalid_state_id;
> +
> +      ++_M_current;
> +      return __repeat;
> +    }
> +
>    // _M_alt branch is "match once more", while _M_next is "get me out
>    // of this quantifier". Executing _M_next first or _M_alt first don't
>    // mean the same thing, and we need to choose the correct order under
> @@ -401,7 +426,11 @@ _GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2)
>  				   _M_current);
>  	  else
>  	    _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> -	  return _M_rep_once_more(__match_mode, __i);
> +	  _StateIdT __next = _M_rep_once_more(__match_mode, __i);
> +	  if constexpr (__search_mode == _Search_mode::_Dfs)
> +	    return _M_match_simple_repeat_body(__next, __i);
> +	  else
> +	    return __next;
>  	}
>        else // Non-greedy mode
>  	{
> 
> 
> --


More information about the Libstdc++ mailing list