rx: Cleanup conditional branches.

Use match_operator, not code_iterators.  Use a new helper function,
rx_split_cbranch.  Get the modes right on the comparisons.  Distinguish
fp comparisons with CC_Fmode.

From-SVN: r168919
This commit is contained in:
Richard Henderson 2011-01-17 09:53:55 -08:00 committed by Richard Henderson
parent af530bb412
commit e963cb1a8a
5 changed files with 485 additions and 338 deletions

View File

@ -1,5 +1,34 @@
2011-01-17 Richard Henderson <rth@redhat.com>
* config/rx/predicates.md (label_ref_operand): New.
(rx_z_comparison_operator): New.
(rx_zs_comparison_operator): New.
(rx_fp_comparison_operator): New.
* config/rx/rx.c (rx_print_operand) [B]: Examine comparison modes.
Validate that the flags are set properly for the comparison.
(rx_gen_cond_branch_template): Remove.
(rx_cc_modes_compatible): Remove.
(mode_from_flags): New.
(flags_from_code): Rename from flags_needed_for_conditional.
(rx_cc_modes_compatible): Re-write in terms of flags_from_mode.
(rx_select_cc_mode): Likewise.
(rx_split_fp_compare): New.
(rx_split_cbranch): New.
* config/rx/rx.md (most_cond, zs_cond): Remove iterators.
(*cbranchsi4): Use match_operator and rx_split_cbranch.
(*cbranchsf4): Similarly.
(*cbranchsi4_tst): Rename from *tstbranchsi4_<code>. Use
match_operator and rx_split_cbranch.
(*cbranchsi4_tst_ext): Combine *tstbranchsi4m_eq and
tstbranchsi4m_ne. Use match_operator and rx_split_cbranch.
(*cmpsi): Rename from cmpsi.
(*tstsi): Rename from tstsi.
(*cmpsf): Rename from cmpsf; use CC_Fmode.
(*conditional_branch): Rename from conditional_branch.
(*reveresed_conditional_branch): Remove.
(b<code>): Remove expander.
* config/rx/rx-protos.h: Update.
* config/rx/rx.c (rx_compare_redundant): Remove.
* config/rx/rx.md (cmpsi): Don't use it.
* config/rx/rx-protos.h: Update.

View File

@ -293,3 +293,20 @@
element = XVECEXP (op, 0, count - 1);
return GET_CODE (element) == RETURN;
})
(define_predicate "label_ref_operand"
(match_code "label_ref")
)
(define_predicate "rx_z_comparison_operator"
(match_code "eq,ne")
)
(define_predicate "rx_zs_comparison_operator"
(match_code "eq,ne,lt,ge")
)
;; GT, LE, UNLE, UNGT omitted due to operand swap required.
(define_predicate "rx_fp_comparison_operator"
(match_code "eq,ne,lt,ge,ordered,unordered,uneq,unlt,unge,ltgt")
)

View File

@ -34,12 +34,13 @@ extern void rx_emit_stack_popm (rtx *, bool);
extern void rx_emit_stack_pushm (rtx *);
extern void rx_expand_epilogue (bool);
extern bool rx_expand_insv (rtx *);
extern const char * rx_gen_cond_branch_template (rtx, bool);
extern char * rx_gen_move_template (rtx *, bool);
extern bool rx_is_legitimate_constant (rtx);
extern bool rx_is_mode_dependent_addr (rtx);
extern bool rx_is_restricted_memory_address (rtx, Mmode);
extern void rx_notice_update_cc (rtx body, rtx insn);
extern void rx_split_cbranch (Mmode, Rcode, rtx, rtx, rtx);
extern bool rx_split_fp_compare (Rcode, Rcode *, Rcode *);
extern Mmode rx_select_cc_mode (Rcode, rtx, rtx);
#endif

View File

@ -53,6 +53,15 @@
static void rx_print_operand (FILE *, rtx, int);
#define CC_FLAG_S (1 << 0)
#define CC_FLAG_Z (1 << 1)
#define CC_FLAG_O (1 << 2)
#define CC_FLAG_C (1 << 3)
#define CC_FLAG_FP (1 << 4) /* fake, to differentiate CC_Fmode */
static unsigned int flags_from_mode (enum machine_mode mode);
static unsigned int flags_from_code (enum rtx_code code);
enum rx_cpu_types rx_cpu_type = RX600;
/* Return true if OP is a reference to an object in a small data area. */
@ -395,21 +404,84 @@ rx_print_operand (FILE * file, rtx op, int letter)
break;
case 'B':
switch (GET_CODE (op))
{
case LT: fprintf (file, "lt"); break;
case GE: fprintf (file, "ge"); break;
case GT: fprintf (file, "gt"); break;
case LE: fprintf (file, "le"); break;
case GEU: fprintf (file, "geu"); break;
case LTU: fprintf (file, "ltu"); break;
case GTU: fprintf (file, "gtu"); break;
case LEU: fprintf (file, "leu"); break;
case EQ: fprintf (file, "eq"); break;
case NE: fprintf (file, "ne"); break;
default: debug_rtx (op); gcc_unreachable ();
}
break;
{
enum rtx_code code = GET_CODE (op);
enum machine_mode mode = GET_MODE (XEXP (op, 0));
const char *ret;
if (mode == CC_Fmode)
{
/* C flag is undefined, and O flag carries unordered. None of the
branch combinations that include O use it helpfully. */
switch (code)
{
case ORDERED:
ret = "no";
break;
case UNORDERED:
ret = "o";
break;
case LT:
ret = "n";
break;
case GE:
ret = "pz";
break;
case EQ:
ret = "eq";
break;
case NE:
ret = "ne";
break;
default:
gcc_unreachable ();
}
}
else
{
switch (code)
{
case LT:
ret = "n";
break;
case GE:
ret = "pz";
break;
case GT:
ret = "gt";
break;
case LE:
ret = "le";
break;
case GEU:
ret = "geu";
break;
case LTU:
ret = "ltu";
break;
case GTU:
ret = "gtu";
break;
case LEU:
ret = "leu";
break;
case EQ:
ret = "eq";
break;
case NE:
ret = "ne";
break;
default:
gcc_unreachable ();
}
/* ??? Removable when all of cbranch, cstore, cmove are updated. */
if (GET_MODE_CLASS (mode) == MODE_CC)
gcc_checking_assert ((flags_from_code (code)
& ~flags_from_mode (mode)) == 0);
}
fputs (ret, file);
break;
}
case 'C':
gcc_assert (CONST_INT_P (op));
@ -700,51 +772,6 @@ rx_gen_move_template (rtx * operands, bool is_movu)
extension, src_template, dst_template);
return out_template;
}
/* Returns an assembler template for a conditional branch instruction. */
const char *
rx_gen_cond_branch_template (rtx condition, bool reversed)
{
enum rtx_code code = GET_CODE (condition);
if (reversed)
{
if (rx_float_compare_mode)
code = reverse_condition_maybe_unordered (code);
else
code = reverse_condition (code);
}
/* We do not worry about encoding the branch length here as GAS knows
how to choose the smallest version, and how to expand a branch that
is to a destination that is out of range. */
switch (code)
{
case UNEQ: return "bo\t1f\n\tbeq\t%0\n1:";
case LTGT: return "bo\t1f\n\tbne\t%0\n1:";
case UNLT: return "bo\t1f\n\tbn\t%0\n1:";
case UNGE: return "bo\t1f\n\tbpz\t%0\n1:";
case UNLE: return "bo\t1f\n\tbgt\t1f\n\tbra\t%0\n1:";
case UNGT: return "bo\t1f\n\tble\t1f\n\tbra\t%0\n1:";
case UNORDERED: return "bo\t%0";
case ORDERED: return "bno\t%0";
case LT: return rx_float_compare_mode ? "bn\t%0" : "blt\t%0";
case GE: return rx_float_compare_mode ? "bpz\t%0" : "bge\t%0";
case GT: return "bgt\t%0";
case LE: return "ble\t%0";
case GEU: return "bgeu\t%0";
case LTU: return "bltu\t%0";
case GTU: return "bgtu\t%0";
case LEU: return "bleu\t%0";
case EQ: return "beq\t%0";
case NE: return "bne\t%0";
default:
gcc_unreachable ();
}
}
/* Return VALUE rounded up to the next ALIGNMENT boundary. */
@ -2543,69 +2570,100 @@ rx_trampoline_init (rtx tramp, tree fndecl, rtx chain)
}
}
static enum machine_mode
rx_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
static int
rx_memory_move_cost (enum machine_mode mode, reg_class_t regclass, bool in)
{
if (m1 == CCmode)
return m2;
if (m2 == CCmode)
return m1;
if (m1 == m2)
return m1;
if (m1 == CC_ZSmode)
return m1;
if (m2 == CC_ZSmode)
return m2;
return VOIDmode;
return 2 + memory_move_secondary_cost (mode, regclass, in);
}
#define CC_FLAG_S (1 << 0)
#define CC_FLAG_Z (1 << 1)
#define CC_FLAG_O (1 << 2)
#define CC_FLAG_C (1 << 3)
static unsigned int
flags_needed_for_conditional (rtx conditional)
{
switch (GET_CODE (conditional))
{
case LE:
case GT: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O;
case LEU:
case GTU: return CC_FLAG_Z | CC_FLAG_C;
case LT:
case GE: return CC_FLAG_S | CC_FLAG_O;
case LTU:
case GEU: return CC_FLAG_C;
case EQ:
case NE: return CC_FLAG_Z;
default: gcc_unreachable ();
}
}
/* Convert a CC_MODE to the set of flags that it represents. */
static unsigned int
flags_from_mode (enum machine_mode mode)
{
switch (mode)
{
case CCmode: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O | CC_FLAG_C;
case CC_ZSmode: return CC_FLAG_S | CC_FLAG_Z;
case CC_ZSOmode: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O;
case CC_ZSCmode: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_C;
default: gcc_unreachable ();
case CC_ZSmode:
return CC_FLAG_S | CC_FLAG_Z;
case CC_ZSOmode:
return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O;
case CC_ZSCmode:
return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_C;
case CCmode:
return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O | CC_FLAG_C;
case CC_Fmode:
return CC_FLAG_FP;
default:
gcc_unreachable ();
}
}
static int
rx_memory_move_cost (enum machine_mode mode, reg_class_t regclass, bool in)
/* Convert a set of flags to a CC_MODE that can implement it. */
static enum machine_mode
mode_from_flags (unsigned int f)
{
return 2 + memory_move_secondary_cost (mode, regclass, in);
if (f & CC_FLAG_FP)
return CC_Fmode;
if (f & CC_FLAG_O)
{
if (f & CC_FLAG_C)
return CCmode;
else
return CC_ZSOmode;
}
else if (f & CC_FLAG_C)
return CC_ZSCmode;
else
return CC_ZSmode;
}
/* Convert an RTX_CODE to the set of flags needed to implement it.
This assumes an integer comparison. */
static unsigned int
flags_from_code (enum rtx_code code)
{
switch (code)
{
case LT:
case GE:
return CC_FLAG_S;
case GT:
case LE:
return CC_FLAG_S | CC_FLAG_O | CC_FLAG_Z;
case GEU:
case LTU:
return CC_FLAG_C;
case GTU:
case LEU:
return CC_FLAG_C | CC_FLAG_Z;
case EQ:
case NE:
return CC_FLAG_Z;
default:
gcc_unreachable ();
}
}
/* Return a CC_MODE of which both M1 and M2 are subsets. */
static enum machine_mode
rx_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
{
unsigned f;
/* Early out for identical modes. */
if (m1 == m2)
return m1;
/* There's no valid combination for FP vs non-FP. */
f = flags_from_mode (m1) | flags_from_mode (m2);
if (f & CC_FLAG_FP)
return VOIDmode;
/* Otherwise, see what mode can implement all the flags. */
return mode_from_flags (f);
}
/* Return the minimal CC mode needed to implement (CMP_CODE X Y). */
@ -2616,24 +2674,89 @@ rx_select_cc_mode (enum rtx_code cmp_code, rtx x, rtx y ATTRIBUTE_UNUSED)
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
return CC_Fmode;
switch (cmp_code)
return mode_from_flags (flags_from_code (cmp_code));
}
/* Split the floating-point comparison IN into individual comparisons
O1 and O2. O2 may be UNKNOWN if there is no second comparison.
Return true iff the comparison operands must be swapped. */
bool
rx_split_fp_compare (enum rtx_code in, enum rtx_code *o1, enum rtx_code *o2)
{
enum rtx_code cmp1 = in, cmp2 = UNKNOWN;
bool swap = false;
switch (in)
{
case EQ:
case NE:
case ORDERED:
case UNORDERED:
case LT:
case GE:
return CC_ZSmode;
case EQ:
case NE:
break;
case GT:
case LE:
return CC_ZSOmode;
case GEU:
case LTU:
case GTU:
case LEU:
return CC_ZSCmode;
cmp1 = swap_condition (cmp1);
swap = true;
break;
case UNEQ:
cmp1 = UNORDERED;
cmp2 = EQ;
break;
case UNLT:
cmp1 = UNORDERED;
cmp2 = LT;
break;
case UNGE:
cmp1 = UNORDERED;
cmp2 = GE;
break;
case UNLE:
cmp1 = UNORDERED;
cmp2 = GT;
swap = true;
break;
case UNGT:
cmp1 = UNORDERED;
cmp2 = LE;
swap = true;
break;
case LTGT:
cmp1 = ORDERED;
cmp2 = NE;
break;
default:
return CCmode;
gcc_unreachable ();
}
*o1 = cmp1;
*o2 = cmp2;
return swap;
}
/* Split the conditional branch. Emit (COMPARE C1 C2) into CC_REG with
CC_MODE, and use that in branches based on that compare. */
void
rx_split_cbranch (enum machine_mode cc_mode, enum rtx_code cmp1,
rtx c1, rtx c2, rtx label)
{
rtx flags, x;
flags = gen_rtx_REG (cc_mode, CC_REG);
x = gen_rtx_COMPARE (cc_mode, c1, c2);
x = gen_rtx_SET (VOIDmode, flags, x);
emit_insn (x);
x = gen_rtx_fmt_ee (cmp1, VOIDmode, flags, const0_rtx);
x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, label, pc_rtx);
x = gen_rtx_SET (VOIDmode, pc_rtx, x);
emit_jump_insn (x);
}

View File

@ -19,14 +19,6 @@
;; <http://www.gnu.org/licenses/>.
;; This code iterator allows all branch instructions to
;; be generated from a single define_expand template.
(define_code_iterator most_cond [eq ne gt ge lt le gtu geu ltu leu
unordered ordered ])
;; Likewise, but only the ones that use Z or S.
(define_code_iterator zs_cond [eq ne gtu geu ltu leu ])
;; This code iterator is used for sign- and zero- extensions.
(define_mode_iterator small_int_modes [(HI "") (QI "")])
@ -150,6 +142,8 @@
(define_insn_reservation "throughput_18_latency_18" 1
(eq_attr "timings" "1818") "throughput*18")
;; ----------------------------------------------------------------------------
;; Comparisons
;; Note - we do not specify the two instructions necessary to perform
@ -160,245 +154,228 @@
(define_expand "cbranchsi4"
[(set (pc)
(if_then_else (match_operator 0 "comparison_operator"
[(match_operand:SI 1 "register_operand")
(match_operand:SI 2 "rx_source_operand")])
(label_ref (match_operand 3 ""))
(pc)))
]
""
(if_then_else
(match_operator 0 "comparison_operator"
[(match_operand:SI 1 "register_operand")
(match_operand:SI 2 "rx_source_operand")])
(label_ref (match_operand 3 ""))
(pc)))]
""
)
(define_insn_and_split "*cbranchsi4_<code>"
(define_insn_and_split "*cbranchsi4"
[(set (pc)
(if_then_else (most_cond (match_operand:SI 0 "register_operand" "r")
(match_operand:SI 1 "rx_source_operand" "riQ"))
(label_ref (match_operand 2 "" ""))
(pc)))
]
(if_then_else
(match_operator 3 "comparison_operator"
[(match_operand:SI 0 "register_operand" "r")
(match_operand:SI 1 "rx_source_operand" "riQ")])
(match_operand 2 "label_ref_operand" "")
(pc)))]
""
"#"
"reload_completed"
[(const_int 0)]
"
/* We contstruct the split by hand as otherwise the JUMP_LABEL
attribute is not set correctly on the jump insn. */
emit_insn (gen_cmpsi (operands[0], operands[1]));
emit_jump_insn (gen_conditional_branch (operands[2],
gen_rtx_fmt_ee (<most_cond:CODE>, CCmode,
gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
"
)
{
rx_split_cbranch (CCmode, GET_CODE (operands[3]),
operands[0], operands[1], operands[2]);
DONE;
})
;; -----------------------------------------------------------------------------
;; These two are the canonical TST/branch insns. However, GCC
;; generates a wide variety of tst-like patterns, we catch those
;; below.
(define_insn_and_split "*tstbranchsi4_<code>"
[(set (pc)
(if_then_else (zs_cond (and:SI (match_operand:SI 0 "register_operand" "r")
(match_operand:SI 1 "rx_source_operand" "riQ"))
(const_int 0))
(label_ref (match_operand 2 "" ""))
(pc)))
]
""
"#"
"reload_completed"
[(const_int 0)]
"
emit_insn (gen_tstsi (operands[0], operands[1]));
emit_jump_insn (gen_conditional_branch (operands[2],
gen_rtx_fmt_ee (<zs_cond:CODE>, CCmode,
gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
"
)
;; Inverse of above
(define_insn_and_split "*tstbranchsi4r_<code>"
[(set (pc)
(if_then_else (zs_cond (and:SI (match_operand:SI 0 "register_operand" "r")
(match_operand:SI 1 "rx_source_operand" "riQ"))
(const_int 0))
(pc)
(label_ref (match_operand 2 "" ""))))
]
""
"#"
"reload_completed"
[(const_int 0)]
"
emit_insn (gen_tstsi (operands[0], operands[1]));
emit_jump_insn (gen_conditional_branch (operands[2],
gen_rtx_fmt_ee (reverse_condition (<zs_cond:CODE>), CCmode,
gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
"
)
;; Various other ways that GCC codes "var & const"
(define_insn_and_split "*tstbranchsi4m_eq"
[(set (pc)
(if_then_else (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
(match_operand 1 "rx_constshift_operand" "i")
(match_operand 2 "rx_constshift_operand" "i"))
(const_int 0))
(label_ref (match_operand 3 "" ""))
(pc)))
]
""
"#"
""
[(set (pc)
(if_then_else (eq (and:SI (match_dup 0)
(match_dup 4))
(const_int 0))
(label_ref (match_dup 3))
(pc)))
]
"operands[4] = GEN_INT (((1 << INTVAL (operands[1]))-1) << INTVAL (operands[2]));"
)
(define_insn_and_split "*tstbranchsi4m_ne"
[(set (pc)
(if_then_else (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
(match_operand 1 "rx_constshift_operand" "i")
(match_operand 2 "rx_constshift_operand" "i"))
(const_int 0))
(label_ref (match_operand 3 "" ""))
(pc)))
]
""
"#"
""
[(set (pc)
(if_then_else (ne (and:SI (match_dup 0)
(match_dup 4))
(const_int 0))
(label_ref (match_dup 3))
(pc)))
]
"operands[4] = GEN_INT (((1 << INTVAL (operands[1]))-1) << INTVAL (operands[2]));"
)
;; -----------------------------------------------------------------------------
(define_expand "cbranchsf4"
[(set (pc)
(if_then_else (match_operator 0 "comparison_operator"
[(match_operand:SF 1 "register_operand")
(match_operand:SF 2 "rx_source_operand")])
(label_ref (match_operand 3 ""))
(pc)))
]
"ALLOW_RX_FPU_INSNS"
""
)
(define_insn_and_split "*cbranchsf4_<code>"
[(set (pc)
(if_then_else (most_cond (match_operand:SF 0 "register_operand" "r")
(match_operand:SF 1 "rx_source_operand" "rFiQ"))
(label_ref (match_operand 2 "" ""))
(pc)))
]
"ALLOW_RX_FPU_INSNS"
"#"
"&& reload_completed"
[(const_int 0)]
"
/* We contstruct the split by hand as otherwise the JUMP_LABEL
attribute is not set correctly on the jump insn. */
emit_insn (gen_cmpsf (operands[0], operands[1]));
emit_jump_insn (gen_conditional_branch (operands[2],
gen_rtx_fmt_ee (<most_cond:CODE>, CCmode,
gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
"
)
(define_insn "tstsi"
[(set (reg:CC_ZS CC_REG)
(compare:CC_ZS (and:SI (match_operand:SI 0 "register_operand" "r,r,r")
(match_operand:SI 1 "rx_source_operand" "r,i,Q"))
(const_int 0)))]
""
{
rx_float_compare_mode = false;
return "tst\t%Q1, %0";
}
[(set_attr "timings" "11,11,33")
(set_attr "length" "3,7,6")]
)
(define_insn "cmpsi"
(define_insn "*cmpsi"
[(set (reg:CC CC_REG)
(compare:CC (match_operand:SI 0 "register_operand" "r,r,r,r,r,r,r")
(match_operand:SI 1 "rx_source_operand" "r,Uint04,Int08,Sint16,Sint24,i,Q")))]
""
"reload_completed"
"cmp\t%Q1, %0"
[(set_attr "timings" "11,11,11,11,11,11,33")
(set_attr "length" "2,2,3,4,5,6,5")]
)
;; ??? g++.dg/eh/080514-1.C to see this happen.
(define_insn "cmpsf"
[(set (reg:CC_ZSO CC_REG)
(compare:CC_ZSO (match_operand:SF 0 "register_operand" "r,r,r")
(match_operand:SF 1 "rx_source_operand" "r,iF,Q")))]
;; Canonical method for representing TST.
(define_insn_and_split "*cbranchsi4_tst"
[(set (pc)
(if_then_else
(match_operator 3 "rx_zs_comparison_operator"
[(and:SI (match_operand:SI 0 "register_operand" "r")
(match_operand:SI 1 "rx_source_operand" "riQ"))
(const_int 0)])
(match_operand 2 "label_ref_operand" "")
(pc)))]
""
"#"
"reload_completed"
[(const_int 0)]
{
rx_split_cbranch (CC_ZSmode, GET_CODE (operands[3]),
XEXP (operands[3], 0), XEXP (operands[3], 1),
operands[2]);
DONE;
})
;; Various other ways that GCC codes "var & const"
(define_insn_and_split "*cbranchsi4_tst_ext"
[(set (pc)
(if_then_else
(match_operator 4 "rx_z_comparison_operator"
[(zero_extract:SI
(match_operand:SI 0 "register_operand" "r")
(match_operand:SI 1 "rx_constshift_operand" "")
(match_operand:SI 2 "rx_constshift_operand" ""))
(const_int 0)])
(match_operand 3 "label_ref_operand" "")
(pc)))]
""
"#"
"reload_completed"
[(const_int 0)]
{
HOST_WIDE_INT mask;
rtx x;
mask = 1;
mask <<= INTVAL (operands[1]);
mask -= 1;
mask <<= INTVAL (operands[2]);
x = gen_rtx_AND (SImode, operands[0], gen_int_mode (mask, SImode));
rx_split_cbranch (CC_ZSmode, GET_CODE (operands[4]),
x, const0_rtx, operands[3]);
DONE;
})
(define_insn "*tstsi"
[(set (reg:CC_ZS CC_REG)
(compare:CC_ZS
(and:SI (match_operand:SI 0 "register_operand" "r,r,r")
(match_operand:SI 1 "rx_source_operand" "r,i,Q"))
(const_int 0)))]
"reload_completed"
"tst\t%Q1, %0"
[(set_attr "timings" "11,11,33")
(set_attr "length" "3,7,6")]
)
(define_expand "cbranchsf4"
[(set (pc)
(if_then_else
(match_operator 0 "comparison_operator"
[(match_operand:SF 1 "register_operand")
(match_operand:SF 2 "register_operand")])
(label_ref (match_operand 3 ""))
(pc)))]
"ALLOW_RX_FPU_INSNS"
{
rx_float_compare_mode = true;
return "fcmp\t%1, %0";
}
{
enum rtx_code cmp1, cmp2;
/* If the comparison needs swapping of operands, do that now.
Do not split the comparison in two yet. */
if (rx_split_fp_compare (GET_CODE (operands[0]), &cmp1, &cmp2))
{
rtx op1, op2;
if (cmp2 != UNKNOWN)
{
gcc_assert (cmp1 == UNORDERED);
if (cmp2 == GT)
cmp1 = UNGT;
else if (cmp2 == LE)
cmp1 = UNLE;
else
gcc_unreachable ();
}
op1 = operands[2];
op2 = operands[1];
operands[0] = gen_rtx_fmt_ee (cmp1, VOIDmode, op1, op2);
operands[1] = op1;
operands[2] = op2;
}
})
(define_insn_and_split "*cbranchsf4"
[(set (pc)
(if_then_else
(match_operator 3 "rx_fp_comparison_operator"
[(match_operand:SF 0 "register_operand" "r")
(match_operand:SF 1 "rx_source_operand" "rFiQ")])
(match_operand 2 "label_ref_operand" "")
(pc)))]
"ALLOW_RX_FPU_INSNS"
"#"
"&& reload_completed"
[(const_int 0)]
{
enum rtx_code cmp0, cmp1, cmp2;
rtx flags, lab1, lab2, over, x;
bool swap;
cmp0 = GET_CODE (operands[3]);
swap = rx_split_fp_compare (cmp0, &cmp1, &cmp2);
gcc_assert (!swap);
flags = gen_rtx_REG (CC_Fmode, CC_REG);
x = gen_rtx_COMPARE (CC_Fmode, operands[0], operands[1]);
x = gen_rtx_SET (VOIDmode, flags, x);
emit_insn (x);
over = NULL;
lab1 = lab2 = operands[2];
/* The one case of LTGT needs to be split into cmp1 && cmp2. */
if (cmp0 == LTGT)
{
over = gen_label_rtx ();
lab1 = gen_rtx_LABEL_REF (VOIDmode, over);
cmp1 = reverse_condition_maybe_unordered (cmp1);
}
/* Otherwise we split into cmp1 || cmp2. */
x = gen_rtx_fmt_ee (cmp1, VOIDmode, flags, const0_rtx);
x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, lab1, pc_rtx);
x = gen_rtx_SET (VOIDmode, pc_rtx, x);
emit_jump_insn (x);
if (cmp2 != UNKNOWN)
{
x = gen_rtx_fmt_ee (cmp2, VOIDmode, flags, const0_rtx);
x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, lab2, pc_rtx);
x = gen_rtx_SET (VOIDmode, pc_rtx, x);
emit_jump_insn (x);
}
if (over)
emit_label (over);
DONE;
})
(define_insn "*cmpsf"
[(set (reg:CC_F CC_REG)
(compare:CC_F
(match_operand:SF 0 "register_operand" "r,r,r")
(match_operand:SF 1 "rx_source_operand" "r,iF,Q")))]
"ALLOW_RX_FPU_INSNS && reload_completed"
"fcmp\t%1, %0"
[(set_attr "timings" "11,11,33")
(set_attr "length" "3,7,5")]
)
;; Flow Control Instructions:
(define_expand "b<code>"
(define_insn "*conditional_branch"
[(set (pc)
(if_then_else (most_cond (reg:CC CC_REG) (const_int 0))
(label_ref (match_operand 0))
(pc)))]
(if_then_else
(match_operator 1 "comparison_operator"
[(reg CC_REG) (const_int 0)])
(label_ref (match_operand 0 "" ""))
(pc)))]
""
""
)
(define_insn "conditional_branch"
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(reg:CC CC_REG) (const_int 0)])
(label_ref (match_operand 0 "" ""))
(pc)))]
""
{
return rx_gen_cond_branch_template (operands[1], false);
}
"b%B1\t%0"
[(set_attr "length" "8") ;; This length is wrong, but it is
;; too hard to compute statically.
(set_attr "timings" "33")] ;; The timing assumes that the branch is taken.
)
(define_insn "*reveresed_conditional_branch"
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(reg:CC CC_REG) (const_int 0)])
(pc)
(label_ref (match_operand 0 "" ""))))]
""
{
return rx_gen_cond_branch_template (operands[1], true);
}
[(set_attr "length" "8") ;; This length is wrong, but it is
;; too hard to compute statically.
(set_attr "timings" "33")] ;; The timing assumes that the branch is taken.
)
;; ----------------------------------------------------------------------------
(define_insn "jump"
[(set (pc)