https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63321
--- Comment #6 from Oleg Endo <olegendo at gcc dot gnu.org> --- The shll/shlr insns effectively perform two operations: T = zero_extract single bit 0 / 31 from reg reg = reg << 1 / reg >> 1. The other shift insns as in comment #5 perform only a single operation. Thus those two things should be probably handled slightly differently. With my current patchset for handling single bit zero_extract (PR 64345), code like void test4 (unsigned int x, unsigned int* y) { y[0] = (x >> 0) & 1; y[1] = (x >> 1) & 1; y[2] = (x >> 2) & 1; y[3] = (x >> 3) & 1; } results in the following insns right after the combine pass: (insn 7 4 8 2 (set (reg:SI 171 [ D.1733 ]) (and:SI (reg/v:SI 169 [ x ]) (const_int 1 [0x1]))) sh_tmp.cpp:432 115 {*andsi_compact} (nil)) ... (insn 10 9 11 2 (parallel [ (set (reg:SI 173 [ D.1733 ]) (zero_extract:SI (reg/v:SI 169 [ x ]) (const_int 1 [0x1]) (const_int 1 [0x1]))) (clobber (reg:SI 147 t)) ]) sh_tmp.cpp:433 409 {any_treg_expr_to_reg} (expr_list:REG_UNUSED (reg:SI 147 t) (nil))) ... (insn 13 12 14 2 (parallel [ (set (reg:SI 175 [ D.1733 ]) (zero_extract:SI (reg/v:SI 169 [ x ]) (const_int 1 [0x1]) (const_int 2 [0x2]))) (clobber (reg:SI 147 t)) ]) sh_tmp.cpp:434 409 {any_treg_expr_to_reg} (expr_list:REG_UNUSED (reg:SI 147 t) (nil))) ... (insn 16 15 17 2 (parallel [ (set (reg:SI 177 [ D.1733 ]) (zero_extract:SI (reg/v:SI 169 [ x ]) (const_int 1 [0x1]) (const_int 3 [0x3]))) (clobber (reg:SI 147 t)) ]) sh_tmp.cpp:435 409 {any_treg_expr_to_reg} (expr_list:REG_UNUSED (reg:SI 147 t) (expr_list:REG_DEAD (reg/v:SI 169 [ x ]) (nil)))) Those pseudo-insns are then split into tst/bld/movt/movrt sequences in the split1 pass. If a special shll/shlr pass is done right after combine and before split1, it's possible to identify potential good shll/shlr sequences rather easily and rewrite the code to use shll/shlr instead.