https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90773

--- Comment #2 from H.J. Lu <hjl.tools at gmail dot com> ---
Something like this:

diff --git a/gcc/expr.c b/gcc/expr.c
index c78bc74c0d9..4412aa7518c 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1090,10 +1090,13 @@ op_by_pieces_d::op_by_pieces_d (rtx to, bool to_load,
 void
 op_by_pieces_d::run ()
 {
+  bool started = false;
+
   while (m_max_size > 1 && m_len > 0)
     {
       scalar_int_mode mode = widest_int_mode_for_size (m_max_size);

+repeat:
       if (prepare_mode (mode, m_align))
        {
          unsigned int size = GET_MODE_SIZE (mode);
@@ -1101,6 +1104,8 @@ op_by_pieces_d::run ()

          while (m_len >= size)
            {
+             started = true;
+
              if (m_reverse)
                m_offset -= size;

@@ -1124,6 +1129,24 @@ op_by_pieces_d::run ()
          finish_mode (mode);
        }

+      if (m_len == 0)
+       break;
+
+      if (started)
+       {
+         mode = smallest_int_mode_for_size (m_len * BITS_PER_UNIT);
+         unsigned int last_gap = GET_MODE_SIZE (mode) - m_len;
+         if (last_gap)
+           {
+             if (m_reverse)
+               m_offset += last_gap;
+             else
+               m_offset -= last_gap;
+             m_len += last_gap;
+             goto repeat;
+           }
+       }
+
       m_max_size = GET_MODE_SIZE (mode);
     }

which should be opt-in by target.

Reply via email to