The current method of treating shifts of extended values on RISC-V frequently causes sequences of 3 shifts, despite the presence of the 'zero_extendsidi2_shifted' pattern.
Consider: unsigned long f(unsigned int a, unsigned long b) { a = a << 1; unsigned long c = (unsigned long) a; c = b + (c<<4); return c; } which will present at combine-time as: Trying 7, 8 -> 9: 7: r78:SI=r81:DI#0<<0x1 REG_DEAD r81:DI 8: r79:DI=zero_extend(r78:SI) REG_DEAD r78:SI 9: r72:DI=r79:DI<<0x4 REG_DEAD r79:DI Failed to match this instruction: (set (reg:DI 72 [ _1 ]) (and:DI (ashift:DI (reg:DI 81) (const_int 5 [0x5])) (const_int 68719476704 [0xfffffffe0]))) and produce the following (optimized) assembly: f: slliw a5,a0,1 slli a5,a5,32 srli a5,a5,28 add a0,a5,a1 ret The current way of handling this (in 'zero_extendsidi2_shifted') doesn't apply for two reasons: - this is seen before reload, and - (more importantly) the constant mask is not 0xfffffffful. To address this, we introduce a generalized version of shifting zero-extended values that supports any mask of consecutive ones as long as the number of training zeros is the inner shift-amount. With this new split, we generate the following assembly for the aforementioned function: f: slli a0,a0,33 srli a0,a0,28 add a0,a0,a1 ret gcc/ChangeLog: * config/riscv/riscv.md (zero_extendsidi2_shifted): Replace with a generalized split that requires no clobber, runs before reload and works for smaller masks. Signed-off-by: Philipp Tomsich <philipp.toms...@vrull.eu> --- gcc/config/riscv/riscv.md | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index b8ab0cf169a..cc10cd90a74 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -2119,23 +2119,26 @@ (define_split ;; occur when unsigned int is used for array indexing. Split this into two ;; shifts. Otherwise we can get 3 shifts. -(define_insn_and_split "zero_extendsidi2_shifted" - [(set (match_operand:DI 0 "register_operand" "=r") - (and:DI (ashift:DI (match_operand:DI 1 "register_operand" "r") - (match_operand:QI 2 "immediate_operand" "I")) - (match_operand 3 "immediate_operand" ""))) - (clobber (match_scratch:DI 4 "=&r"))] - "TARGET_64BIT && !TARGET_ZBA - && ((INTVAL (operands[3]) >> INTVAL (operands[2])) == 0xffffffff)" - "#" - "&& reload_completed" - [(set (match_dup 4) - (ashift:DI (match_dup 1) (const_int 32))) - (set (match_dup 0) - (lshiftrt:DI (match_dup 4) (match_dup 5)))] - "operands[5] = GEN_INT (32 - (INTVAL (operands [2])));" - [(set_attr "type" "shift") - (set_attr "mode" "DI")]) +(define_split + [(set (match_operand:DI 0 "register_operand") + (and:DI (ashift:DI (match_operand:DI 1 "register_operand") + (match_operand:QI 2 "immediate_operand")) + (match_operand:DI 3 "consecutive_bits_operand")))] + "TARGET_64BIT" + [(set (match_dup 0) (ashift:DI (match_dup 1) (match_dup 4))) + (set (match_dup 0) (lshiftrt:DI (match_dup 0) (match_dup 5)))] +{ + unsigned HOST_WIDE_INT mask = UINTVAL (operands[3]); + int leading = clz_hwi (mask); + int trailing = ctz_hwi (mask); + + /* The shift-amount must match the number of trailing bits */ + if (trailing != UINTVAL (operands[2])) + FAIL; + + operands[4] = GEN_INT (leading + trailing); + operands[5] = GEN_INT (leading); +}) ;; ;; .................... -- 2.34.1