https://gcc.gnu.org/g:f95a1c7f4e4b1f4c41e40d2eab8cbde258292962
commit r17-4462-gf95a1c7f4e4b1f4c41e40d2eab8cbde258292962 Author: Milan Tripkovic <[email protected]> Date: Sat Sep 19 07:55:27 2026 -0600 [PATCH] RISC-V: PR target/126074 - Missing cases for constant synthesis on RISC-V This patch implements the shNadd.uw case from the PR. The instruction shNadd.uw a0, a0, a0 computes: ((a0 & 0xffffffff) << N) + a0 If a0 holds a value sign extended from bit 31, with u being its low 32 bits, this formula evaluates to: u * (2^N + 1) - 2^32 This allows us to build certain 64-bit constants using just a lui and a single shNadd.uw. To find u, riscv_build_integer_1 reverses the formula: it adds 2^32 to the constant and divide it by 3, 5, or 9. If the quotient fits in 32 bits with bit 31 set, and the new sequence is cheaper, we emit an FMA entry with use_uw set. For 0x180005000, before: li a0,1572864 addi t0,a0,5 slli a0,t0,12 after: li a0,-715821056 sh1add.uw a0,a0,a0 Finally, in riscv_move_integer, the FMA check is moved above the use_uw check. Since our shNadd.uw case sets use_uw = true, checking use_uw first would mistakenly treat the operation as a plain zero-extended shift instead of shNadd.uw. PR target/126074 gcc/ChangeLog: * config/riscv/riscv.cc (riscv_build_integer_1): Add logic to synthesize constants using shNadd.uw. (riscv_move_integer): Handle FMA with use_uw for RTL emission. gcc/testsuite/ChangeLog: * gcc.target/riscv/pr126074.c: New test. Diff: --- gcc/config/riscv/riscv.cc | 62 +++++++++++++++++++++++++++---- gcc/testsuite/gcc.target/riscv/pr126074.c | 36 ++++++++++++++++++ 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index f20f2a6bd3e1..9fca2f679a07 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -1580,6 +1580,42 @@ riscv_build_integer_1 (struct riscv_integer_op codes[RISCV_MAX_INTEGER_OPS], memcpy (codes, alt_codes, sizeof (alt_codes)); cost = alt_cost; } + if (cost > 2 && mode == DImode) + { + unsigned HOST_WIDE_INT uval = value; + + if (uval < (HOST_WIDE_INT_UC (1) << 35)) + { + unsigned HOST_WIDE_INT d = uval + (HOST_WIDE_INT_UC (1) << 32); + + for (int n = 1; n <= 3; n++) + { + unsigned HOST_WIDE_INT mult = (HOST_WIDE_INT_UC (1) << n) + 1; + + if (d % mult) + continue; + unsigned HOST_WIDE_INT u = d / mult; + + /* With bit 31 clear there is no sign extension, the result + is u * (2^N + 1), and the plain shNadd case above already + handles it. */ + if (u >= (HOST_WIDE_INT_UC (1) << 32) + || (u & HOST_WIDE_INT_UC (0x80000000)) == 0) + continue; + alt_cost = 1 + riscv_build_integer_1 (alt_codes, + sext_hwi (u, 32), mode); + + if (alt_cost >= cost) + continue; + alt_codes[alt_cost - 1].code = FMA; + alt_codes[alt_cost - 1].value = mult; + alt_codes[alt_cost - 1].use_uw = true; + alt_codes[alt_cost - 1].save_temporary = false; + memcpy (codes, alt_codes, sizeof (alt_codes)); + cost = alt_cost; + } + } + } } /* We might be able to generate a constant close to our target @@ -3616,6 +3652,24 @@ riscv_move_integer (rtx temp, rtx dest, HOST_WIDE_INT value) /* UNKNOWN means load the constant value into X. */ x = GEN_INT (codes[i].value); } + else if (codes[i].code == FMA) + { + int value = exact_log2 (codes[i].value - 1); + rtx t = can_create_pseudo_p () ? gen_reg_rtx (mode) : temp; + rtx op; + gcc_assert (value >= 1 && value <= 3); + + /* This case is for shNadd.uw. */ + if (codes[i].use_uw) + op = gen_rtx_AND (mode, + gen_rtx_ASHIFT (mode, x, GEN_INT (value)), + GEN_INT (HOST_WIDE_INT_UC (0xffffffff) + << value)); + else + op = gen_rtx_ASHIFT (mode, x, GEN_INT (value)); + + x = riscv_emit_set (t, gen_rtx_PLUS (mode, op, x)); + } else if (codes[i].use_uw) { /* If the sequence requires using a "uw" form of an insn, we're @@ -3636,14 +3690,6 @@ riscv_move_integer (rtx temp, rtx dest, HOST_WIDE_INT value) x = gen_rtx_fmt_ee (AND, mode, x, GEN_INT (value)); x = riscv_emit_set (t, x); } - else if (codes[i].code == FMA) - { - HOST_WIDE_INT value = exact_log2 (codes[i].value - 1); - rtx ashift = gen_rtx_fmt_ee (ASHIFT, mode, x, GEN_INT (value)); - x = gen_rtx_fmt_ee (PLUS, mode, ashift, x); - rtx t = can_create_pseudo_p () ? gen_reg_rtx (mode) : temp; - x = riscv_emit_set (t, x); - } else if (codes[i].code == CONCAT || codes[i].code == VEC_MERGE) { if (codes[i].code == CONCAT && !TARGET_ZBKB) diff --git a/gcc/testsuite/gcc.target/riscv/pr126074.c b/gcc/testsuite/gcc.target/riscv/pr126074.c new file mode 100644 index 000000000000..6a5886f3ce89 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr126074.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target rv64 } */ +/* { dg-options "-march=rv64gc_zba_zbb_zbs" } */ + +unsigned long foo_sh1add_0x180011000 (void) { return 0x180011000UL; } +unsigned long foo_sh1add_0x180017000 (void) { return 0x180017000UL; } +unsigned long foo_sh1add_0x18001d000 (void) { return 0x18001d000UL; } +unsigned long foo_sh1add_0x180005000 (void) { return 0x180005000UL; } + +unsigned long foo_sh2add_0x18000f000 (void) { return 0x18000f000UL; } +unsigned long foo_sh2add_0x180019000 (void) { return 0x180019000UL; } +unsigned long foo_sh2add_0x3c82b3000 (void) { return 0x3c82b3000UL; } + +unsigned long foo_sh3add_0x380009000 (void) { return 0x380009000UL; } +unsigned long foo_sh3add_0x38001b000 (void) { return 0x38001b000UL; } +unsigned long foo_sh3add_0x5914a6000 (void) { return 0x5914a6000UL; } +unsigned long foo_sh3add_0x7cf04c000 (void) { return 0x7cf04c000UL; } + +unsigned long foo_sh1add_0x1ffffd000 (void) { return 0x1ffffd000UL; } +unsigned long foo_sh2add_0x3ffffb000 (void) { return 0x3ffffb000UL; } +unsigned long foo_sh3add_0x7ffff7000 (void) { return 0x7ffff7000UL; } + +unsigned long foo_neg_0x80000000 (void) { return 0x80000000UL; } +unsigned long foo_neg_0x180000000 (void) { return 0x180000000UL; } +unsigned long foo_neg_0x380000000 (void) { return 0x380000000UL; } + +unsigned long foo_neg_0x400000000 (void) { return 0x400000000UL; } + +unsigned long foo_neg_0x800000000 (void) { return 0x800000000UL; } +unsigned long foo_neg_0x1800050000000 (void) { return 0x1800050000000UL; } + +unsigned long foo_neg_0x7ffffffff (void) { return 0x7ffffffffUL; } + +unsigned long foo_neg_0x37fff7000 (void) { return 0x37fff7000UL; } + +/* { dg-final { scan-assembler-times {\msh[123]add\.uw\M} 14 } } */
