So this improves our ability to select across reg,-1. The early
versions of this patch allowed const,-1, but those sequences weren't any
better and occasionally ever-so-slightly worse, so those are rejected.
I've spot checked spec2017 where it does show up, but it's not clear
that it's showing up in any hot code.
The basic idea is to use an sCC to generate 1,0, subtract 1 giving 0,
-1. Then we can IOR that with the other input. Concretely:
int f(int a, int b, int c)
{
a = -1;
if (c < 10) a = b;
return a;
}
Currently generates:
li a5,9
addi a1,a1,1
sgt a2,a2,a5
czero.nez a2,a1,a2
addi a0,a2,-1
ret
After this patch:
slti a0,a2,10
addi a0,a0,-1
or a0,a0,a1
ret
Probably the same performance on 4+ wide designs (and perhaps often on a
2 wide designs). But it encodes a lot more efficiently, 18 bytes for
the first sequence, just 10 bytes for the second. That can be important
on some designs, particularly since if-converted blocks are more likely
to be large and/or cross cache line boundaries.
This has been bootstrapped and regression tested on x86, and riscv64.
The riscv64 bootstraps were on the Pioneer, K1 (early version of the
patch) and K3 (most recent versions). It's also been tested on all the
*-elf platforms in my tester as well as additional bootstraps on
platforms like alpha, sh4, etc.
I'll wait for a final confirmation from the pre-commit tester before
moving forward.
Jeff
PR rtl-optimization/123313
gcc/
* ifcvt.cc (noce_try_store_flag_logical): New function.
(noce_process_if_block): Call it.
gcc/testsuite/
* gcc.target/riscv/pr123313.c: New test.
* gcc.target/riscv/pr124009.c: Adjust expected output.
diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index c80a02b2255b..37e757461934 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -1778,6 +1778,75 @@ noce_try_store_flag_constants (struct noce_if_info
*if_info)
return false;
}
+/* This is trying to capture cases like dest = cond ? a : -1.
+
+ The basic idea is to use a store-flag insn to generate a -1/0
+ value (directly or indirectly), then IOR that with the other
+ input. */
+static bool
+noce_try_store_flag_logical (struct noce_if_info *if_info)
+{
+ rtx a = if_info->a;
+ rtx b = if_info->b;
+ rtx dest = if_info->x;
+ machine_mode mode = GET_MODE (dest);
+
+ if (STORE_FLAG_VALUE != -1 && STORE_FLAG_VALUE != 1)
+ return false;
+
+ if (!noce_simple_bbs (if_info))
+ return false;
+
+ bool swapped = false;
+ if (a == CONSTM1_RTX (GET_MODE (dest))
+ && if_info->rev_cond)
+ {
+ std::swap (a, b);
+ swapped = true;
+ }
+
+ if (b != CONSTM1_RTX (GET_MODE (dest)))
+ return false;
+
+ /* If the other arm is not a REG/SUBREG, then punt. This is primarily to
+ let the target handle the constant case, which it can likely do better.
+ It also means we don't have to worry about non terminal expressions. */
+ if (!REG_P (a) && !SUBREG_P (a))
+ return false;
+
+ /* At this point we've got dest = cond ? a : -1. Emit the store flag and
+ adjust its value (if necessary) to -1/0. */
+ start_sequence ();
+ rtx temp = gen_reg_rtx (mode);
+ rtx target = noce_emit_store_flag (if_info, temp, !swapped, false);
+ if (!target)
+ {
+ end_sequence ();
+ return false;
+ }
+
+ if (STORE_FLAG_VALUE == 1)
+ {
+ rtx x = gen_rtx_PLUS (mode, target, CONSTM1_RTX (mode));
+ emit_move_insn (target, x);
+ }
+
+ /* Now we've got -1/0 in TARGET. We can just IOR with A. */
+ rtx x = gen_rtx_IOR (mode, target, a);
+ emit_move_insn (dest, x);
+
+ /* We've generated all the RTL, make sure it recognizes and is
+ profitable. */
+ rtx_insn *seq = end_ifcvt_sequence (if_info);
+ if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info))
+ return false;
+
+ emit_insn_before_setloc (seq, if_info->jump,
+ INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "noce_try_store_flag_logical";
+ return true;
+}
+
/* Convert "if (test) foo++" into "foo += (test != 0)", and
similarly for "foo--". */
@@ -4483,6 +4552,9 @@ noce_process_if_block (struct noce_if_info *if_info)
goto success;
if (noce_try_sign_bit_splat (if_info))
goto success;
+ if (!targetm.have_conditional_execution ()
+ && noce_try_store_flag_logical (if_info))
+ goto success;
if (HAVE_conditional_move
&& noce_try_cmove (if_info))
goto success;
diff --git a/gcc/testsuite/gcc.target/riscv/pr123313.c
b/gcc/testsuite/gcc.target/riscv/pr123313.c
new file mode 100644
index 000000000000..c50478c87bf3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr123313.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-march=rv64gcb_zicond -mabi=lp64d -mbranch-cost=4"
{ target rv64 } } */
+/* { dg-additional-options "-march=rv32gcb_zicond -mabi=ilp32 -mbranch-cost=4"
{ target rv32 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Os" "-Og" "-Oz" } } */
+
+#define TEST(TYPE, NAME) TYPE f##NAME(TYPE a, TYPE b, TYPE c) { a = -1; if (c
< 10) a = b; return a; }
+
+TEST(signed char, sc)
+TEST(signed short, ss)
+TEST(signed int, si)
+TEST(signed long, sl)
+
+/* { dg-final { scan-assembler-times "slti\t" 4 } } */
+/* { dg-final { scan-assembler-times "addi\t" 4 } } */
+/* { dg-final { scan-assembler-times "or\t" 4 } } */
+/* { dg-final { scan-assembler-not "czero" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/pr124009.c
b/gcc/testsuite/gcc.target/riscv/pr124009.c
index 6f541cacbb85..25a3bc93499b 100644
--- a/gcc/testsuite/gcc.target/riscv/pr124009.c
+++ b/gcc/testsuite/gcc.target/riscv/pr124009.c
@@ -5,7 +5,7 @@
int foo(int x, int y) { return (y < x) ? 1 : -1; }
-/* { dg-final { scan-assembler-times {slli\t} 1 } } */
+/* { dg-final { scan-assembler-times {(slli|ori)\t} 1 } } */
/* { dg-final { scan-assembler-times {addi\t} 1 } } */
/* { dg-final { scan-assembler-not {czero.eqz\t} } } */