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.