]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[RISC-V][PR tree-optimization/52345] Optimize testing multiple booleans
authorShreya Munnangi <smunnangi1@ventanamicro.com>
Mon, 3 Nov 2025 02:21:53 +0000 (19:21 -0700)
committerJeff Law <jlaw@ventanamicro.com>
Mon, 3 Nov 2025 02:22:20 +0000 (19:22 -0700)
This is Shreya's work, my contribution was primarily covering the testing.
Bootstrapped and regression tested on x86 and riscv64.  It's also been tested
on all the embedded targets in my tester without regression.

While this improves code generation to optimal on riscv-64, I'm electing to
keep the BZ open because we probably should have the same kind of
simplification in match.pd.  Shreya is just starting to write some match.pd
patterns and I expect we'll return to write a match.pd pattern for this issue
relatively soon.

Obviously waiting for pre-commit CI to chime in before moving forward.

Jeff

--

In PR52345, we have this testcase:

int f(int a, int b)
{
  int c = a != 0;
  int d = (c!=0|b!=0);
  return d;
}

Basically, "d" will either be 0 or 1. Depending on "a", "c" will also
either be 0 or 1. So if "a" is 0 and "b" is 0, then "d" will also be 0. Otherwise, it will be 1.

When the testcase is compiled, we get this generated assembly code:

        snez    a0,a0
        or      a0,a1,a0
        snez    a0,a0

RISC-V has a missed optimization here, as this can simply be done by first
computing a|b and checking if the result is equal to 0. If "a" is 0 and "b" is
0, we will get 0. Otherwise, we will get 1. Doing this removes the unnecessary
first snez instruction.

When we looked at the combine pass, it was trying:

Failed to match this instruction:
(set (reg/i:DI 10 a0)
    (ne:DI (ior:DI (ne:DI (reg:DI 151 [ a ])
                (const_int 0 [0]))
            (reg:DI 152 [ b ]))
        (const_int 0 [0])))

In simplify_relational_operation_1 of simplify-rtx.cc, we added a condition.
For cases where the outer code is a "not equal to" (NE) and the operands match
the pattern above, we simply emit an NE of an IOR of the two registers, giving
us:

        or      a0,a0,a1
        snez    a0,a0

We then generalized this to include the case where the outer code is an "equal
to" (EQ). With the logic working in the same way, we simply adjust the
recognition code to check that the outer code is either an NE or EQ and
generalize the NE we emit to match the outer code.

--

PR target/52345
gcc/

* simplify-rtx.cc (simplify_relational_operation_1): Optimize boolean
IOR equality tests.

gcc/testsuite/

* gcc.target/riscv/pr52345.c: Add new test cases.

gcc/simplify-rtx.cc
gcc/testsuite/gcc.target/riscv/pr52345.c [new file with mode: 0644]

index b9591eb6bb71cd37b39b07f5f220176dc4c12888..989cf9c2c82fc39694011d2a8a17b209f807a727 100644 (file)
@@ -6451,6 +6451,21 @@ simplify_context::simplify_relational_operation_1 (rtx_code code,
       /* Canonicalize (LEU x 0) as (EQ x 0).  */
       if (code == LEU)
         return simplify_gen_relational (EQ, mode, cmp_mode, op0, op1);
+
+      if ((code == NE || code == EQ)
+         /* Verify op0 is IOR */
+         && GET_CODE (op0) == IOR
+         /* only enters if op1 is 0 */
+         /* Verify IOR operand is NE */
+         && GET_CODE (XEXP (op0, 0)) == NE
+         /* Verfiy second NE operand is 0 */
+         && XEXP (XEXP (op0, 0), 1) == CONST0_RTX (mode))
+       {
+         rtx t = gen_rtx_IOR (mode, XEXP (XEXP (op0, 0), 0), XEXP (op0, 1));
+         t = gen_rtx_fmt_ee (code, mode, t, CONST0_RTX (mode));
+         return t;
+       }
+
     }
   else if (op1 == const1_rtx)
     {
diff --git a/gcc/testsuite/gcc.target/riscv/pr52345.c b/gcc/testsuite/gcc.target/riscv/pr52345.c
new file mode 100644 (file)
index 0000000..90feb91
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gcbv_zicond -mabi=lp64d" { target { rv64 } } } */
+/* { dg-options "-O2 -march=rv32gcbv_zicond -mabi=ilp32" { target { rv32 } } } */
+
+int f(int a, int b)
+{
+  int c = a != 0;
+  int d = (c!=0|b!=0);
+  return d;
+}
+
+int h (int a, int b)
+{
+  int c = (a!=0|b);
+  int d = c==0;
+  return d;
+}
+
+/* { dg-final { scan-assembler-times {\tor} 2 } } */
+/* { dg-final { scan-assembler-times {\tsnez} 1 } } */
+/* { dg-final { scan-assembler-times {\tseqz} 1 } } */