int, int, FILE *));
static enum rtx_code unsigned_comparison PARAMS ((enum rtx_code code));
static rtx ix86_expand_int_compare PARAMS ((enum rtx_code, rtx, rtx));
-static rtx ix86_expand_fp_compare PARAMS ((enum rtx_code, rtx, rtx, int));
-static rtx ix86_expand_compare PARAMS ((enum rtx_code, int));
+static enum machine_mode ix86_fp_compare_mode PARAMS ((enum rtx_code));
+static int ix86_use_fcomi_compare PARAMS ((enum rtx_code));
+static enum rtx_code ix86_prepare_fp_compare_args PARAMS ((enum rtx_code,
+ rtx *, rtx *));
+static rtx ix86_expand_compare PARAMS ((enum rtx_code));
static rtx gen_push PARAMS ((rtx));
static int memory_address_length PARAMS ((rtx addr));
static int ix86_flags_dependant PARAMS ((rtx, rtx, enum attr_type));
register rtx op;
enum machine_mode mode;
{
- return ((mode == VOIDmode || GET_MODE (op) == mode)
- && GET_RTX_CLASS (GET_CODE (op)) == '<'
- && GET_CODE (op) != LE
- && GET_CODE (op) != GT);
+ if (mode != VOIDmode && GET_MODE (op) != mode)
+ return 0;
+
+ switch (GET_CODE (op))
+ {
+ case EQ: case NE:
+ case LT: case GE:
+ case LEU: case LTU: case GEU: case GTU:
+ return 1;
+
+ default:
+ return 0;
+ }
}
/* Return 1 if OP is a comparison operator that can be issued by fcmov. */
register rtx op;
enum machine_mode mode;
{
- return ((mode == VOIDmode || GET_MODE (op) == mode)
- && GET_RTX_CLASS (GET_CODE (op)) == '<'
- && GET_CODE (op) == unsigned_condition (GET_CODE (op)));
+ if (mode != VOIDmode && GET_MODE (op) != mode)
+ return 0;
+
+ switch (GET_CODE (op))
+ {
+ case EQ: case NE:
+ case LEU: case LTU: case GEU: case GTU:
+ case UNORDERED: case ORDERED:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Return 1 if OP is any normal comparison operator plus {UN}ORDERED. */
+
+int
+uno_comparison_operator (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && GET_MODE (op) != mode)
+ return 0;
+
+ switch (GET_CODE (op))
+ {
+ case EQ: case NE:
+ case LE: case LT: case GE: case GT:
+ case LEU: case LTU: case GEU: case GTU:
+ case UNORDERED: case ORDERED:
+ return 1;
+
+ default:
+ return 0;
+ }
}
/* Return 1 if OP is a binary operator that can be promoted to wider mode. */
case LEU:
suffix = "be";
break;
+ case UNORDERED:
+ suffix = "p";
+ break;
+ case ORDERED:
+ suffix = "np";
+ break;
default:
abort ();
}
case LTU:
case GEU:
case GTU:
+ case UNORDERED:
+ case ORDERED:
break;
default:
abort ();
return gen_rtx_fmt_ee (code, VOIDmode, flags, const0_rtx);
}
-/* Generate insn patterns to do a floating point compare of OPERANDS.
- If UNORDERED, allow for unordered compares. */
+/* Figure out whether to use ordered or unordered fp comparisons.
+ Return the appropriate mode to use. */
-static rtx
-ix86_expand_fp_compare (code, op0, op1, unordered)
+static enum machine_mode
+ix86_fp_compare_mode (code)
enum rtx_code code;
- rtx op0, op1;
- int unordered;
{
- enum machine_mode fpcmp_mode;
- enum machine_mode intcmp_mode;
- rtx tmp;
+ int unordered;
- /* When not doing IEEE compliant compares, disable unordered. */
- if (! TARGET_IEEE_FP)
- unordered = 0;
- fpcmp_mode = unordered ? CCFPUmode : CCFPmode;
+ switch (code)
+ {
+ case NE: case EQ:
+ /* When not doing IEEE compliant compares, fault on NaNs. */
+ unordered = (TARGET_IEEE_FP != 0);
+ break;
+
+ case LT: case LE: case GT: case GE:
+ unordered = 0;
+ break;
+
+ case UNORDERED: case ORDERED:
+ case UNEQ: case UNGE: case UNGT: case UNLE: case UNLT: case LTGT:
+ unordered = 1;
+ break;
+
+ default:
+ abort ();
+ }
/* ??? If we knew whether invalid-operand exceptions were masked,
we could rely on fcom to raise an exception and take care of
- NaNs. But we don't. We could know this from c9x math bits. */
+ NaNs. But we don't. We could know this from c99 math pragmas. */
if (TARGET_IEEE_FP)
unordered = 1;
+ return unordered ? CCFPUmode : CCFPmode;
+}
+
+/* Return true if we should use an FCOMI instruction for this fp comparison. */
+
+static int
+ix86_use_fcomi_compare (code)
+ enum rtx_code code;
+{
+ return (TARGET_CMOVE
+ && (code == ORDERED || code == UNORDERED
+ /* All other unordered compares require checking
+ multiple sets of bits. */
+ || ix86_fp_compare_mode (code) == CCFPmode));
+}
+
+/* Swap, force into registers, or otherwise massage the two operands
+ to a fp comparison. The operands are updated in place; the new
+ comparsion code is returned. */
+
+static enum rtx_code
+ix86_prepare_fp_compare_args (code, pop0, pop1)
+ enum rtx_code code;
+ rtx *pop0, *pop1;
+{
+ enum machine_mode fpcmp_mode = ix86_fp_compare_mode (code);
+ rtx op0 = *pop0, op1 = *pop1;
+ enum machine_mode op_mode = GET_MODE (op0);
+
/* All of the unordered compare instructions only work on registers.
- The same is true of the XFmode compare instructions. */
- if (unordered || GET_MODE (op0) == XFmode)
+ The same is true of the XFmode compare instructions. The same is
+ true of the fcomi compare instructions. */
+
+ if (fpcmp_mode == CCFPUmode
+ || op_mode == XFmode
+ || ix86_use_fcomi_compare (code))
{
- op0 = force_reg (GET_MODE (op0), op0);
- op1 = force_reg (GET_MODE (op1), op1);
+ op0 = force_reg (op_mode, op0);
+ op1 = force_reg (op_mode, op1);
}
else
{
}
if (GET_CODE (op0) != REG)
- op0 = force_reg (GET_MODE (op0), op0);
+ op0 = force_reg (op_mode, op0);
if (CONSTANT_P (op1))
{
if (standard_80387_constant_p (op1))
- op1 = force_reg (GET_MODE (op1), op1);
+ op1 = force_reg (op_mode, op1);
else
- op1 = validize_mem (force_const_mem (GET_MODE (op1), op1));
+ op1 = validize_mem (force_const_mem (op_mode, op1));
}
}
+ *pop0 = op0;
+ *pop1 = op1;
+ return code;
+}
+
+/* Generate insn patterns to do a floating point compare of OPERANDS. */
+
+rtx
+ix86_expand_fp_compare (code, op0, op1, scratch)
+ enum rtx_code code;
+ rtx op0, op1, scratch;
+{
+ enum machine_mode fpcmp_mode, intcmp_mode;
+ rtx tmp;
+
+ fpcmp_mode = ix86_fp_compare_mode (code);
+ code = ix86_prepare_fp_compare_args (code, &op0, &op1);
+
/* %%% fcomi is probably always faster, even when dealing with memory,
since compare-and-branch would be three insns instead of four. */
- if (TARGET_CMOVE && !unordered)
+ if (ix86_use_fcomi_compare (code))
{
- if (GET_CODE (op0) != REG)
- op0 = force_reg (GET_MODE (op0), op0);
- if (GET_CODE (op1) != REG)
- op1 = force_reg (GET_MODE (op1), op1);
-
tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1);
tmp = gen_rtx_SET (VOIDmode, gen_rtx_REG (fpcmp_mode, FLAGS_REG), tmp);
emit_insn (tmp);
/* The FP codes work out to act like unsigned. */
code = unsigned_comparison (code);
- intcmp_mode = fpcmp_mode;
+ intcmp_mode = CCmode;
}
else
{
rtx tmp2;
tmp = gen_rtx_COMPARE (fpcmp_mode, op0, op1);
tmp2 = gen_rtx_UNSPEC (HImode, gen_rtvec (1, tmp), 9);
- tmp = gen_reg_rtx (HImode);
- emit_insn (gen_rtx_SET (VOIDmode, tmp, tmp2));
+ emit_insn (gen_rtx_SET (VOIDmode, scratch, tmp2));
- if (! unordered)
+ if (fpcmp_mode == CCFPmode
+ || code == ORDERED
+ || code == UNORDERED)
{
/* We have two options here -- use sahf, or testing bits of ah
directly. On PPRO, they are equivalent, sahf being one byte
if (TARGET_USE_SAHF || optimize_size)
{
do_sahf:
+ emit_insn (gen_x86_sahf_1 (scratch));
/* The FP codes work out to act like unsigned. */
code = unsigned_comparison (code);
- emit_insn (gen_x86_sahf_1 (tmp));
intcmp_mode = CCmode;
}
else
mask = 0x40;
code = EQ;
break;
+ case UNORDERED:
+ mask = 0x04;
+ code = NE;
+ break;
+ case ORDERED:
+ mask = 0x04;
+ code = EQ;
+ break;
+
default:
abort ();
}
- emit_insn (gen_testqi_ext_ccno_0 (tmp, GEN_INT (mask)));
+ emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (mask)));
intcmp_mode = CCNOmode;
}
}
switch (code)
{
case GT:
- emit_insn (gen_testqi_ext_ccno_0 (tmp, GEN_INT (0x45)));
+ emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x45)));
code = EQ;
break;
case LT:
- emit_insn (gen_andqi_ext_0 (tmp, tmp, GEN_INT (0x45)));
- emit_insn (gen_cmpqi_ext_3 (tmp, GEN_INT (0x01)));
+ emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45)));
+ emit_insn (gen_cmpqi_ext_3 (scratch, GEN_INT (0x01)));
intcmp_mode = CCmode;
code = EQ;
break;
case GE:
- emit_insn (gen_testqi_ext_ccno_0 (tmp, GEN_INT (0x05)));
+ emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x05)));
code = EQ;
break;
case LE:
- emit_insn (gen_andqi_ext_0 (tmp, tmp, GEN_INT (0x45)));
- emit_insn (gen_addqi_ext_1 (tmp, tmp, constm1_rtx));
- emit_insn (gen_cmpqi_ext_3 (tmp, GEN_INT (0x40)));
+ emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45)));
+ emit_insn (gen_addqi_ext_1 (scratch, scratch, constm1_rtx));
+ emit_insn (gen_cmpqi_ext_3 (scratch, GEN_INT (0x40)));
intcmp_mode = CCmode;
code = LTU;
break;
case EQ:
- emit_insn (gen_andqi_ext_0 (tmp, tmp, GEN_INT (0x45)));
- emit_insn (gen_cmpqi_ext_3 (tmp, GEN_INT (0x40)));
+ emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45)));
+ emit_insn (gen_cmpqi_ext_3 (scratch, GEN_INT (0x40)));
intcmp_mode = CCmode;
code = EQ;
break;
case NE:
- emit_insn (gen_andqi_ext_0 (tmp, tmp, GEN_INT (0x45)));
- emit_insn (gen_xorqi_cc_ext_1 (tmp, tmp, GEN_INT (0x40)));
+ emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45)));
+ emit_insn (gen_xorqi_cc_ext_1 (scratch, scratch, GEN_INT (0x40)));
+ code = NE;
+ break;
+
+ case UNORDERED:
+ emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x04)));
+ code = NE;
+ break;
+ case ORDERED:
+ emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x04)));
+ code = EQ;
+ break;
+ case UNEQ:
+ emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x40)));
+ code = NE;
+ break;
+ case UNGE:
+ emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45)));
+ emit_insn (gen_xorqi_cc_ext_1 (scratch, scratch, GEN_INT (0x01)));
+ code = NE;
+ break;
+ case UNGT:
+ emit_insn (gen_andqi_ext_0 (scratch, scratch, GEN_INT (0x45)));
+ emit_insn (gen_addqi_ext_1 (scratch, scratch, constm1_rtx));
+ emit_insn (gen_cmpqi_ext_3 (scratch, GEN_INT (0x44)));
+ code = GEU;
+ break;
+ case UNLE:
+ emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x45)));
code = NE;
break;
+ case UNLT:
+ emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x01)));
+ code = NE;
+ break;
+ case LTGT:
+ emit_insn (gen_testqi_ext_ccno_0 (scratch, GEN_INT (0x40)));
+ code = EQ;
+ break;
+
default:
abort ();
}
}
static rtx
-ix86_expand_compare (code, unordered)
+ix86_expand_compare (code)
enum rtx_code code;
- int unordered;
{
rtx op0, op1, ret;
op0 = ix86_compare_op0;
op1 = ix86_compare_op1;
if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
- ret = ix86_expand_fp_compare (code, op0, op1, unordered);
+ ret = ix86_expand_fp_compare (code, op0, op1, gen_reg_rtx (HImode));
else
ret = ix86_expand_int_compare (code, op0, op1);
}
void
-ix86_expand_branch (code, unordered, label)
+ix86_expand_branch (code, label)
enum rtx_code code;
- int unordered;
rtx label;
{
- rtx tmp, lo[2], hi[2], label2;
- enum rtx_code code1, code2, code3;
+ rtx tmp;
- if (GET_MODE (ix86_compare_op0) != DImode)
+ switch (GET_MODE (ix86_compare_op0))
{
- tmp = ix86_expand_compare (code, unordered);
+ case QImode:
+ case HImode:
+ case SImode:
+ tmp = ix86_expand_compare (code);
tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
gen_rtx_LABEL_REF (VOIDmode, label),
pc_rtx);
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
return;
- }
-
- /* Expand DImode branch into multiple compare+branch. */
- if (CONSTANT_P (ix86_compare_op0) && ! CONSTANT_P (ix86_compare_op1))
- {
- tmp = ix86_compare_op0;
- ix86_compare_op0 = ix86_compare_op1;
- ix86_compare_op1 = tmp;
- code = swap_condition (code);
- }
- split_di (&ix86_compare_op0, 1, lo+0, hi+0);
- split_di (&ix86_compare_op1, 1, lo+1, hi+1);
-
- /* When comparing for equality, we can use (hi0^hi1)|(lo0^lo1) to avoid
- two branches. This costs one extra insn, so disable when optimizing
- for size. */
+ case SFmode:
+ case DFmode:
+ case XFmode:
+ /* Don't expand the comparison early, so that we get better code
+ when jump or whoever decides to reverse the comparison. */
+ {
+ rtvec vec;
+ int use_fcomi;
+
+ code = ix86_prepare_fp_compare_args (code, &ix86_compare_op0,
+ &ix86_compare_op1);
+
+ tmp = gen_rtx_fmt_ee (code, ix86_fp_compare_mode (code),
+ ix86_compare_op0, ix86_compare_op1);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, label),
+ pc_rtx);
+ tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp);
+
+ use_fcomi = ix86_use_fcomi_compare (code);
+ vec = rtvec_alloc (3 + !use_fcomi);
+ RTVEC_ELT (vec, 0) = tmp;
+ RTVEC_ELT (vec, 1)
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18));
+ RTVEC_ELT (vec, 2)
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17));
+ if (! use_fcomi)
+ RTVEC_ELT (vec, 3)
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode));
+
+ emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
+ return;
+ }
- if ((code == EQ || code == NE)
- && (!optimize_size
- || hi[1] == const0_rtx || lo[1] == const0_rtx))
- {
- rtx xor0, xor1;
+ case DImode:
+ /* Expand DImode branch into multiple compare+branch. */
+ {
+ rtx lo[2], hi[2], label2;
+ enum rtx_code code1, code2, code3;
- xor1 = hi[0];
- if (hi[1] != const0_rtx)
- {
- xor1 = expand_binop (SImode, xor_optab, xor1, hi[1],
- NULL_RTX, 0, OPTAB_WIDEN);
- }
+ if (CONSTANT_P (ix86_compare_op0) && ! CONSTANT_P (ix86_compare_op1))
+ {
+ tmp = ix86_compare_op0;
+ ix86_compare_op0 = ix86_compare_op1;
+ ix86_compare_op1 = tmp;
+ code = swap_condition (code);
+ }
+ split_di (&ix86_compare_op0, 1, lo+0, hi+0);
+ split_di (&ix86_compare_op1, 1, lo+1, hi+1);
- xor0 = lo[0];
- if (lo[1] != const0_rtx)
- {
- xor0 = expand_binop (SImode, xor_optab, xor0, lo[1],
- NULL_RTX, 0, OPTAB_WIDEN);
- }
+ /* When comparing for equality, we can use (hi0^hi1)|(lo0^lo1) to
+ avoid two branches. This costs one extra insn, so disable when
+ optimizing for size. */
- tmp = expand_binop (SImode, ior_optab, xor1, xor0,
- NULL_RTX, 0, OPTAB_WIDEN);
+ if ((code == EQ || code == NE)
+ && (!optimize_size
+ || hi[1] == const0_rtx || lo[1] == const0_rtx))
+ {
+ rtx xor0, xor1;
- ix86_compare_op0 = tmp;
- ix86_compare_op1 = const0_rtx;
- ix86_expand_branch (code, unordered, label);
- return;
- }
+ xor1 = hi[0];
+ if (hi[1] != const0_rtx)
+ xor1 = expand_binop (SImode, xor_optab, xor1, hi[1],
+ NULL_RTX, 0, OPTAB_WIDEN);
- /* Otherwise, if we are doing less-than, op1 is a constant and the
- low word is zero, then we can just examine the high word. */
+ xor0 = lo[0];
+ if (lo[1] != const0_rtx)
+ xor0 = expand_binop (SImode, xor_optab, xor0, lo[1],
+ NULL_RTX, 0, OPTAB_WIDEN);
- if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
- && (code == LT || code == LTU))
- {
- ix86_compare_op0 = hi[0];
- ix86_compare_op1 = hi[1];
- ix86_expand_branch (code, unordered, label);
- return;
- }
+ tmp = expand_binop (SImode, ior_optab, xor1, xor0,
+ NULL_RTX, 0, OPTAB_WIDEN);
- /* Otherwise, we need two or three jumps. */
+ ix86_compare_op0 = tmp;
+ ix86_compare_op1 = const0_rtx;
+ ix86_expand_branch (code, label);
+ return;
+ }
- label2 = gen_label_rtx ();
+ /* Otherwise, if we are doing less-than, op1 is a constant and the
+ low word is zero, then we can just examine the high word. */
- code1 = code;
- code2 = swap_condition (code);
- code3 = unsigned_condition (code);
+ if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
+ && (code == LT || code == LTU))
+ {
+ ix86_compare_op0 = hi[0];
+ ix86_compare_op1 = hi[1];
+ ix86_expand_branch (code, label);
+ return;
+ }
- switch (code)
- {
- case LT: case GT: case LTU: case GTU:
- break;
+ /* Otherwise, we need two or three jumps. */
- case LE: code1 = LT; code2 = GT; break;
- case GE: code1 = GT; code2 = LT; break;
- case LEU: code1 = LTU; code2 = GTU; break;
- case GEU: code1 = GTU; code2 = LTU; break;
+ label2 = gen_label_rtx ();
- case EQ: code1 = NIL; code2 = NE; break;
- case NE: code2 = NIL; break;
+ code1 = code;
+ code2 = swap_condition (code);
+ code3 = unsigned_condition (code);
- default:
- abort ();
- }
+ switch (code)
+ {
+ case LT: case GT: case LTU: case GTU:
+ break;
- /*
- * a < b =>
- * if (hi(a) < hi(b)) goto true;
- * if (hi(a) > hi(b)) goto false;
- * if (lo(a) < lo(b)) goto true;
- * false:
- */
+ case LE: code1 = LT; code2 = GT; break;
+ case GE: code1 = GT; code2 = LT; break;
+ case LEU: code1 = LTU; code2 = GTU; break;
+ case GEU: code1 = GTU; code2 = LTU; break;
- ix86_compare_op0 = hi[0];
- ix86_compare_op1 = hi[1];
+ case EQ: code1 = NIL; code2 = NE; break;
+ case NE: code2 = NIL; break;
- if (code1 != NIL)
- ix86_expand_branch (code1, unordered, label);
- if (code2 != NIL)
- ix86_expand_branch (code2, unordered, label2);
+ default:
+ abort ();
+ }
- ix86_compare_op0 = lo[0];
- ix86_compare_op1 = lo[1];
- ix86_expand_branch (code3, unordered, label);
+ /*
+ * a < b =>
+ * if (hi(a) < hi(b)) goto true;
+ * if (hi(a) > hi(b)) goto false;
+ * if (lo(a) < lo(b)) goto true;
+ * false:
+ */
+
+ ix86_compare_op0 = hi[0];
+ ix86_compare_op1 = hi[1];
+
+ if (code1 != NIL)
+ ix86_expand_branch (code1, label);
+ if (code2 != NIL)
+ ix86_expand_branch (code2, label2);
+
+ ix86_compare_op0 = lo[0];
+ ix86_compare_op1 = lo[1];
+ ix86_expand_branch (code3, label);
+
+ if (code2 != NIL)
+ emit_label (label2);
+ return;
+ }
- if (code2 != NIL)
- emit_label (label2);
+ default:
+ abort ();
+ }
}
int
-ix86_expand_setcc (code, unordered, dest)
+ix86_expand_setcc (code, dest)
enum rtx_code code;
- int unordered;
rtx dest;
{
rtx ret, tmp;
if (type == 0)
emit_move_insn (dest, const0_rtx);
- ret = ix86_expand_compare (code, unordered);
+ ret = ix86_expand_compare (code);
PUT_MODE (ret, QImode);
tmp = dest;
code = GEU;
ix86_compare_op1 = GEN_INT (INTVAL (ix86_compare_op1) + 1);
}
+
start_sequence ();
- compare_op = ix86_expand_compare (code, code == EQ || code == NE);
+ compare_op = ix86_expand_compare (code);
compare_seq = gen_sequence ();
end_sequence ();
case GE:
case GT:
tmp = gen_reg_rtx (QImode);
- ix86_expand_setcc (code, 0, tmp);
+ ix86_expand_setcc (code, tmp);
code = NE;
ix86_compare_op0 = tmp;
ix86_compare_op1 = const0_rtx;