https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87763
--- Comment #27 from Jakub Jelinek <jakub at gcc dot gnu.org> --- So, looking at what other targets do on the #c21 testcase, e.g. rs6000 actually matches what the combiner produces: Trying 10, 9 -> 11: 10: r131:SI=r133:DI#0&0xfffffffffff00fff REG_DEAD r133:DI 9: r130:SI=r134:DI#0<<0xc&0xff000 REG_DEAD r134:DI 11: r127:SI=r130:SI|r131:SI REG_DEAD r131:SI REG_DEAD r130:SI Successfully matched this instruction: (set (reg:SI 127) (ior:SI (and:SI (subreg:SI (reg:DI 133) 0) (const_int -1044481 [0xfffffffffff00fff])) (and:SI (ashift:SI (subreg:SI (reg:DI 134) 0) (const_int 12 [0xc])) (const_int 1044480 [0xff000])))) and we have: (insn:TI 11 12 17 (set (reg:SI 3 3 [127]) (ior:SI (and:SI (reg:SI 3 3 [133]) (const_int -1044481 [0xfffffffffff00fff])) (and:SI (ashift:SI (reg:SI 4 4 [134]) (const_int 12 [0xc])) (const_int 1044480 [0xff000])))) "pr87763.c":3:26 240 {*rotlsi3_insert_2} (expr_list:REG_DEAD (reg:SI 4 4 [134]) (nil))) until final. So, any reason why aarch64 can't do that too? It can be just define_insn_and_split that splits it at the first possible split occassion. I see combiner trying: (set (reg/i:SI 0 x0) (ior:SI (and:SI (reg:SI 100) (const_int -1044481 [0xfffffffffff00fff])) (and:SI (ashift:SI (reg:SI 101) (const_int 12 [0xc])) (const_int 1044480 [0xff000])))) Should be easy to verify one of the constants has number of trailing zeros equal to the shift count, then 1 or more one bits and then all ones above it and the other constants being negation thereof. For shift count zero that is obviously a different pattern, and probably for the swapping of the two ior arguments if combiner doesn't canonicalize them always one way; you probably need a couple of other patterns, but it all should be doable in stage4.