In the RISC-V backend, there is code that does a zero-extend by default for QImode compare arguments. This is because zero-extend is a single and insn, whereas sign-extend is two shifts. However, if we have two values that are already sign extended, or a sign extended value and a CONST_INT, then we get better code if we do the sign-extend, as this requires 0 instructions.
Without the patch, for a switch using signed char, I get andi a0,a0,0xff li a5,4 bgtu a0,a5,.L1 and with the patch the andi is optimized aways. This patch contains two testcases. The switch-si.c testcase requires the previous patch to pass. The switch-qi.c testcase requires both this patch and the previous patch to pass. This was tested with riscv{32,64}-{elf,linux} cross builds and testsuite runs. There were no regressions. Since this is a RISC-V specific patch, I can self approve it. Jim gcc/ * config/riscv/riscv.c (riscv_extend_comparands): In unsigned QImode test, check for sign extended subreg and/or constant operands, and do a sign extend in that case. gcc/testsuite/ * gcc.target/riscv/switch-qi.c: New. * gcc.target/riscv/switch-si.c: New. --- gcc/config/riscv/riscv.c | 14 ++++++++++++-- gcc/testsuite/gcc.target/riscv/switch-qi.c | 15 +++++++++++++++ gcc/testsuite/gcc.target/riscv/switch-si.c | 15 +++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.target/riscv/switch-qi.c create mode 100644 gcc/testsuite/gcc.target/riscv/switch-si.c diff --git a/gcc/config/riscv/riscv.c b/gcc/config/riscv/riscv.c index 2a8f87d1e94..b4975888bbb 100644 --- a/gcc/config/riscv/riscv.c +++ b/gcc/config/riscv/riscv.c @@ -2002,8 +2002,18 @@ riscv_extend_comparands (rtx_code code, rtx *op0, rtx *op1) /* Comparisons consider all XLEN bits, so extend sub-XLEN values. */ if (GET_MODE_SIZE (word_mode) > GET_MODE_SIZE (GET_MODE (*op0))) { - /* It is more profitable to zero-extend QImode values. */ - if (unsigned_condition (code) == code && GET_MODE (*op0) == QImode) + /* It is more profitable to zero-extend QImode values. But not if the + first operand has already been sign-extended, and the second one is + is a constant or has already been sign-extended also. */ + if (unsigned_condition (code) == code + && (GET_MODE (*op0) == QImode + && ! (GET_CODE (*op0) == SUBREG + && SUBREG_PROMOTED_VAR_P (*op0) + && SUBREG_PROMOTED_SIGNED_P (*op0) + && (CONST_INT_P (*op1) + || (GET_CODE (*op1) == SUBREG + && SUBREG_PROMOTED_VAR_P (*op1) + && SUBREG_PROMOTED_SIGNED_P (*op1)))))) { *op0 = gen_rtx_ZERO_EXTEND (word_mode, *op0); if (CONST_INT_P (*op1)) diff --git a/gcc/testsuite/gcc.target/riscv/switch-qi.c b/gcc/testsuite/gcc.target/riscv/switch-qi.c new file mode 100644 index 00000000000..973d09aaaf1 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/switch-qi.c @@ -0,0 +1,15 @@ +/* { dg-do compile { target { riscv64*-*-* } } } */ +/* { dg-options "-march=rv64gc -mabi=lp64 -O2" } */ + +/* Test for riscv_extend_comparands patch. */ +extern void asdf(int); +void foo(signed char x) { + switch (x) { + case 0: asdf(10); break; + case 1: asdf(11); break; + case 2: asdf(12); break; + case 3: asdf(13); break; + case 4: asdf(14); break; + } +} +/* { dg-final { scan-assembler-not "andi" } } */ diff --git a/gcc/testsuite/gcc.target/riscv/switch-si.c b/gcc/testsuite/gcc.target/riscv/switch-si.c new file mode 100644 index 00000000000..de4d68f4d0e --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/switch-si.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +/* Test for do_tablejump patch. */ +extern void asdf(int); +void foo(int x) { + switch (x) { + case 0: asdf(10); break; + case 1: asdf(11); break; + case 2: asdf(12); break; + case 3: asdf(13); break; + case 4: asdf(14); break; + } +} +/* { dg-final { scan-assembler-not "srli" } } */ -- 2.14.1