]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
xtensa: Improve usage of SALT/SALTU instructions
authorTakayuki 'January June' Suwa <jjsuwa_sys3175@yahoo.co.jp>
Sun, 14 Dec 2025 13:08:38 +0000 (22:08 +0900)
committerMax Filippov <jcmvbkbc@gmail.com>
Tue, 16 Dec 2025 12:17:09 +0000 (04:17 -0800)
In the expansion of cstoresi4 insn patterns, LT[U] comparisons where the
second operand is an integer constant are canonicalized to LE[U] ones with
one less than the original.

     /* example */
     int test0(int a) {
       return a < 100;
     }
     unsigned int test1(unsigned int a) {
       return a <= 100u;
     }
     void test2(int a[], int b) {
       int i;
       for (i = 0; i < 16; ++i)
a[i] = (a[i] <= b);
     }

     ;; before (TARGET_SALT)
     test0:
      entry sp, 32
      movi a8, 0x63
      salt a2, a8, a2
      addi.n a2, a2, -1 ;; unwanted inverting
      neg a2, a2 ;;
      retw.n
     test1:
      entry sp, 32
      movi a8, 0x64
      saltu a2, a8, a2
      addi.n a2, a2, -1 ;; unwanted inverting
      neg a2, a2 ;;
      retw.n
     test2:
      entry sp, 32
      movi.n a9, 0x10
      loop a9, .L5_LEND
     .L5:
      l32i.n a8, a2, 0
      salt a8, a3, a8
      addi.n a8, a8, -1 ;; immediate cannot be hoisted out
      neg a8, a8
      s32i.n a8, a2, 0
      addi.n a2, a2, 4
      .L5_LEND:
      retw.n

This patch reverts such canonicalization by adding 1 to the comparison value
and then converting it back from LE[U] to LT[U], which better matches the
output machine instructions.  This patch also makes it easier to benefit
from other optimizations such as CSE, constant propagation, or loop-invariant
hoisting by XORing the result with a register that has a value of 1, rather
than subtracting 1 and then negating the sign to invert the truth of the
result.

     ;; after (TARGET_SALT)
     test0:
      entry sp, 32
      movi a8, 0x64
      salt a2, a2, a8
      retw.n
     test1:
      entry sp, 32
      movi a8, 0x65
      saltu a2, a2, a8
      retw.n
     test2:
      entry sp, 32
      movi.n a10, 1 ;; hoisted out
      movi.n a9, 0x10
      loop a9, .L5_LEND
     .L5:
      l32i.n a8, a2, 0
      salt a8, a3, a8
      xor a8, a8, a10
      s32i.n a8, a2, 0
      addi.n a2, a2, 4
      .L5_LEND:
      retw.n

gcc/ChangeLog:

* config/xtensa/xtensa.cc (xtensa_expand_scc_SALT):
New sub-function that emits the SALT/SALTU instructions.
(xtensa_expand_scc): Change the part related to the SALT/SALTU
instructions to a call to the above sub-function.

gcc/config/xtensa/xtensa.cc

index 43df10e4b63e1a02637dc0866adbab4cbb1da766..1299e45bc7d118796d4f5005f90d2e154d5b5895 100644 (file)
@@ -993,70 +993,71 @@ xtensa_expand_conditional_move (rtx *operands, int isflt)
 }
 
 
-int
-xtensa_expand_scc (rtx operands[4], machine_mode cmp_mode)
+static bool
+xtensa_expand_scc_SALT (rtx dest, enum rtx_code code, rtx op0, rtx op1)
 {
-  rtx dest = operands[0];
-  rtx cmp;
-  rtx one_tmp, zero_tmp;
-  rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
+  int flags;
 
-  if (cmp_mode == SImode && TARGET_SALT)
+  /* Revert back the canonicalization of '(lt[u]:SI (reg:SI) (const_int N)'
+     to '(le[u]:SI (reg:SI) (const_int N-1)'.
+     Note that a comparison like '(le[u]:SI (reg:SI) (const_int [U]INT_MAX))'
+     will never be passed; because the result of such a comparison is a mere
+     constant.  */
+  if (CONST_INT_P (op1))
+    switch (code)
+      {
+      case LE:
+       code = LT, op1 = GEN_INT (INTVAL (op1) + 1);
+       break;
+      case LEU:
+       code = LTU, op1 = GEN_INT (UINTVAL (op1) + 1u);
+       break;
+      default:
+       break;
+      }
+
+  /* b0: inverting, b1: swap(op0,op1), b2: unsigned */
+  switch (code)
     {
-      rtx a = operands[2], b = force_reg (SImode, operands[3]);
-      enum rtx_code code = GET_CODE (operands[1]);
-      bool invert_res = false;
+    case GE:   flags = 1; break;
+    case GT:   flags = 2; break;
+    case LE:   flags = 3; break;
+    case LT:   flags = 0; break;
+    case GEU:  flags = 5; break;
+    case GTU:  flags = 6; break;
+    case LEU:  flags = 7; break;
+    case LTU:  flags = 4; break;
+    default:   return false;
+    }
 
-      switch (code)
-       {
-       case GE:
-       case GEU:
-         invert_res = true;
-         break;
-       case GT:
-       case GTU:
-         std::swap (a, b);
-         break;
-       case LE:
-       case LEU:
-         invert_res = true;
-         std::swap (a, b);
-         break;
-       default:
-         break;
-       }
+  op1 = force_reg (SImode, op1);
+  if (flags & 2)
+    std::swap (op0, op1);
+  emit_insn ((flags & 4) ? gen_saltu (dest, op0, op1)
+                        : gen_salt (dest, op0, op1));
+  if (flags & 1)
+    /* XORing with a temporary register with a value of 1 is advantageous
+       over negation followed by addition of 1 because the temporary register
+       assignment can be subject to CSE, constant propagation, or loop-
+       invariant hoisting.  */
+    emit_insn (gen_xorsi3 (dest, dest, force_reg (SImode, const1_rtx)));
 
-      switch (code)
-       {
-       case GE:
-       case GT:
-       case LE:
-       case LT:
-         emit_insn (gen_salt (dest, a, b));
-         if (!invert_res)
-           return 1;
-         break;
-       case GEU:
-       case GTU:
-       case LEU:
-       case LTU:
-         emit_insn (gen_saltu (dest, a, b));
-         if (!invert_res)
-           return 1;
-         break;
-       default:
-         break;
-       }
+  return true;
+}
 
-      if (invert_res)
-       {
-         emit_insn (gen_negsi2 (dest, dest));
-         emit_insn (gen_addsi3 (dest, dest, const1_rtx));
-         return 1;
-       }
-    }
+int
+xtensa_expand_scc (rtx operands[4], machine_mode cmp_mode)
+{
+  rtx dest = operands[0];
+  rtx cmp, one_tmp, zero_tmp;
+  rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
+  enum rtx_code code = GET_CODE (operands[1]);
+
+  if (TARGET_SALT && cmp_mode == SImode
+      && xtensa_expand_scc_SALT (dest, code, operands[2], operands[3]))
+    return 1;
 
-  if (! (cmp = gen_conditional_move (GET_CODE (operands[1]), cmp_mode,
+  if (! (cmp = gen_conditional_move (code, cmp_mode,
                                     operands[2], operands[3])))
     return 0;