From 868fc62791b6b0f10484cc6fa15f81911418e605 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 28 Nov 2022 19:36:15 +0000 Subject: [PATCH] RISC-V: Avoid redundant sign-extension for SImode SGE, SGEU, SLE, SLEU We produce inefficient code for some synthesized SImode conditional set operations (i.e. ones that are not directly implemented in hardware) on RV64. For example a piece of C code like this: int sleu (unsigned int x, unsigned int y) { return x <= y; } gets compiled (at `-O2') to this: sleu: sgtu a0,a0,a1 # 9 [c=4 l=4] *sgtu_disi xori a0,a0,1 # 10 [c=4 l=4] *xorsi3_internal/1 andi a0,a0,1 # 16 [c=4 l=4] anddi3/1 ret # 25 [c=0 l=4] simple_return or (at `-O1') to this: sleu: sgtu a0,a0,a1 # 9 [c=4 l=4] *sgtu_disi xori a0,a0,1 # 10 [c=4 l=4] *xorsi3_internal/1 sext.w a0,a0 # 16 [c=4 l=4] extendsidi2/0 ret # 24 [c=0 l=4] simple_return This is because the middle end expands a SLEU operation missing from RISC-V hardware into a sequence of a SImode SGTU operation followed by an explicit SImode XORI operation with immediate 1. And while the SGTU machine instruction (alias SLTU with the input operands swapped) gives a properly sign-extended 32-bit result which is valid both as a SImode or a DImode operand the middle end does not see that through a SImode XORI operation, because we tell the middle end that the RISC-V target (unlike MIPS) may hold values in DImode integer registers that are valid for SImode operations even if not properly sign-extended. However the RISC-V psABI requires that 32-bit function arguments and results passed in 64-bit integer registers be properly sign-extended, so this is explicitly done at the conclusion of the function. Fix this by making the backend use a sequence of a DImode SGTU operation followed by a SImode SEQZ operation instead. The latter operation is known by the middle end to produce a properly sign-extended 32-bit result and therefore combine gets rid of the sign-extension operation that follows and actually folds it into the very same XORI machine operation resulting in: sleu: sgtu a0,a0,a1 # 9 [c=4 l=4] *sgtu_didi xori a0,a0,1 # 16 [c=4 l=4] xordi3/1 ret # 25 [c=0 l=4] simple_return instead (although the SEQZ alias SLTIU against immediate 1 machine instruction would equally do and is actually retained at `-O0'). This is handled analogously for the remaining synthesized operations of this kind, i.e. `SLE', `SGEU', and `SGE'. gcc/ * config/riscv/riscv.cc (riscv_emit_int_order_test): Use EQ 0 rather that XOR 1 for LE and LEU operations. gcc/testsuite/ * gcc.target/riscv/sge.c: New test. * gcc.target/riscv/sgeu.c: New test. * gcc.target/riscv/sle.c: New test. * gcc.target/riscv/sleu.c: New test. --- gcc/config/riscv/riscv.cc | 4 ++-- gcc/testsuite/gcc.target/riscv/sge.c | 12 ++++++++++++ gcc/testsuite/gcc.target/riscv/sgeu.c | 12 ++++++++++++ gcc/testsuite/gcc.target/riscv/sle.c | 12 ++++++++++++ gcc/testsuite/gcc.target/riscv/sleu.c | 12 ++++++++++++ 5 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.target/riscv/sge.c create mode 100644 gcc/testsuite/gcc.target/riscv/sgeu.c create mode 100644 gcc/testsuite/gcc.target/riscv/sle.c create mode 100644 gcc/testsuite/gcc.target/riscv/sleu.c diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index 74612a7..ab02a81 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -3004,9 +3004,9 @@ riscv_emit_int_order_test (enum rtx_code code, bool *invert_ptr, } else if (invert_ptr == 0) { - rtx inv_target = riscv_force_binary (GET_MODE (target), + rtx inv_target = riscv_force_binary (word_mode, inv_code, cmp0, cmp1); - riscv_emit_binary (XOR, target, inv_target, const1_rtx); + riscv_emit_binary (EQ, target, inv_target, const0_rtx); } else { diff --git a/gcc/testsuite/gcc.target/riscv/sge.c b/gcc/testsuite/gcc.target/riscv/sge.c new file mode 100644 index 0000000..5f7e7ae --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/sge.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target rv64 } */ +/* { dg-skip-if "" { *-*-* } { "-O0" } } */ + +int +sge (int x, int y) +{ + return x >= y; +} + +/* { dg-final { scan-assembler "\\sxori\\sa0,a0,1\n\\sret\n" } } */ +/* { dg-final { scan-assembler-not "andi|sext\\.w" } } */ diff --git a/gcc/testsuite/gcc.target/riscv/sgeu.c b/gcc/testsuite/gcc.target/riscv/sgeu.c new file mode 100644 index 0000000..234b9aa --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/sgeu.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target rv64 } */ +/* { dg-skip-if "" { *-*-* } { "-O0" } } */ + +int +sgeu (unsigned int x, unsigned int y) +{ + return x >= y; +} + +/* { dg-final { scan-assembler "\\sxori\\sa0,a0,1\n\\sret\n" } } */ +/* { dg-final { scan-assembler-not "andi|sext\\.w" } } */ diff --git a/gcc/testsuite/gcc.target/riscv/sle.c b/gcc/testsuite/gcc.target/riscv/sle.c new file mode 100644 index 0000000..3259c19 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/sle.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target rv64 } */ +/* { dg-skip-if "" { *-*-* } { "-O0" } } */ + +int +sle (int x, int y) +{ + return x <= y; +} + +/* { dg-final { scan-assembler "\\sxori\\sa0,a0,1\n\\sret\n" } } */ +/* { dg-final { scan-assembler-not "andi|sext\\.w" } } */ diff --git a/gcc/testsuite/gcc.target/riscv/sleu.c b/gcc/testsuite/gcc.target/riscv/sleu.c new file mode 100644 index 0000000..301b8c3 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/sleu.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target rv64 } */ +/* { dg-skip-if "" { *-*-* } { "-O0" } } */ + +int +sleu (unsigned int x, unsigned int y) +{ + return x <= y; +} + +/* { dg-final { scan-assembler "\\sxori\\sa0,a0,1\n\\sret\n" } } */ +/* { dg-final { scan-assembler-not "andi|sext\\.w" } } */ -- 2.7.4