Implement TGEI/TGEIU/TLTI/TLTIU/TEQI/TNEI for MIPS32, while ensuring
they are not generated for R6.  Optimize comparisons like `x <= 0`
to use $0 on architectures without immediate trap instructions.
Preserve $0 as operand when possible to avoid redundant moves.

        PR target/115678

gcc/ChangeLog:

        * config/mips/mips.cc (mips_expand_conditional_trap): Handle
        LT/LTU against 1 by converting to GE/GEU with $0 only when
        immediate traps are unavailable.  Process GT/LE/GTU/LEU with
        immediates, including cases where the adjusted immediate would
        overflow.  Keep $0 as operand when allowed.
        * config/mips/mips.md (*conditional_trap<mode>): Use
        mips_trap_comparison_operator to reject invalid immediate
        adjustments.  Emit appropriate instruction based on operand
        type and comparison code.
        * config/mips/predicates.md (mips_trap_comparison_operator):
        New predicate.

gcc/testsuite/ChangeLog:

        * gcc.target/mips/pr115678-1.c: New test for register traps
        on MIPS32.
        * gcc.target/mips/pr115678-2.c: New test for immediate traps
        on MIPS32, including boundary cases.
        * gcc.target/mips/pr115678-3.c: New test for register traps
        on R6.
        * gcc.target/mips/pr115678-4.c: New test for immediate
        comparisons on R6 (should use register traps with li and
        avoid redundant moves involving $0).
---
 gcc/config/mips/mips.cc                    | 52 +++++++++++++++++++--
 gcc/config/mips/mips.md                    | 53 +++++++++++++++++++++-
 gcc/config/mips/predicates.md              | 15 ++++++
 gcc/testsuite/gcc.target/mips/pr115678-1.c | 27 +++++++++++
 gcc/testsuite/gcc.target/mips/pr115678-2.c | 41 +++++++++++++++++
 gcc/testsuite/gcc.target/mips/pr115678-3.c | 27 +++++++++++
 gcc/testsuite/gcc.target/mips/pr115678-4.c | 37 +++++++++++++++
 7 files changed, 247 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/mips/pr115678-1.c
 create mode 100644 gcc/testsuite/gcc.target/mips/pr115678-2.c
 create mode 100644 gcc/testsuite/gcc.target/mips/pr115678-3.c
 create mode 100644 gcc/testsuite/gcc.target/mips/pr115678-4.c

diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
index af9ae9f550e..486e08949e6 100644
--- a/gcc/config/mips/mips.cc
+++ b/gcc/config/mips/mips.cc
@@ -6074,11 +6074,56 @@ mips_expand_conditional_trap (rtx comparison)
 {
   rtx op0, op1;
   machine_mode mode;
-  enum rtx_code code;
+  enum rtx_code code = GET_CODE (comparison);
+
+  /* On targets without immediate trap instructions, convert LT/LTU
+   comparisons against 1 (which correspond to x <= 0 for signed or
+   unsigned x) to GE/GEU with $0 as the first operand.  This avoids
+   loading the constant 1 into a register.  */
+  if (!ISA_HAS_COND_TRAPI
+      && (code == LT || code == LTU)
+      && CONST_INT_P (XEXP (comparison, 1))
+      && INTVAL (XEXP (comparison, 1)) == 1)
+    {
+      op0 = const0_rtx;
+      op1 = XEXP (comparison, 0);
+      code = (code == LT) ? GE : GEU;
+      mode = GET_MODE (op1);
+      op1 = force_reg (mode, op1);
+      emit_insn (gen_rtx_TRAP_IF (VOIDmode,
+                                 gen_rtx_fmt_ee (code, mode, op0, op1),
+                                 const0_rtx));
+      return;
+    }
+
+  /* Handle GT/LE/GTU/LEU with an immediate operand.  Convert to the
+     corresponding opposite trap with an adjusted immediate if the
+     result is still a valid small operand.  If the adjusted immediate
+     would be out of range, leave the comparison unchanged and let the
+     generic code below force the immediate into a register.  */
+  if (ISA_HAS_COND_TRAPI
+      && CONST_INT_P (XEXP (comparison, 1))
+      && (code == GT || code == LE || code == GTU || code == LEU))
+    {
+      HOST_WIDE_INT imm = INTVAL (XEXP (comparison, 1)) + 1;
+      if (SMALL_OPERAND (imm))
+       {
+         op0 = XEXP (comparison, 0);
+         op1 = GEN_INT (imm);
+         code = (code == GT  ? GE  :
+                 code == LE  ? LT  :
+                 code == GTU ? GEU : LTU);
+         mode = GET_MODE (XEXP (comparison, 0));
+         op0 = force_reg (mode, op0);
+         emit_insn (gen_rtx_TRAP_IF (VOIDmode,
+                                     gen_rtx_fmt_ee (code, mode, op0, op1),
+                                     const0_rtx));
+         return;
+       }
+    }
 
   /* MIPS conditional trap instructions don't have GT or LE flavors,
      so we must swap the operands and convert to LT and GE respectively.  */
-  code = GET_CODE (comparison);
   switch (code)
     {
     case GT:
@@ -6097,7 +6142,8 @@ mips_expand_conditional_trap (rtx comparison)
     }
 
   mode = GET_MODE (XEXP (comparison, 0));
-  op0 = force_reg (mode, op0);
+  if (!reg_or_0_operand (op0, mode))
+    op0 = force_reg (mode, op0);
   if (!(ISA_HAS_COND_TRAPI
        ? arith_operand (op1, mode)
        : reg_or_0_operand (op1, mode)))
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 18244e4abcc..46d9ad17e58 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -1260,12 +1260,61 @@
   [(set_attr "type" "trap")])
 
 (define_insn "*conditional_trap<mode>"
-  [(trap_if (match_operator:GPR 0 "trap_comparison_operator"
+  [(trap_if (match_operator:GPR 0 "mips_trap_comparison_operator"
                                [(match_operand:GPR 1 "reg_or_0_operand" "dJ")
                                 (match_operand:GPR 2 "arith_operand" "dI")])
            (const_int 0))]
   "ISA_HAS_COND_TRAPI"
-  "t%C0\t%z1,%2"
+{
+  enum rtx_code code = GET_CODE (operands[0]);
+  bool imm_p = CONST_INT_P (operands[2]);
+
+  switch (code)
+    {
+    case EQ:
+      return imm_p ? "teqi\t%z1,%2" : "teq\t%z1,%2";
+    case NE:
+      return imm_p ? "tnei\t%z1,%2" : "tne\t%z1,%2";
+    case GE:
+      return imm_p ? "tgei\t%z1,%2" : "tge\t%z1,%2";
+    case GEU:
+      return imm_p ? "tgeiu\t%z1,%2" : "tgeu\t%z1,%2";
+    case LT:
+      return imm_p ? "tlti\t%z1,%2" : "tlt\t%z1,%2";
+    case LTU:
+      return imm_p ? "tltiu\t%z1,%2" : "tltu\t%z1,%2";
+    case GT:
+      if (imm_p)
+       {
+         operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
+         return "tgei\t%z1,%2";
+       }
+      return "tlt\t%z2,%1";
+    case LE:
+      if (imm_p)
+       {
+         operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
+         return "tlti\t%z1,%2";
+       }
+      return "tge\t%z2,%1";
+    case GTU:
+      if (imm_p)
+       {
+         operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
+         return "tgeiu\t%z1,%2";
+       }
+      return "tltu\t%z2,%1";
+    case LEU:
+      if (imm_p)
+       {
+         operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
+         return "tltiu\t%z1,%2";
+       }
+      return "tgeu\t%z2,%1";
+    default:
+      gcc_unreachable ();
+    }
+}
   [(set_attr "type" "trap")])
 
 ;;
diff --git a/gcc/config/mips/predicates.md b/gcc/config/mips/predicates.md
index b0e2219e312..98e618f6b96 100644
--- a/gcc/config/mips/predicates.md
+++ b/gcc/config/mips/predicates.md
@@ -556,6 +556,21 @@
 (define_predicate "trap_comparison_operator"
   (match_code "eq,ne,lt,ltu,ge,geu"))
 
+;; Like trap_comparison_operator, but also rejects GT/LE/GTU/LEU
+;; when the second operand is an immediate whose value + 1 would
+;; be out of range for an immediate trap instruction.
+(define_predicate "mips_trap_comparison_operator"
+  (match_operand 0 "comparison_operator")
+{
+  enum rtx_code code = GET_CODE (op);
+  if (code == GT || code == LE || code == GTU || code == LEU)
+    {
+      rtx imm = XEXP (op, 1);
+      return !CONST_INT_P (imm) || SMALL_OPERAND (INTVAL (imm) + 1);
+    }
+  return true;
+})
+
 (define_predicate "order_operator"
   (match_code "lt,ltu,le,leu,ge,geu,gt,gtu")
 {
diff --git a/gcc/testsuite/gcc.target/mips/pr115678-1.c 
b/gcc/testsuite/gcc.target/mips/pr115678-1.c
new file mode 100644
index 00000000000..0a0acaa3154
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/pr115678-1.c
@@ -0,0 +1,27 @@
+/* { dg-options "-mips32" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */
+/* { dg-final { scan-assembler-not "\tteqi\t" } } */
+/* { dg-final { scan-assembler-not "\ttnei\t" } } */
+/* { dg-final { scan-assembler-not "\ttgei\t" } } */
+/* { dg-final { scan-assembler-not "\ttgeiu\t" } } */
+/* { dg-final { scan-assembler-not "\ttlti\t" } } */
+/* { dg-final { scan-assembler-not "\ttltiu\t" } } */
+/* { dg-final { scan-assembler-times "\tteq\t" 1 } } */
+/* { dg-final { scan-assembler-times "\ttne\t" 1 } } */
+/* { dg-final { scan-assembler-times "\ttge\t" 2 } } */
+/* { dg-final { scan-assembler-times "\ttlt\t" 2 } } */
+/* { dg-final { scan-assembler-times "\ttgeu\t" 2 } } */
+/* { dg-final { scan-assembler-times "\ttltu\t" 2 } } */
+
+NOMIPS16 void test_eq (int a, int b)    { if (a == b) __builtin_trap(); }  /* 
teq $4,$5 */
+NOMIPS16 void test_ne (int a, int b)    { if (a != b) __builtin_trap(); }  /* 
tne $4,$5 */
+
+NOMIPS16 void test_ge (int a, int b)    { if (a >= b) __builtin_trap(); }  /* 
tge $4,$5 */
+NOMIPS16 void test_lt (int a, int b)    { if (a < b) __builtin_trap(); }   /* 
tlt $4,$5 */
+NOMIPS16 void test_gt (int a, int b)    { if (a > b) __builtin_trap(); }   /* 
tlt $5,$4 */
+NOMIPS16 void test_le (int a, int b)    { if (a <= b) __builtin_trap(); }  /* 
tge $5,$4 */
+
+NOMIPS16 void test_geu (unsigned a, unsigned b) { if (a >= b) 
__builtin_trap(); } /* tgeu $4,$5 */
+NOMIPS16 void test_ltu (unsigned a, unsigned b) { if (a < b) __builtin_trap(); 
}  /* tltu $4,$5*/
+NOMIPS16 void test_gtu (unsigned a, unsigned b) { if (a > b) __builtin_trap(); 
}  /* tltu $5,$4 */
+NOMIPS16 void test_leu (unsigned a, unsigned b) { if (a <= b) 
__builtin_trap(); } /* tgeu $5,$4 */
diff --git a/gcc/testsuite/gcc.target/mips/pr115678-2.c 
b/gcc/testsuite/gcc.target/mips/pr115678-2.c
new file mode 100644
index 00000000000..ddf5f548f46
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/pr115678-2.c
@@ -0,0 +1,41 @@
+/* { dg-options "-mips32" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */
+/* { dg-final { scan-assembler-not "\tteq\t" } } */
+/* { dg-final { scan-assembler-not "\ttne\t" } } */
+/* { dg-final { scan-assembler-times "\tteqi\t" 3 } } */
+/* { dg-final { scan-assembler-times "\ttnei\t" 3 } } */
+/* { dg-final { scan-assembler-times "\ttgei\t" 4 } } */
+/* { dg-final { scan-assembler-times "\ttgeiu\t" 2 } } */
+/* { dg-final { scan-assembler-times "\ttlti\t" 4 } } */
+/* { dg-final { scan-assembler-times "\ttltiu\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tli\t" 4 } } */
+/* { dg-final { scan-assembler-times "\ttge\t" 1 } } */
+/* { dg-final { scan-assembler-times "\ttgeu\t" 1 } } */
+/* { dg-final { scan-assembler-times "\ttlt\t" 1 } } */
+/* { dg-final { scan-assembler-times "\ttltu\t" 1 } } */
+
+NOMIPS16 void test_eq (int a)    { if (a == 5) __builtin_trap(); }   /* teqi 
$4,5 */
+NOMIPS16 void test_eq2 (int a)   { if (a == 0) __builtin_trap(); }   /* teqi 
$4,0 */
+NOMIPS16 void test_ne (int a)    { if (a != 5) __builtin_trap(); }   /* tnei 
$4,5 */
+NOMIPS16 void test_ne2 (int a)   { if (a != 0) __builtin_trap(); }   /* tnei 
$4,0 */
+
+NOMIPS16 void test_ge (int a)    { if (a >= 5) __builtin_trap(); }   /* tgei 
$4,5 */
+NOMIPS16 void test_ge2 (int a)   { if (a >= 0) __builtin_trap(); }   /* tgei 
$4,0 */
+NOMIPS16 void test_lt (int a)    { if (a < 5) __builtin_trap(); }    /* tlti 
$4,5 */
+NOMIPS16 void test_lt2 (int a)   { if (a < 0) __builtin_trap(); }    /* tlti 
$4,0 */
+NOMIPS16 void test_gt (int a)    { if (a > 5) __builtin_trap(); }    /* tgei 
$4,6 */
+NOMIPS16 void test_gt2 (int a)   { if (a > 0) __builtin_trap(); }    /* tgei 
$4,1 */
+NOMIPS16 void test_le (int a)    { if (a <= 5) __builtin_trap(); }   /* tlti 
$4,6 */
+NOMIPS16 void test_le2 (int a)   { if (a <= 0) __builtin_trap(); }   /* tlti 
$4,1 */
+
+NOMIPS16 void test_geu (unsigned a)    { if (a >= 5) __builtin_trap(); }  /* 
tgeiu $4,5 */
+NOMIPS16 void test_ltu (unsigned a)    { if (a < 5) __builtin_trap(); }   /* 
tltiu $4,5 */
+NOMIPS16 void test_gtu (unsigned a)    { if (a > 5) __builtin_trap(); }   /* 
tgeiu $4,6 */
+NOMIPS16 void test_gtu2 (unsigned a)   { if (a > 0) __builtin_trap(); }   /* 
tnei $4,0 */
+NOMIPS16 void test_leu (unsigned a)    { if (a <= 5) __builtin_trap(); }  /* 
tltiu $4,6 */
+NOMIPS16 void test_leu2 (unsigned a)   { if (a <= 0) __builtin_trap(); }  /* 
teqi $4,0 */
+
+NOMIPS16 void test_gt_border (int a)    { if (a > 32767) __builtin_trap(); }   
  /* tge $4,$2 */
+NOMIPS16 void test_le_border (int a)    { if (a <= 32767) __builtin_trap(); }  
  /* tlt $4,$2 */
+NOMIPS16 void test_gtu_border (unsigned a) { if (a > 32767) __builtin_trap(); 
}  /* tgeu $4,$2 */
+NOMIPS16 void test_leu_border (unsigned a) { if (a <= 32767) __builtin_trap(); 
} /* tltu $4,$2 */
diff --git a/gcc/testsuite/gcc.target/mips/pr115678-3.c 
b/gcc/testsuite/gcc.target/mips/pr115678-3.c
new file mode 100644
index 00000000000..5d97722ec3a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/pr115678-3.c
@@ -0,0 +1,27 @@
+/* { dg-options "isa_rev>=6" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */
+/* { dg-final { scan-assembler-not "\tteqi\t" } } */
+/* { dg-final { scan-assembler-not "\ttnei\t" } } */
+/* { dg-final { scan-assembler-not "\ttgei\t" } } */
+/* { dg-final { scan-assembler-not "\ttgeiu\t" } } */
+/* { dg-final { scan-assembler-not "\ttlti\t" } } */
+/* { dg-final { scan-assembler-not "\ttltiu\t" } } */
+/* { dg-final { scan-assembler-times "\tteq\t" 1 } } */
+/* { dg-final { scan-assembler-times "\ttne\t" 1 } } */
+/* { dg-final { scan-assembler-times "\ttge\t" 2 } } */
+/* { dg-final { scan-assembler-times "\ttlt\t" 2 } } */
+/* { dg-final { scan-assembler-times "\ttgeu\t" 2 } } */
+/* { dg-final { scan-assembler-times "\ttltu\t" 2 } } */
+
+NOMIPS16 void test_eq (int a, int b)    { if (a == b) __builtin_trap(); }  /* 
teq $4,$5 */
+NOMIPS16 void test_ne (int a, int b)    { if (a != b) __builtin_trap(); }  /* 
tne $4,$5 */
+
+NOMIPS16 void test_ge (int a, int b)    { if (a >= b) __builtin_trap(); }  /* 
tge $4,$5 */
+NOMIPS16 void test_lt (int a, int b)    { if (a < b) __builtin_trap(); }   /* 
tlt $4,$5 */
+NOMIPS16 void test_gt (int a, int b)    { if (a > b) __builtin_trap(); }   /* 
tlt $5,$4 */
+NOMIPS16 void test_le (int a, int b)    { if (a <= b) __builtin_trap(); }  /* 
tge $5,$4 */
+
+NOMIPS16 void test_geu (unsigned a, unsigned b) { if (a >= b) 
__builtin_trap(); } /* tgeu $4,$5 */
+NOMIPS16 void test_ltu (unsigned a, unsigned b) { if (a < b) __builtin_trap(); 
}  /* tltu $4,$5*/
+NOMIPS16 void test_gtu (unsigned a, unsigned b) { if (a > b) __builtin_trap(); 
}  /* tltu $5,$4 */
+NOMIPS16 void test_leu (unsigned a, unsigned b) { if (a <= b) 
__builtin_trap(); } /* tgeu $5,$4 */
diff --git a/gcc/testsuite/gcc.target/mips/pr115678-4.c 
b/gcc/testsuite/gcc.target/mips/pr115678-4.c
new file mode 100644
index 00000000000..1329e2aa478
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/pr115678-4.c
@@ -0,0 +1,37 @@
+/* { dg-options "isa_rev>=6" } */
+/* { dg-skip-if "code quality test" { *-*-* } { "-O0" } { "" } } */
+/* { dg-final { scan-assembler-not "\tteqi\t" } } */
+/* { dg-final { scan-assembler-not "\ttnei\t" } } */
+/* { dg-final { scan-assembler-not "\ttgei\t" } } */
+/* { dg-final { scan-assembler-not "\ttgeiu\t" } } */
+/* { dg-final { scan-assembler-not "\ttlti\t" } } */
+/* { dg-final { scan-assembler-not "\ttltiu\t" } } */
+/* { dg-final { scan-assembler-not "\tmove\t" } } */
+/* { dg-final { scan-assembler-times "\tli\t" 10 } } */
+/* { dg-final { scan-assembler-times "\tteq\t" 3 } } */
+/* { dg-final { scan-assembler-times "\ttne\t" 3 } } */
+/* { dg-final { scan-assembler-times "\ttge\t" 4 } } */
+/* { dg-final { scan-assembler-times "\ttlt\t" 4 } } */
+/* { dg-final { scan-assembler-times "\ttgeu\t" 2 } } */
+/* { dg-final { scan-assembler-times "\ttltu\t" 2 } } */
+
+NOMIPS16 void test_eq (int a)    { if (a == 5) __builtin_trap(); }   /* li 
$2,5   teq $4,$2 */
+NOMIPS16 void test_eq2 (int a)   { if (a == 0) __builtin_trap(); }   /* teq 
$4,$0 */
+NOMIPS16 void test_ne (int a)    { if (a != 5) __builtin_trap(); }   /* li 
$2,5   tne $4,$2 */
+NOMIPS16 void test_ne2 (int a)   { if (a != 0) __builtin_trap(); }   /* tne 
$4,$0 */
+
+NOMIPS16 void test_ge (int a)    { if (a >= 5) __builtin_trap(); }   /* li 
$2,4   tlt $2,$4 */
+NOMIPS16 void test_ge2 (int a)   { if (a >= 0) __builtin_trap(); }   /* tge 
$4,$0 */
+NOMIPS16 void test_lt (int a)    { if (a < 5) __builtin_trap(); }    /* li 
$2,4   tge $2,$4 */
+NOMIPS16 void test_lt2 (int a)   { if (a < 0) __builtin_trap(); }    /* tlt 
$4,$0 */
+NOMIPS16 void test_gt (int a)    { if (a > 5) __builtin_trap(); }    /* li 
$2,5   tlt $2,$4 */
+NOMIPS16 void test_gt2 (int a)   { if (a > 0) __builtin_trap(); }    /* tlt 
$0,$4 */
+NOMIPS16 void test_le (int a)    { if (a <= 5) __builtin_trap(); }   /* li 
$2,5   tge $2,$4 */
+NOMIPS16 void test_le2 (int a)   { if (a <= 0) __builtin_trap(); }   /* tge 
$0,$4 */
+
+NOMIPS16 void test_geu (unsigned a)    { if (a >= 5) __builtin_trap(); }  /* 
li $2,4   tltu $2,$4 */
+NOMIPS16 void test_ltu (unsigned a)    { if (a < 5) __builtin_trap(); }   /* 
li $2,4   tgeu $2,$4 */
+NOMIPS16 void test_gtu (unsigned a)    { if (a > 5) __builtin_trap(); }   /* 
li $2,5   tltu $2,$4 */
+NOMIPS16 void test_gtu2 (unsigned a)   { if (a > 0) __builtin_trap(); }   /* 
tne $4,$0 */
+NOMIPS16 void test_leu (unsigned a)    { if (a <= 5) __builtin_trap(); }  /* 
li $2,5   tgeu $2,$4 */
+NOMIPS16 void test_leu2 (unsigned a)   { if (a <= 0) __builtin_trap(); }  /* 
teq $4,$0 */
-- 
2.53.0

Reply via email to