https://gcc.gnu.org/g:e5990a6ce611f522b8f48c2b469983da19d39777
commit r15-7208-ge5990a6ce611f522b8f48c2b469983da19d39777 Author: Jeff Law <j...@ventanamicro.com> Date: Sat Jan 25 09:42:19 2025 -0700 [RISC-V][PR target/116256] Improve handling of single bit constants So under the umbrella of pr116256 (P3 regression) I've been exploring removal of the mvconst_internal pattern. Not surprisingly, that's going to cause all kinds of undesirable fallout. While I can kind of see a path forward for that work, it's going to require some combine work that I don't think we want to tackle in the context of gcc-15. Essentially without mvconst_internal we'll have fully exposed constant synthesis prior to combine. Remember that combine has limits on what combinations it will perform based on how many instructions are in the source sequence. If we need 2+ instructions to synthesize the constant, those eat into our budget. In a world without mvconst_internal we'd need to either improve combine to handle 5 insns cases (which do show up in the testsuite) or we need to significantly improve how combine handles REG_EQUAL notes. 5 insn combinations sound like insanity to me. So I'd tend to lean towards the latter, though that's going to need some refactoring and diving into note redistribution (ugh!). In the mean time we can start limiting mvconst_internal. For the remaining case in pr116256 we have this code in combine: > (insn 8 5 10 2 (set (reg:V2048HF 138 [ _5 ]) > (vec_duplicate:V2048HF (reg:HF 142 [ x ]))) "j.c":152:11 3712 {*vec_duplicatev2048hf} > (expr_list:REG_DEAD (reg:HF 142 [ x ]) > (nil))) > (insn 10 8 11 2 (set (reg:DI 139) > (const_int 2048 [0x800])) "j.c":152:11 275 {*mvconst_internal} > (nil)) (insn 11 10 0 2 (set (mem:V2048HF (reg/f:DI 141 [ in ]) [1 MEM <vector(2048) _Float16> [(_Float16 *)in_7(D)]+0 S4096 A128]) > (if_then_else:V2048HF (unspec:V2048BI [ > (const_vector:V2048BI [ > (const_int 1 [0x1]) repeated x2048 > ]) > (reg:DI 139) > (const_int 2 [0x2]) repeated x3 > (reg:SI 66 vl) > (reg:SI 67 vtype) > ] UNSPEC_VPREDICATE) > (reg:V2048HF 138 [ _5 ]) > (unspec:V2048HF [ > (reg:DI 0 zero) > ] UNSPEC_VUNDEF))) "j.c":152:11 3843 {*pred_movv2048hf} > (expr_list:REG_DEAD (reg/f:DI 141 [ in ]) > (expr_list:REG_DEAD (reg:DI 0 zero) > (expr_list:REG_DEAD (reg:SI 66 vl) > (expr_list:REG_DEAD (reg:SI 67 vtype) > (expr_list:REG_DEAD (reg:V2048HF 138 [ _5 ]) > (expr_list:REG_DEAD (reg:DI 139) > (nil)))))))) Note a couple things. First insn 8 will be split shortly after combine and will need the constant 2048. But that's obviously exposed late. Second (of course) is the mvconst_internal pattern at insn 10. After split1 we'll have: > (insn 16 5 17 2 (set (reg:DI 144) (const_int 4096 [0x1000])) "j.c":152:11 -1 > (nil)) > (insn 17 16 18 2 (set (reg:DI 143) > (plus:DI (reg:DI 144) > (const_int -2048 [0xfffffffffffff800]))) "j.c":152:11 -1 > (expr_list:REG_EQUAL (const_int 2048 [0x800]) > (nil))) > (insn 18 17 19 2 (set (reg:V2048HF 138 [ _5 ]) > (if_then_else:V2048HF (unspec:V2048BI [ (const_vector:V2048BI [ > (const_int 1 [0x1]) repeated x2048 > ]) > (reg:DI 143) > (const_int 2 [0x2]) repeated x3 > (reg:SI 66 vl) > (reg:SI 67 vtype) > ] UNSPEC_VPREDICATE) > (vec_duplicate:V2048HF (reg:HF 142 [ x ])) > (unspec:V2048HF [ (reg:DI 0 zero) > ] UNSPEC_VUNDEF))) "j.c":152:11 -1 > (nil)) > (insn 19 18 20 2 (set (reg:DI 145) > (const_int 4096 [0x1000])) "j.c":152:11 -1 > (nil)) > (insn 20 19 11 2 (set (reg:DI 139) > (plus:DI (reg:DI 145) > (const_int -2048 [0xfffffffffffff800]))) "j.c":152:11 -1 > (expr_list:REG_EQUAL (const_int 2048 [0x800]) > (nil))) > (insn 11 20 0 2 (set (mem:V2048HF (reg/f:DI 141 [ in ]) [1 MEM <vector(2048) _Float16> [(_Float16 *)in_7(D)]+0 S4096 A128]) > (if_then_else:V2048HF (unspec:V2048BI [ > (const_vector:V2048BI [ > (const_int 1 [0x1]) repeated x2048 > ]) > (reg:DI 139) (const_int 2 [0x2]) repeated x3 > (reg:SI 66 vl) > (reg:SI 67 vtype) > ] UNSPEC_VPREDICATE) > (reg:V2048HF 138 [ _5 ]) > (unspec:V2048HF [ (reg:DI 0 zero) > ] UNSPEC_VUNDEF))) "j.c":152:11 3843 {*pred_movv2048hf} > (expr_list:REG_DEAD (reg/f:DI 141 [ in ]) > (expr_list:REG_DEAD (reg:DI 0 zero) (expr_list:REG_DEAD (reg:SI 66 vl) > (expr_list:REG_DEAD (reg:SI 67 vtype) > (expr_list:REG_DEAD (reg:V2048HF 138 [ _5 ]) > (expr_list:REG_DEAD (reg:DI 139) > (nil)))))))) Note the synthesis of 2048 appears twice. I seriously considered adding a local cprop pass at this point. That could be done with a bit of work. It didn't look too bad -- the biggest problem is cprop isn't designed to run once we've left cfglayout. But we could probably finesse that by not allowing it to change jumps if we've left cfglayout or converting it to do the more complex jump fixups. You might ask why the post-reload optimizers don't help since this at least looks like a case where they could. After LRA the RTL looks like: > (insn 26 5 25 2 (set (reg:DI 15 a5 [144]) > (const_int 4096 [0x1000])) "/home/jlaw/test/gcc/gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/dup-1.c":152:11 277 {*movdi_64bit} (expr_list:REG_EQUIV (const_int 4096 [0x1000]) > (nil))) > (insn 25 26 19 2 (set (reg:DI 15 a5 [143]) > (plus:DI (reg:DI 15 a5 [144]) > (const_int -2048 [0xfffffffffffff800]))) "/home/jlaw/test/gcc/gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/dup-1.c":152:11 5 {adddi3} > (expr_list:REG_EQUIV (const_int 2048 [0x800]) > (nil))) > (insn 19 25 20 2 (set (reg:V2048QI 100 v4 [orig:138 _11 ] [138]) > (if_then_else:V2048QI (unspec:V2048BI [ > (const_vector:V2048BI [ > (const_int 1 [0x1]) repeated x2048 > ]) > (reg:DI 15 a5 [143]) > (const_int 2 [0x2]) repeated x3 > (reg:SI 66 vl) > (reg:SI 67 vtype) > ] UNSPEC_VPREDICATE) > (vec_duplicate:V2048QI (reg:QI 12 a2 [145])) > (unspec:V2048QI [ (reg:DI 0 zero) > ] UNSPEC_VUNDEF))) "/home/jlaw/test/gcc/gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/dup-1.c":152:11 4172 {*pred_broadcastv2048qi} > (nil)) (insn 20 19 21 2 (set (reg:DI 15 a5 [146]) > (const_int 4096 [0x1000])) "/home/jlaw/test/gcc/gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/dup-1.c":152:11 277 {*movdi_64bit} (expr_list:REG_EQUIV (const_int 4096 [0x1000]) > (nil))) > (insn 21 20 11 2 (set (reg:DI 15 a5 [139]) > (plus:DI (reg:DI 15 a5 [146]) > (const_int -2048 [0xfffffffffffff800]))) "/home/jlaw/test/gcc/gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/dup-1.c":152:11 5 {adddi3} > (expr_list:REG_EQUIV (const_int 2048 [0x800]) > (nil))) Note the re-use of a5 for the constant synthesis steps. That's going to spoil any chance of reload_cse saving us. That re-use also gets in the way of vsetvl elimination and we ultimately get this code: > foo10: > li a5,4096 > addi a5,a5,-2048 > vsetvli zero,a5,e16,m8,ta,ma > vfmv.v.f v8,fa0 > li a5,4096 > addi a5,a5,-2048 > vsetvli zero,a5,e16,m8,ta,ma > vse16.v v8,0(a0) > ret The regression is we have the obviously redundant vsetvl. The additional copy of the synthesis is undesirable as well. If we filter out single bit constants from mvconst_internal we trivially fix that regression. The only fallout is a class of saturation tests which want to test against 0x80000000. Under the hood this is a minor codegen issue interacting badly with combine's deliberate rejection of simplification of extensions of constants. Rather than constructing the SImode constant, then zero extending the result we can just generate the constant we actually want directly in DImode. The net is we fix the regression, don't introduce any obvious new regressions and slightly reduce our dependence on mvconst_internal. All good in my book. Obviously I'll wait for pre-commit CI to render a verdict. PR target/116256 gcc/ * config/riscv/riscv.md (mvconst_internal): Reject single bit constants. * config/riscv/riscv.cc (riscv_gen_zero_extend_rtx): Improve handling constants. Diff: --- gcc/config/riscv/riscv.cc | 12 +++++++++--- gcc/config/riscv/riscv.md | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index 5a3a05041773..4652454b8fec 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -12684,10 +12684,16 @@ riscv_gen_zero_extend_rtx (rtx x, machine_mode mode) emit_move_insn (xmode_reg, x); else { - rtx reg_x = gen_reg_rtx (mode); + /* Combine deliberately does not simplify extensions of constants + (long story). So try to generate the zero extended constant + efficiently. - emit_move_insn (reg_x, x); - riscv_emit_unary (ZERO_EXTEND, xmode_reg, reg_x); + First extract the constant and mask off all the bits not in MODE. */ + HOST_WIDE_INT val = INTVAL (x); + val &= GET_MODE_MASK (mode); + + /* X may need synthesis, so do not blindly copy it. */ + xmode_reg = force_reg (Xmode, gen_int_mode (val, Xmode)); } return xmode_reg; diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index e4123c912dcb..09053df1eb9b 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -2470,7 +2470,8 @@ (match_operand:GPR 1 "splittable_const_int_operand" "i"))] "!ira_in_progress && !(p2m1_shift_operand (operands[1], <MODE>mode) - || high_mask_shift_operand (operands[1], <MODE>mode))" + || high_mask_shift_operand (operands[1], <MODE>mode) + || exact_log2 (INTVAL (operands[1])) >= 0)" "#" "&& 1" [(const_int 0)]