On Wed, Jan 28, 2026 at 6:18 PM Patrick Palka <[email protected]> wrote:

> On Wed, 28 Jan 2026, Patrick Palka wrote:
>
> > Changes in v7:
> >  - Make fallback_next and fallback_rep_once_more code paths fall through
> >    to the next / rep_once_more code paths instead of pushing a frame
> >    that gets immediately popped.
> >
> > Changes in v6:
> >  - Use an inline namespace to give the _Executor class a different
> >    mangled name since its implementation is no longer compatible with
> >    the previous recursive one
> >  - Explicitly cast _M_subexpr to _StateIdT in _M_handle_subexpr_begin/end
> >    to self-document that what we're storing in
> _ExecutorFrame::_M_state_id
> >    isn't really a state id
> >  - I experimented with using std::deque instead of std::vector for the
> >    frames stack.
> >    Upsides:
> >      - Reduces peak memory use from 50MB to 33MB for the microbenchmark
> >        regex_match(string(200000, 'a'), "(a|b|c)*")
> >        due to less unused capacity (vector's 2x growth factor means that
> >        50% of the time there will be at least 25% unused capacity)
> >      - Seems to be as fast as vector
> >      - Automatically reclaims unused capacity as the stack unwinds
> >      - No expensive reallocation needed when growing the stack in the
> >        case of non-trivially-copyable input iterators
> >     Downsides:
> >      - deque will allocate/free a chunk of memory whenever crossing a
> >        chunk boundary (512 elements) in either direction, which could
> >        degrade performance in rare cases where our stack size hovers
> >        near a multiple of the chunk boundary.
> >      - the fixed chunk size means small inputs will use at least
> >        512*24 bytes=12kB of memory
> >     Ultimately it seems using std::deque is better for very large
> >     inputs, and for small inputs std::vector is slightly better.  I'm
> >     not sure which container to go with, for now I stuck with
> std::vector.
> >     (N.B. we seem to be 2x faster than libc++'s std::regex and use 60%
> >     less memory for the above microbenchmark)
> >
> > Changes in v5:
> >   - Replace char _ExecutorFrame::_M_char with
> >     uint8_t _ExecutorFrame::_M_bytes[7] to fully account for
> >     the implicit struct padding and make it char size/signedness
> >     agnostic.
> >
> > Changes in v4:
> >   - Do not remove the BFS executor.  I convinced myself that it's
> >     worthwhile to keep it around for backwards compatibility and its
> >     better time complexity (e.g. for PR78276).
> >   - However, this means the mangling of _Executor is now the same as
> >     before (since the __dfs_mode template parameter is no longer gone).
> >     I believe we need to give this class a different mangling so
> >     programs that mix old/new std::regex impls continue to work.
> >
> > Changes in v3:
> >
> >   - Add char _M_char member to _ExecutorFrame (without increasing the
> >     size of struct thanks to padding)
> >   - Use it to fix bug in restore_subexpr_end frame code whereby we
> >     weren't restoring _M_cur_results.matched properly
> >   - Combine restore_subexpr_begin/end frame codes into one
> >     restore_cur_results frame code
> >   - Combine restore_rep_count_val/posn frame codes into single
> >     restore_rep_count frame_code (this makes the patch 3/4 from v2
> >     obsolete)
> >   - Store the frame stack in the data member _ExecutorFrame::_M_frames
> >     alongside the rest of the executor state instead of using a local.
> >     Using a local variable is just more code with no real benefit.
> >
> > -- >8 --
> >
> > libstdc++/regex: Convert DFS executor to use iteration [PR86164]
> >
> > This replaces the recursion and implicit system stack usage of the DFS
> > executor with iteration and an explicit heap-based stack.  After this
> > patch, we no longer stack overflow on the PR86164 testcases since system
> > stack usage is no longer linear with respect to input size.  This should
> > otherwise be a non-functional change.
> >
> >       PR libstdc++/86164
> >
> > libstdc++-v3/ChangeLog:
> >
> >       * include/bits/regex.h (__detail::_Executor): Use inline
> >       namespace _V2.
> >       * include/bits/regex_executor.h (__detail::_ExecutorFrame):
> >       Declare.
> >       (__detail::_Executor): Use inline namespace _V2.
> >       (__detail::_Executor::_M_node): Declare.
> >       (__detail::_Executor::_M_frames): New data member.
> >       * include/bits/regex_executor.tcc (__detail::_ExecutorFrameOpcode):
> >       New.
> >       (__detail::_ExecutorFrame): New.
> >       (__detail::_Executor): Use inline namespace _V2.
> >       (__detail::_Executor::_M_rep_once_more): Replace recursive
> >       _M_dfs calls with an _S_opcode_next frame push, and any tail work
> >       after such calls with an appropriate frame push.
> >       (__detail::_M_handle_repeat): Likewise.
> >       (__detail::_M_handle_subexpr_begin): Likewise.
> >       (__detail::_M_handle_subexpr_end): Likewise.
> >       (__detail::_M_handle_line_begin_assertion): Likewise.
> >       (__detail::_M_handle_line_end_assertion): Likewise.
> >       (__detail::_M_handle_word_boundary): Likewise.
> >       (__detail::_M_handle_subexpr_lookahead): Likewise.
> >       (__detail::_M_handle_match): Likewise.
> >       (__detail::_M_handle_backref): Likewise.
> >       (__detail::_M_handle_accept): Likewise.
> >       (__detail::_M_handle_alternative): Likewise.
> >       (__detail::_M_node): Factored out from _M_dfs.
> >       (__detail::_M_dfs): Push an initial frame to _M_frames that
> >       visits the starting node and pass this stack each subroutine.
> >       Pop the latest _ExecutorFrame from _M_frames and handle
> >       appropriately according to its _ExecutorFrameOpcode.  Loop until
> >       _M_frames is empty.
> >       * include/std/regex: Include <cstdint>.
> > ---
> >  libstdc++-v3/include/bits/regex.h            |   2 +
> >  libstdc++-v3/include/bits/regex_executor.h   |   9 +
> >  libstdc++-v3/include/bits/regex_executor.tcc | 218 +++++++++++++++----
> >  libstdc++-v3/include/std/regex               |   1 +
> >  4 files changed, 185 insertions(+), 45 deletions(-)
> >
> > diff --git a/libstdc++-v3/include/bits/regex.h
> b/libstdc++-v3/include/bits/regex.h
> > index 68fb6254f362..5bcb9a447ce2 100644
> > --- a/libstdc++-v3/include/bits/regex.h
> > +++ b/libstdc++-v3/include/bits/regex.h
> > @@ -61,8 +61,10 @@ namespace __detail
> >                     _RegexExecutorPolicy                 __policy,
> >                     bool                                 __match_mode);
> >
> > +_GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2)
> >    template<typename, typename, typename, bool>
> >      class _Executor;
> > +_GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2)
> >
> >    template<typename _Tp>
> >      struct __is_contiguous_iter : false_type { };
> > diff --git a/libstdc++-v3/include/bits/regex_executor.h
> b/libstdc++-v3/include/bits/regex_executor.h
> > index f9857bfc4c1d..94048e1f632c 100644
> > --- a/libstdc++-v3/include/bits/regex_executor.h
> > +++ b/libstdc++-v3/include/bits/regex_executor.h
> > @@ -36,11 +36,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >  namespace __detail
> >  {
> > +_GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2)
> >    /**
> >     * @addtogroup regex-detail
> >     * @{
> >     */
> >
> > +  template<typename _BiIter, bool _Trivial =
> is_trivially_copyable<_BiIter>::value>
> > +    struct _ExecutorFrame;
> > +
> >    /**
> >     * @brief Takes a regex and an input string and does the matching.
> >     *
> > @@ -142,6 +146,9 @@ namespace __detail
> >        void
> >        _M_handle_alternative(_Match_mode, _StateIdT);
> >
> > +      void
> > +      _M_node(_Match_mode, _StateIdT);
> > +
> >        void
> >        _M_dfs(_Match_mode __match_mode, _StateIdT __start);
> >
> > @@ -290,6 +297,7 @@ namespace __detail
> >       };
> >
> >      public:
> > +      _GLIBCXX_STD_C::vector<_ExecutorFrame<_BiIter>>       _M_frames;
> >        _ResultsVec
>  _M_cur_results;
> >        _BiIter                                               _M_current;
> >        _BiIter                                               _M_begin;
> > @@ -305,6 +313,7 @@ namespace __detail
> >      };
> >
> >   ///@} regex-detail
> > +_GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2)
> >  } // namespace __detail
> >  _GLIBCXX_END_NAMESPACE_VERSION
> >  } // namespace std
> > diff --git a/libstdc++-v3/include/bits/regex_executor.tcc
> b/libstdc++-v3/include/bits/regex_executor.tcc
> > index 421e585f39d9..ddfdbb0cb751 100644
> > --- a/libstdc++-v3/include/bits/regex_executor.tcc
> > +++ b/libstdc++-v3/include/bits/regex_executor.tcc
> > @@ -36,6 +36,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >  #pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
> >  namespace __detail
> >  {
> > +_GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2)
> >    template<typename _BiIter, typename _Alloc, typename _TraitsT,
> >          bool __dfs_mode>
> >      bool _Executor<_BiIter, _Alloc, _TraitsT, __dfs_mode>::
> > @@ -55,6 +56,71 @@ namespace __detail
> >        return false;
> >      }
> >
> > +  enum _ExecutorFrameOpcode : uint8_t
> > +  {
> > +    _S_fopcode_next,
> > +    _S_fopcode_fallback_next,
> > +    _S_fopcode_rep_once_more,
> > +    _S_fopcode_fallback_rep_once_more,
> > +    _S_fopcode_posix_alternative,
> > +    _S_fopcode_merge_sol,
> > +    _S_fopcode_restore_current,
> > +    _S_fopcode_restore_cur_results,
> > +    _S_fopcode_restore_rep_count,
> > +    _S_fopcode_decrement_rep_count,
> > +  };
> > +
> > +  template<typename _BiIter, bool _Trivial /* =
> is_trivially_copyable<_BiIter>::value */>
> > +    struct _ExecutorFrame
> > +    {
> > +      _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i)
> > +      : _M_op(__op), _M_state_id(__i)
> > +      { }
> > +
> > +      _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i, _BiIter
> __p)
> > +      : _M_op(__op), _M_state_id(__i), _M_pos(__p)
> > +      { }
> > +
> > +      _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i, long __v)
> > +      : _M_op(__op), _M_state_id(__i), _M_val(__v)
> > +      { }
> > +
> > +      _ExecutorFrameOpcode _M_op;
> > +      uint8_t _M_bytes[7];
> > +      _StateIdT _M_state_id;
> > +      // _M_pos and _M_val are mutually exclusive, which the optimized
> > +      // partial specialization below depends on.
> > +      _BiIter _M_pos = _BiIter();
> > +      long _M_val = 0;
> > +    };
> > +
> > +  // Space-optimized partial specialization for when the input iterator
> is
> > +  // trivially copyable.
> > +  template<typename _BiIter>
> > +    struct _ExecutorFrame<_BiIter, true>
> > +    {
> > +      _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i)
> > +      : _M_op(__op), _M_state_id(__i)
> > +      { }
> > +
> > +      _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i, _BiIter
> __p)
> > +      : _M_op(__op), _M_state_id(__i), _M_pos(__p)
> > +      { }
> > +
> > +      _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i, long __v)
> > +      : _M_op(__op), _M_state_id(__i), _M_val(__v)
> > +      { }
> > +
> > +      _ExecutorFrameOpcode _M_op;
> > +      uint8_t _M_bytes[7];
> > +      _StateIdT _M_state_id;
> > +      union
> > +      {
> > +     _BiIter _M_pos;
> > +     long _M_val;
> > +      };
> > +    };
> > +
> >    // The _M_main function operates in different modes, DFS mode or BFS
> mode,
> >    // indicated by template parameter __dfs_mode, and dispatches to one
> of the
> >    // _M_main_dispatch overloads.
> > @@ -181,19 +247,20 @@ namespace __detail
> >        auto& __rep_count = _M_rep_count[__i];
> >        if (__rep_count.second == 0 || __rep_count.first != _M_current)
> >       {
> > -       auto __back = __rep_count;
> > +       _M_frames.emplace_back(_S_fopcode_restore_rep_count,
> > +                              __i, __rep_count.first);
> > +       _M_frames.back()._M_bytes[0] = __rep_count.second;
> >         __rep_count.first = _M_current;
> >         __rep_count.second = 1;
> > -       _M_dfs(__match_mode, __state._M_alt);
> > -       __rep_count = __back;
> > +       _M_frames.emplace_back(_S_fopcode_next, __state._M_alt);
> >       }
> >        else
> >       {
> >         if (__rep_count.second < 2)
> >           {
> >             __rep_count.second++;
> > -           _M_dfs(__match_mode, __state._M_alt);
> > -           __rep_count.second--;
> > +           _M_frames.emplace_back(_S_fopcode_decrement_rep_count, __i);
> > +           _M_frames.emplace_back(_S_fopcode_next, __state._M_alt);
> >           }
> >       }
> >      }
> > @@ -208,23 +275,23 @@ namespace __detail
> >      _M_handle_repeat(_Match_mode __match_mode, _StateIdT __i)
> >      {
> >        const auto& __state = _M_nfa[__i];
> > -
> >        // Greedy.
> >        if (!__state._M_neg)
> >       {
> > -       _M_rep_once_more(__match_mode, __i);
> > -       // If it's DFS executor and already accepted, we're done.
> > -       if (!__dfs_mode || !_M_has_sol)
> > -         _M_dfs(__match_mode, __state._M_next);
> > +       if constexpr (__dfs_mode)
> > +         // If it's DFS executor and already accepted, we're done.
> > +         _M_frames.emplace_back(_S_fopcode_fallback_next,
> __state._M_next);
> > +       else
> > +         _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> > +       _M_frames.emplace_back(_S_fopcode_rep_once_more, __i);
> >       }
> >        else // Non-greedy mode
> >       {
> >         if constexpr (__dfs_mode)
> >           {
> >             // vice-versa.
> > -           _M_dfs(__match_mode, __state._M_next);
> > -           if (!_M_has_sol)
> > -             _M_rep_once_more(__match_mode, __i);
> > +           _M_frames.emplace_back(_S_fopcode_fallback_rep_once_more,
> __i);
> > +           _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >           }
> >         else
> >           {
> > @@ -233,12 +300,11 @@ namespace __detail
> >             // be better by attempting its next node.
> >             if (!_M_has_sol)
> >               {
> > -               _M_dfs(__match_mode, __state._M_next);
> >                 // DON'T attempt anything if it's already accepted. An
> >                 // accepted state *must* be better than a solution that
> >                 // matches a non-greedy quantifier one more time.
> > -               if (!_M_has_sol)
> > -                 _M_rep_once_more(__match_mode, __i);
> > +
>  _M_frames.emplace_back(_S_fopcode_fallback_rep_once_more, __i);
> > +               _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >               }
> >           }
> >       }
> > @@ -250,12 +316,13 @@ namespace __detail
> >      _M_handle_subexpr_begin(_Match_mode __match_mode, _StateIdT __i)
> >      {
> >        const auto& __state = _M_nfa[__i];
> > -
> >        auto& __res = _M_cur_results[__state._M_subexpr];
> > -      auto __back = __res.first;
> > +      _M_frames.emplace_back(_S_fopcode_restore_cur_results,
> > +                          static_cast<_StateIdT>(__state._M_subexpr),
> > +                          __res.first);
> > +      _M_frames.back()._M_bytes[0] = 0xff;
> >        __res.first = _M_current;
> > -      _M_dfs(__match_mode, __state._M_next);
> > -      __res.first = __back;
> > +      _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >      }
> >
> >    template<typename _BiIter, typename _Alloc, typename _TraitsT,
> > @@ -264,13 +331,14 @@ namespace __detail
> >      _M_handle_subexpr_end(_Match_mode __match_mode, _StateIdT __i)
> >      {
> >        const auto& __state = _M_nfa[__i];
> > -
> >        auto& __res = _M_cur_results[__state._M_subexpr];
> > -      auto __back = __res;
> > +      _M_frames.emplace_back(_S_fopcode_restore_cur_results,
> > +                          static_cast<_StateIdT>(__state._M_subexpr),
> > +                          __res.second);
> > +      _M_frames.back()._M_bytes[0] = __res.matched;
> >        __res.second = _M_current;
> >        __res.matched = true;
> > -      _M_dfs(__match_mode, __state._M_next);
> > -      __res = __back;
> > +      _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >      }
> >
> >    template<typename _BiIter, typename _Alloc, typename _TraitsT,
> > @@ -280,7 +348,7 @@ namespace __detail
> >      {
> >        const auto& __state = _M_nfa[__i];
> >        if (_M_at_begin())
> > -     _M_dfs(__match_mode, __state._M_next);
> > +     _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >      }
> >
> >    template<typename _BiIter, typename _Alloc, typename _TraitsT,
> > @@ -290,7 +358,7 @@ namespace __detail
> >      {
> >        const auto& __state = _M_nfa[__i];
> >        if (_M_at_end())
> > -     _M_dfs(__match_mode, __state._M_next);
> > +     _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >      }
> >
> >    template<typename _BiIter, typename _Alloc, typename _TraitsT,
> > @@ -300,7 +368,7 @@ namespace __detail
> >      {
> >        const auto& __state = _M_nfa[__i];
> >        if (_M_word_boundary() == !__state._M_neg)
> > -     _M_dfs(__match_mode, __state._M_next);
> > +     _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >      }
> >
> >    // Here __state._M_alt offers a single start node for a sub-NFA.
> > @@ -312,7 +380,7 @@ namespace __detail
> >      {
> >        const auto& __state = _M_nfa[__i];
> >        if (_M_lookahead(__state._M_alt) == !__state._M_neg)
> > -     _M_dfs(__match_mode, __state._M_next);
> > +     _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >      }
> >
> >    template<typename _BiIter, typename _Alloc, typename _TraitsT,
> > @@ -321,16 +389,15 @@ namespace __detail
> >      _M_handle_match(_Match_mode __match_mode, _StateIdT __i)
> >      {
> >        const auto& __state = _M_nfa[__i];
> > -
> >        if (_M_current == _M_end)
> >       return;
> >        if constexpr (__dfs_mode)
> >       {
> >         if (__state._M_matches(*_M_current))
> >           {
> > +           _M_frames.emplace_back(_S_fopcode_restore_current, 0,
> _M_current);
> >             ++_M_current;
> > -           _M_dfs(__match_mode, __state._M_next);
> > -           --_M_current;
> > +           _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >           }
> >       }
> >        else
> > @@ -413,13 +480,12 @@ namespace __detail
> >       {
> >         if (__last != _M_current)
> >           {
> > -           auto __backup = _M_current;
> > +           _M_frames.emplace_back(_S_fopcode_restore_current, 0,
> _M_current);
> >             _M_current = __last;
> > -           _M_dfs(__match_mode, __state._M_next);
> > -           _M_current = __backup;
> > +           _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >           }
> >         else
> > -         _M_dfs(__match_mode, __state._M_next);
> > +         _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
> >       }
> >      }
> >
> > @@ -483,31 +549,26 @@ namespace __detail
> >      _M_handle_alternative(_Match_mode __match_mode, _StateIdT __i)
> >      {
> >        const auto& __state = _M_nfa[__i];
> > -
> >        if (_M_nfa._M_flags & regex_constants::ECMAScript)
> >       {
> >         // TODO: Fix BFS support. It is wrong.
> > -       _M_dfs(__match_mode, __state._M_alt);
> >         // Pick lhs if it matches. Only try rhs if it doesn't.
> > -       if (!_M_has_sol)
> > -         _M_dfs(__match_mode, __state._M_next);
> > +       _M_frames.emplace_back(_S_fopcode_fallback_next,
> __state._M_next);
> > +       _M_frames.emplace_back(_S_fopcode_next, __state._M_alt);
> >       }
> >        else
> >       {
> >         // Try both and compare the result.
> >         // See "case _S_opcode_accept:" handling above.
> > -       _M_dfs(__match_mode, __state._M_alt);
> > -       auto __has_sol = _M_has_sol;
> > -       _M_has_sol = false;
> > -       _M_dfs(__match_mode, __state._M_next);
> > -       _M_has_sol |= __has_sol;
> > +       _M_frames.emplace_back(_S_fopcode_posix_alternative,
> __state._M_next);
> > +       _M_frames.emplace_back(_S_fopcode_next, __state._M_alt);
> >       }
> >      }
> >
> >    template<typename _BiIter, typename _Alloc, typename _TraitsT,
> >          bool __dfs_mode>
> >      void _Executor<_BiIter, _Alloc, _TraitsT, __dfs_mode>::
> > -    _M_dfs(_Match_mode __match_mode, _StateIdT __i)
> > +    _M_node(_Match_mode __match_mode, _StateIdT __i)
> >      {
> >        if (_M_states._M_visited(__i))
> >       return;
> > @@ -545,6 +606,72 @@ namespace __detail
> >       }
> >      }
> >
> > +  template<typename _BiIter, typename _Alloc, typename _TraitsT,
> > +        bool __dfs_mode>
> > +    void _Executor<_BiIter, _Alloc, _TraitsT, __dfs_mode>::
> > +    _M_dfs(_Match_mode __match_mode, _StateIdT __start)
>
> This function, now that it's non-recursive, seems like a good candidate
> for the 'flatten' attribute to ensure that of its subroutines (all of
> which are
> small, and most of which have one call site) get inlined.  It'd also ensure
> that the emplace_back and _ExecutorFrame ctor calls get inlined.
>
> For
>
>      for (int i = 0; i < 10000; i++)
>         std::regex_match(
>             std::string(200, 'a'),
>             std::regex("(a|b|c)*")
>         );
>
> compiled with -O2, this results in a 2x speedup, while also reducing code
> size
> by 20%!  So it seems like there's no downsides to using flatten here?
>
Today I learned about the existence of this attribute, but results look
promising.

>
> > +    {
> > +      _M_frames.emplace_back(_S_fopcode_next, __start);
> > +
> > +      while (!_M_frames.empty())
> > +     {
> > +       _ExecutorFrame<_BiIter> __frame = std::move(_M_frames.back());
> > +       _M_frames.pop_back();
> > +
> > +       switch (__frame._M_op)
> > +         {
> > +         case _S_fopcode_fallback_next:
> > +           if (_M_has_sol)
> > +             break;
> > +           // fallthrough
> > +         case _S_fopcode_next:
> > +           _M_node(__match_mode, __frame._M_state_id);
> > +           break;
> > +
> > +         case _S_fopcode_fallback_rep_once_more:
> > +           if (_M_has_sol)
> > +             break;
> > +           // fallthrough
> > +         case _S_fopcode_rep_once_more:
> > +           _M_rep_once_more(__match_mode, __frame._M_state_id);
> > +           break;
> > +
> > +         case _S_fopcode_posix_alternative:
> > +           _M_frames.emplace_back(_S_fopcode_merge_sol, 0, _M_has_sol);
> > +           _M_frames.emplace_back(_S_fopcode_next, __frame._M_state_id);
> > +           _M_has_sol = false;
> > +           break;
> > +
> > +         case _S_fopcode_merge_sol:
> > +           _M_has_sol |= __frame._M_val;
> > +           break;
> > +
> > +         case _S_fopcode_restore_current:
> > +           _M_current = __frame._M_pos;
> > +           break;
> > +
> > +         case _S_fopcode_restore_cur_results:
> > +           if (__frame._M_bytes[0] == 0xff)
> > +             _M_cur_results[__frame._M_state_id].first = __frame._M_pos;
> > +           else
> > +             {
> > +               _M_cur_results[__frame._M_state_id].second =
> __frame._M_pos;
> > +               _M_cur_results[__frame._M_state_id].matched =
> __frame._M_bytes[0];
> > +             }
> > +           break;
> > +
> > +         case _S_fopcode_restore_rep_count:
> > +           _M_rep_count[__frame._M_state_id].first = __frame._M_pos;
> > +           _M_rep_count[__frame._M_state_id].second =
> __frame._M_bytes[0];
> > +           break;
> > +
> > +         case _S_fopcode_decrement_rep_count:
> > +           _M_rep_count[__frame._M_state_id].second--;
> > +           break;
> > +         }
> > +     }
> > +    }
> > +
> >    // Return whether now is at some word boundary.
> >    template<typename _BiIter, typename _Alloc, typename _TraitsT,
> >          bool __dfs_mode>
> > @@ -569,6 +696,7 @@ namespace __detail
> >
> >        return __left_is_word != __right_is_word;
> >      }
> > +_GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2)
> >  } // namespace __detail
> >  #pragma GCC diagnostic pop
> >
> > diff --git a/libstdc++-v3/include/std/regex
> b/libstdc++-v3/include/std/regex
> > index c8fecfb2ad7a..003499c1bab5 100644
> > --- a/libstdc++-v3/include/std/regex
> > +++ b/libstdc++-v3/include/std/regex
> > @@ -40,6 +40,7 @@
> >  #else
> >
> >  #include <bitset>
> > +#include <cstdint>
> >  #include <locale>
> >  #include <stack>
> >  #include <stdexcept>
> > --
> > 2.53.0.rc1.65.gea24e2c554
> >
> >
>
>

Reply via email to