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