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:
parent
af530bb412
commit
e963cb1a8a
@ -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.
|
||||
|
@ -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")
|
||||
)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user