#include "config.h"
#include "system.h"
+#include "toplev.h"
+
+/* Include insn-config.h before expr.h so that HAVE_conditional_move
+ is properly defined. */
+#include "insn-config.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "insn-flags.h"
#include "insn-codes.h"
+#include "function.h"
#include "expr.h"
-#include "insn-config.h"
#include "recog.h"
#include "reload.h"
static int add_equal_note PROTO((rtx, rtx, enum rtx_code, rtx, rtx));
static rtx widen_operand PROTO((rtx, enum machine_mode,
enum machine_mode, int, int));
+static int expand_cmplxdiv_straight PROTO((rtx, rtx, rtx, rtx,
+ rtx, rtx, enum machine_mode,
+ int, enum optab_methods,
+ enum mode_class, optab));
+static int expand_cmplxdiv_wide PROTO((rtx, rtx, rtx, rtx,
+ rtx, rtx, enum machine_mode,
+ int, enum optab_methods,
+ enum mode_class, optab));
static enum insn_code can_fix_p PROTO((enum machine_mode, enum machine_mode,
int, int *));
static enum insn_code can_float_p PROTO((enum machine_mode, enum machine_mode,
int));
static rtx ftruncify PROTO((rtx));
static optab init_optab PROTO((enum rtx_code));
-static void init_libfuncs PROTO((optab, int, int, char *, int));
-static void init_integral_libfuncs PROTO((optab, char *, int));
-static void init_floating_libfuncs PROTO((optab, char *, int));
+static void init_libfuncs PROTO((optab, int, int, const char *, int));
+static void init_integral_libfuncs PROTO((optab, const char *, int));
+static void init_floating_libfuncs PROTO((optab, const char *, int));
#ifdef HAVE_conditional_trap
static void init_traps PROTO((void));
#endif
+static int cmp_available_p PROTO((enum machine_mode, enum rtx_code, int));
+static void emit_cmp_and_jump_insn_1 PROTO((rtx, rtx, enum machine_mode,
+ enum rtx_code, int, rtx));
+static void prepare_cmp_insn PROTO((rtx *, rtx *, enum rtx_code *, rtx,
+ enum machine_mode *, int *, int));
+static rtx prepare_operand PROTO((int, rtx, int, enum machine_mode,
+ enum machine_mode, int));
+static void prepare_float_lib_cmp PROTO((rtx *, rtx *, enum rtx_code *,
+ enum machine_mode *, int *));
\f
/* Add a REG_EQUAL note to the last insn in SEQ. TARGET is being set to
the result of operation CODE applied to OP0 (and OP1 if it is a binary
else
note = gen_rtx_fmt_ee (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1));
- REG_NOTES (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1))
- = gen_rtx_EXPR_LIST (REG_EQUAL, note,
- REG_NOTES (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1)));
+ set_unique_reg_note (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1), REG_EQUAL, note);
return 1;
}
return result;
}
\f
+/* Generate code to perform a straightforward complex divide. */
+
+static int
+expand_cmplxdiv_straight (real0, real1, imag0, imag1, realr, imagr, submode,
+ unsignedp, methods, class, binoptab)
+ rtx real0, real1, imag0, imag1, realr, imagr;
+ enum machine_mode submode;
+ int unsignedp;
+ enum optab_methods methods;
+ enum mode_class class;
+ optab binoptab;
+{
+ rtx divisor;
+ rtx real_t, imag_t;
+ rtx temp1, temp2;
+ rtx res;
+
+ /* Don't fetch these from memory more than once. */
+ real0 = force_reg (submode, real0);
+ real1 = force_reg (submode, real1);
+
+ if (imag0 != 0)
+ imag0 = force_reg (submode, imag0);
+
+ imag1 = force_reg (submode, imag1);
+
+ /* Divisor: c*c + d*d. */
+ temp1 = expand_binop (submode, smul_optab, real1, real1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, smul_optab, imag1, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ return 0;
+
+ divisor = expand_binop (submode, add_optab, temp1, temp2,
+ NULL_RTX, unsignedp, methods);
+ if (divisor == 0)
+ return 0;
+
+ if (imag0 == 0)
+ {
+ /* Mathematically, ((a)(c-id))/divisor. */
+ /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)). */
+
+ /* Calculate the dividend. */
+ real_t = expand_binop (submode, smul_optab, real0, real1,
+ NULL_RTX, unsignedp, methods);
+
+ imag_t = expand_binop (submode, smul_optab, real0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (real_t == 0 || imag_t == 0)
+ return 0;
+
+ imag_t = expand_unop (submode, neg_optab, imag_t,
+ NULL_RTX, unsignedp);
+ }
+ else
+ {
+ /* Mathematically, ((a+ib)(c-id))/divider. */
+ /* Calculate the dividend. */
+ temp1 = expand_binop (submode, smul_optab, real0, real1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, smul_optab, imag0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ return 0;
+
+ real_t = expand_binop (submode, add_optab, temp1, temp2,
+ NULL_RTX, unsignedp, methods);
+
+ temp1 = expand_binop (submode, smul_optab, imag0, real1,
+ NULL_RTX, unsignedp, methods);
+
+ temp2 = expand_binop (submode, smul_optab, real0, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0 || temp2 == 0)
+ return 0;
+
+ imag_t = expand_binop (submode, sub_optab, temp1, temp2,
+ NULL_RTX, unsignedp, methods);
+
+ if (real_t == 0 || imag_t == 0)
+ return 0;
+ }
+
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, real_t, divisor,
+ realr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ real_t, divisor, realr, unsignedp);
+
+ if (res == 0)
+ return 0;
+
+ if (res != realr)
+ emit_move_insn (realr, res);
+
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, imag_t, divisor,
+ imagr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ imag_t, divisor, imagr, unsignedp);
+
+ if (res == 0)
+ return 0;
+
+ if (res != imagr)
+ emit_move_insn (imagr, res);
+
+ return 1;
+}
+\f
+/* Generate code to perform a wide-input-range-acceptable complex divide. */
+
+static int
+expand_cmplxdiv_wide (real0, real1, imag0, imag1, realr, imagr, submode,
+ unsignedp, methods, class, binoptab)
+ rtx real0, real1, imag0, imag1, realr, imagr;
+ enum machine_mode submode;
+ int unsignedp;
+ enum optab_methods methods;
+ enum mode_class class;
+ optab binoptab;
+{
+ rtx ratio, divisor;
+ rtx real_t, imag_t;
+ rtx temp1, temp2, lab1, lab2;
+ enum machine_mode mode;
+ int align;
+ rtx res;
+
+ /* Don't fetch these from memory more than once. */
+ real0 = force_reg (submode, real0);
+ real1 = force_reg (submode, real1);
+
+ if (imag0 != 0)
+ imag0 = force_reg (submode, imag0);
+
+ imag1 = force_reg (submode, imag1);
+
+ /* XXX What's an "unsigned" complex number? */
+ if (unsignedp)
+ {
+ temp1 = real1;
+ temp2 = imag1;
+ }
+ else
+ {
+ temp1 = expand_abs (submode, real1, NULL_RTX, 1);
+ temp2 = expand_abs (submode, imag1, NULL_RTX, 1);
+ }
+
+ if (temp1 == 0 || temp2 == 0)
+ return 0;
+
+ mode = GET_MODE (temp1);
+ align = GET_MODE_ALIGNMENT (mode);
+ lab1 = gen_label_rtx ();
+ emit_cmp_and_jump_insns (temp1, temp2, LT, NULL_RTX,
+ mode, unsignedp, align, lab1);
+
+ /* |c| >= |d|; use ratio d/c to scale dividend and divisor. */
+
+ if (class == MODE_COMPLEX_FLOAT)
+ ratio = expand_binop (submode, binoptab, imag1, real1,
+ NULL_RTX, unsignedp, methods);
+ else
+ ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ imag1, real1, NULL_RTX, unsignedp);
+
+ if (ratio == 0)
+ return 0;
+
+ /* Calculate divisor. */
+
+ temp1 = expand_binop (submode, smul_optab, imag1, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ divisor = expand_binop (submode, add_optab, temp1, real1,
+ NULL_RTX, unsignedp, methods);
+
+ if (divisor == 0)
+ return 0;
+
+ /* Calculate dividend. */
+
+ if (imag0 == 0)
+ {
+ real_t = real0;
+
+ /* Compute a / (c+id) as a / (c+d(d/c)) + i (-a(d/c)) / (c+d(d/c)). */
+
+ imag_t = expand_binop (submode, smul_optab, real0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (imag_t == 0)
+ return 0;
+
+ imag_t = expand_unop (submode, neg_optab, imag_t,
+ NULL_RTX, unsignedp);
+
+ if (real_t == 0 || imag_t == 0)
+ return 0;
+ }
+ else
+ {
+ /* Compute (a+ib)/(c+id) as
+ (a+b(d/c))/(c+d(d/c) + i(b-a(d/c))/(c+d(d/c)). */
+
+ temp1 = expand_binop (submode, smul_optab, imag0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ real_t = expand_binop (submode, add_optab, temp1, real0,
+ NULL_RTX, unsignedp, methods);
+
+ temp1 = expand_binop (submode, smul_optab, real0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ imag_t = expand_binop (submode, sub_optab, imag0, temp1,
+ NULL_RTX, unsignedp, methods);
+
+ if (real_t == 0 || imag_t == 0)
+ return 0;
+ }
+
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, real_t, divisor,
+ realr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ real_t, divisor, realr, unsignedp);
+
+ if (res == 0)
+ return 0;
+
+ if (res != realr)
+ emit_move_insn (realr, res);
+
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, imag_t, divisor,
+ imagr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ imag_t, divisor, imagr, unsignedp);
+
+ if (res == 0)
+ return 0;
+
+ if (res != imagr)
+ emit_move_insn (imagr, res);
+
+ lab2 = gen_label_rtx ();
+ emit_jump_insn (gen_jump (lab2));
+ emit_barrier ();
+
+ emit_label (lab1);
+
+ /* |d| > |c|; use ratio c/d to scale dividend and divisor. */
+
+ if (class == MODE_COMPLEX_FLOAT)
+ ratio = expand_binop (submode, binoptab, real1, imag1,
+ NULL_RTX, unsignedp, methods);
+ else
+ ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ real1, imag1, NULL_RTX, unsignedp);
+
+ if (ratio == 0)
+ return 0;
+
+ /* Calculate divisor. */
+
+ temp1 = expand_binop (submode, smul_optab, real1, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ divisor = expand_binop (submode, add_optab, temp1, imag1,
+ NULL_RTX, unsignedp, methods);
+
+ if (divisor == 0)
+ return 0;
+
+ /* Calculate dividend. */
+
+ if (imag0 == 0)
+ {
+ /* Compute a / (c+id) as a(c/d) / (c(c/d)+d) + i (-a) / (c(c/d)+d). */
+
+ real_t = expand_binop (submode, smul_optab, real0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ imag_t = expand_unop (submode, neg_optab, real0,
+ NULL_RTX, unsignedp);
+
+ if (real_t == 0 || imag_t == 0)
+ return 0;
+ }
+ else
+ {
+ /* Compute (a+ib)/(c+id) as
+ (a(c/d)+b)/(c(c/d)+d) + i (b(c/d)-a)/(c(c/d)+d). */
+
+ temp1 = expand_binop (submode, smul_optab, real0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ real_t = expand_binop (submode, add_optab, temp1, imag0,
+ NULL_RTX, unsignedp, methods);
+
+ temp1 = expand_binop (submode, smul_optab, imag0, ratio,
+ NULL_RTX, unsignedp, methods);
+
+ if (temp1 == 0)
+ return 0;
+
+ imag_t = expand_binop (submode, sub_optab, temp1, real0,
+ NULL_RTX, unsignedp, methods);
+
+ if (real_t == 0 || imag_t == 0)
+ return 0;
+ }
+
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, real_t, divisor,
+ realr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ real_t, divisor, realr, unsignedp);
+
+ if (res == 0)
+ return 0;
+
+ if (res != realr)
+ emit_move_insn (realr, res);
+
+ if (class == MODE_COMPLEX_FLOAT)
+ res = expand_binop (submode, binoptab, imag_t, divisor,
+ imagr, unsignedp, methods);
+ else
+ res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+ imag_t, divisor, imagr, unsignedp);
+
+ if (res == 0)
+ return 0;
+
+ if (res != imagr)
+ emit_move_insn (imagr, res);
+
+ emit_label (lab2);
+
+ return 1;
+}
+\f
/* Generate code to perform an operation specified by BINOPTAB
on operands OP0 and OP1, with result having machine-mode MODE.
rtx carry_tmp = gen_reg_rtx (word_mode);
optab otheroptab = binoptab == add_optab ? sub_optab : add_optab;
int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
- rtx carry_in, carry_out;
+ rtx carry_in = NULL_RTX, carry_out = NULL_RTX;
rtx xop0, xop1;
/* We can handle either a 1 or -1 value for the carry. If STORE_FLAG
carry_out = gen_reg_rtx (word_mode);
carry_out = emit_store_flag_force (carry_out,
(binoptab == add_optab
- ? LTU : GTU),
+ ? LT : GT),
x, op0_piece,
word_mode, 1, normalizep);
}
/* Get out carry from adding/subtracting carry in. */
carry_tmp = emit_store_flag_force (carry_tmp,
binoptab == add_optab
- ? LTU : GTU,
+ ? LT : GT,
x, carry_in,
word_mode, 1, normalizep);
{
rtx temp = emit_move_insn (target, target);
- REG_NOTES (temp)
- = gen_rtx_EXPR_LIST (REG_EQUAL,
- gen_rtx_fmt_ee (binoptab->code, mode,
- copy_rtx (xop0),
- copy_rtx (xop1)),
- REG_NOTES (temp));
+ set_unique_reg_note (temp,
+ REG_EQUAL,
+ gen_rtx_fmt_ee (binoptab->code, mode,
+ copy_rtx (xop0),
+ copy_rtx (xop1)));
}
return target;
}
rtx op1_high = operand_subword_force (op1, high, mode);
rtx op1_low = operand_subword_force (op1, low, mode);
rtx product = 0;
- rtx op0_xhigh;
- rtx op1_xhigh;
+ rtx op0_xhigh = NULL_RTX;
+ rtx op1_xhigh = NULL_RTX;
/* If the target is the same as one of the inputs, don't use it. This
prevents problems with the REG_EQUAL note. */
if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
{
temp = emit_move_insn (product, product);
- REG_NOTES (temp)
- = gen_rtx_EXPR_LIST (REG_EQUAL,
- gen_rtx_fmt_ee (MULT, mode,
- copy_rtx (op0),
- copy_rtx (op1)),
- REG_NOTES (temp));
+ set_unique_reg_note (temp,
+ REG_EQUAL,
+ gen_rtx_fmt_ee (MULT, mode,
+ copy_rtx (op0),
+ copy_rtx (op1)));
}
return product;
}
start_sequence ();
- realr = gen_realpart (submode, target);
+ realr = gen_realpart (submode, target);
imagr = gen_imagpart (submode, target);
if (GET_MODE (op0) == mode)
{
- real0 = gen_realpart (submode, op0);
+ real0 = gen_realpart (submode, op0);
imag0 = gen_imagpart (submode, op0);
}
else
if (GET_MODE (op1) == mode)
{
- real1 = gen_realpart (submode, op1);
+ real1 = gen_realpart (submode, op1);
imag1 = gen_imagpart (submode, op1);
}
else
}
else
{
- /* Divisor is of complex type:
- X/(a+ib) */
- rtx divisor;
- rtx real_t, imag_t;
- rtx temp1, temp2;
-
- /* Don't fetch these from memory more than once. */
- real0 = force_reg (submode, real0);
- real1 = force_reg (submode, real1);
-
- if (imag0 != 0)
- imag0 = force_reg (submode, imag0);
-
- imag1 = force_reg (submode, imag1);
-
- /* Divisor: c*c + d*d */
- temp1 = expand_binop (submode, smul_optab, real1, real1,
- NULL_RTX, unsignedp, methods);
-
- temp2 = expand_binop (submode, smul_optab, imag1, imag1,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0 || temp2 == 0)
- break;
-
- divisor = expand_binop (submode, add_optab, temp1, temp2,
- NULL_RTX, unsignedp, methods);
- if (divisor == 0)
- break;
-
- if (imag0 == 0)
- {
- /* ((a)(c-id))/divisor */
- /* (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)) */
-
- /* Calculate the dividend */
- real_t = expand_binop (submode, smul_optab, real0, real1,
- NULL_RTX, unsignedp, methods);
-
- imag_t = expand_binop (submode, smul_optab, real0, imag1,
- NULL_RTX, unsignedp, methods);
-
- if (real_t == 0 || imag_t == 0)
- break;
-
- imag_t = expand_unop (submode, neg_optab, imag_t,
- NULL_RTX, unsignedp);
- }
- else
+ switch (flag_complex_divide_method)
{
- /* ((a+ib)(c-id))/divider */
- /* Calculate the dividend */
- temp1 = expand_binop (submode, smul_optab, real0, real1,
- NULL_RTX, unsignedp, methods);
-
- temp2 = expand_binop (submode, smul_optab, imag0, imag1,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0 || temp2 == 0)
- break;
-
- real_t = expand_binop (submode, add_optab, temp1, temp2,
- NULL_RTX, unsignedp, methods);
-
- temp1 = expand_binop (submode, smul_optab, imag0, real1,
- NULL_RTX, unsignedp, methods);
-
- temp2 = expand_binop (submode, smul_optab, real0, imag1,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0 || temp2 == 0)
- break;
+ case 0:
+ ok = expand_cmplxdiv_straight (real0, real1, imag0, imag1,
+ realr, imagr, submode,
+ unsignedp, methods,
+ class, binoptab);
+ break;
- imag_t = expand_binop (submode, sub_optab, temp1, temp2,
- NULL_RTX, unsignedp, methods);
+ case 1:
+ ok = expand_cmplxdiv_wide (real0, real1, imag0, imag1,
+ realr, imagr, submode,
+ unsignedp, methods,
+ class, binoptab);
+ break;
- if (real_t == 0 || imag_t == 0)
- break;
+ default:
+ abort ();
}
-
- if (class == MODE_COMPLEX_FLOAT)
- res = expand_binop (submode, binoptab, real_t, divisor,
- realr, unsignedp, methods);
- else
- res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
- real_t, divisor, realr, unsignedp);
-
- if (res == 0)
- break;
- else if (res != realr)
- emit_move_insn (realr, res);
-
- if (class == MODE_COMPLEX_FLOAT)
- res = expand_binop (submode, binoptab, imag_t, divisor,
- imagr, unsignedp, methods);
- else
- res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
- imag_t, divisor, imagr, unsignedp);
-
- if (res == 0)
- break;
- else if (res != imagr)
- emit_move_insn (imagr, res);
-
- ok = 1;
}
break;
MODE is the mode of the operand; the mode of the result is
different but can be deduced from MODE.
- UNSIGNEDP is relevant if extension is needed. */
+ */
rtx
-expand_abs (mode, op0, target, unsignedp, safe)
+expand_abs (mode, op0, target, safe)
enum machine_mode mode;
rtx op0;
rtx target;
- int unsignedp;
int safe;
{
rtx temp, op1;
do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx,
NULL_RTX, op1);
else
- {
- temp = compare_from_rtx (target, CONST0_RTX (mode), GE, 0, mode,
- NULL_RTX, 0);
- if (temp == const1_rtx)
- return target;
- else if (temp != const0_rtx)
- {
- if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0)
- emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op1));
- else
- abort ();
- }
- }
+ do_compare_rtx_and_jump (target, CONST0_RTX (mode), GE, 0, mode,
+ NULL_RTX, 0, NULL_RTX, op1);
op0 = expand_unop (mode, neg_optab, target, target, 0);
if (op0 != target)
{
last = emit_move_insn (target, target);
if (equiv)
- REG_NOTES (last)
- = gen_rtx_EXPR_LIST (REG_EQUAL, equiv, REG_NOTES (last));
+ set_unique_reg_note (last, REG_EQUAL, equiv);
}
else
last = get_last_insn ();
{
rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (note == NULL_RTX)
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (0),
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (-1),
REG_NOTES (insn));
}
}
last = emit_move_insn (target, result);
if (mov_optab->handlers[(int) GET_MODE (target)].insn_code
!= CODE_FOR_nothing)
- REG_NOTES (last) = gen_rtx_EXPR_LIST (REG_EQUAL, copy_rtx (equiv),
- REG_NOTES (last));
+ set_unique_reg_note (last, REG_EQUAL, copy_rtx (equiv));
if (prev == 0)
first = get_insns ();
emit_move_insn (x, const1_rtx);
}
-/* Generate code to compare X with Y
- so that the condition codes are set.
+/* Nonzero if we can perform a comparison of mode MODE for a conditional jump
+ straightforwardly. */
- MODE is the mode of the inputs (in case they are const_int).
- UNSIGNEDP nonzero says that X and Y are unsigned;
+static int
+cmp_available_p (mode, code, can_use_tst_p)
+ enum machine_mode mode;
+ enum rtx_code code;
+ int can_use_tst_p;
+{
+ do
+ {
+ if (cmp_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing
+ || (can_use_tst_p
+ && tst_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing))
+ return 1;
+ mode = GET_MODE_WIDER_MODE (mode);
+ } while (mode != VOIDmode);
+
+ return 0;
+}
+
+/* This function is called when we are going to emit a compare instruction that
+ compares the values found in *PX and *PY, using the rtl operator COMPARISON.
+
+ *PMODE is the mode of the inputs (in case they are const_int).
+ *PUNSIGNEDP nonzero says that the operands are unsigned;
this matters if they need to be widened.
- If they have mode BLKmode, then SIZE specifies the size of both X and Y,
- and ALIGN specifies the known shared alignment of X and Y.
+ If they have mode BLKmode, then SIZE specifies the size of both operands,
+ and ALIGN specifies the known shared alignment of the operands.
- COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).
- It is ignored for fixed-point and block comparisons;
- it is used only for floating-point comparisons. */
+ This function performs all the setup necessary so that the caller only has
+ to emit a single comparison insn. This setup can involve doing a BLKmode
+ comparison or emitting a library call to perform the comparison if no insn
+ is available to handle it.
+ The values which are passed in through pointers can be modified; the caller
+ should perform the comparison on the modified values. */
-void
-emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
- rtx x, y;
- enum rtx_code comparison;
+static void
+prepare_cmp_insn (px, py, pcomparison, size, pmode, punsignedp, align)
+ rtx *px, *py;
+ enum rtx_code *pcomparison;
rtx size;
- enum machine_mode mode;
- int unsignedp;
+ enum machine_mode *pmode;
+ int *punsignedp;
int align;
{
+ enum rtx_code comparison = *pcomparison;
+ enum machine_mode mode = *pmode;
+ rtx x = *px, y = *py;
+ int unsignedp = *punsignedp;
enum mode_class class;
- enum machine_mode wider_mode;
class = GET_MODE_CLASS (mode);
if (mode == BLKmode)
{
+ rtx result;
+ enum machine_mode result_mode;
+
emit_queue ();
x = protect_from_queue (x, 0);
y = protect_from_queue (y, 0);
&& GET_CODE (size) == CONST_INT
&& INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode)))
{
- enum machine_mode result_mode
- = insn_operand_mode[(int) CODE_FOR_cmpstrqi][0];
- rtx result = gen_reg_rtx (result_mode);
+ result_mode = insn_operand_mode[(int) CODE_FOR_cmpstrqi][0];
+ result = gen_reg_rtx (result_mode);
emit_insn (gen_cmpstrqi (result, x, y, size, GEN_INT (align)));
- emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX,
- result_mode, 0, 0);
}
else
#endif
&& GET_CODE (size) == CONST_INT
&& INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode)))
{
- enum machine_mode result_mode
- = insn_operand_mode[(int) CODE_FOR_cmpstrhi][0];
- rtx result = gen_reg_rtx (result_mode);
+ result_mode = insn_operand_mode[(int) CODE_FOR_cmpstrhi][0];
+ result = gen_reg_rtx (result_mode);
emit_insn (gen_cmpstrhi (result, x, y, size, GEN_INT (align)));
- emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX,
- result_mode, 0, 0);
}
else
#endif
#ifdef HAVE_cmpstrsi
if (HAVE_cmpstrsi)
{
- enum machine_mode result_mode
- = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0];
- rtx result = gen_reg_rtx (result_mode);
+ result_mode = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0];
+ result = gen_reg_rtx (result_mode);
size = protect_from_queue (size, 0);
emit_insn (gen_cmpstrsi (result, x, y,
convert_to_mode (SImode, size, 1),
GEN_INT (align)));
- emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX,
- result_mode, 0, 0);
}
else
#endif
{
- rtx result;
-
#ifdef TARGET_MEM_FUNCTIONS
emit_library_call (memcmp_libfunc, 0,
TYPE_MODE (integer_type_node), 3,
register so reload doesn't clobber the value if it needs
the return register for a spill reg. */
result = gen_reg_rtx (TYPE_MODE (integer_type_node));
+ result_mode = TYPE_MODE (integer_type_node);
emit_move_insn (result,
- hard_libcall_value (TYPE_MODE (integer_type_node)));
- emit_cmp_insn (result,
- const0_rtx, comparison, NULL_RTX,
- TYPE_MODE (integer_type_node), 0, 0);
+ hard_libcall_value (result_mode));
}
+ *px = result;
+ *py = const0_rtx;
+ *pmode = result_mode;
return;
}
- /* Handle some compares against zero. */
-
- if (y == CONST0_RTX (mode)
- && tst_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
- {
- int icode = (int) tst_optab->handlers[(int) mode].insn_code;
-
- emit_queue ();
- x = protect_from_queue (x, 0);
- y = protect_from_queue (y, 0);
-
- /* Now, if insn does accept these operands, put them into pseudos. */
- if (! (*insn_operand_predicate[icode][0])
- (x, insn_operand_mode[icode][0]))
- x = copy_to_mode_reg (insn_operand_mode[icode][0], x);
-
- emit_insn (GEN_FCN (icode) (x));
- return;
- }
-
- /* Handle compares for which there is a directly suitable insn. */
-
- if (cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
- {
- int icode = (int) cmp_optab->handlers[(int) mode].insn_code;
-
- emit_queue ();
- x = protect_from_queue (x, 0);
- y = protect_from_queue (y, 0);
-
- /* Now, if insn doesn't accept these operands, put them into pseudos. */
- if (! (*insn_operand_predicate[icode][0])
- (x, insn_operand_mode[icode][0]))
- x = copy_to_mode_reg (insn_operand_mode[icode][0], x);
-
- if (! (*insn_operand_predicate[icode][1])
- (y, insn_operand_mode[icode][1]))
- y = copy_to_mode_reg (insn_operand_mode[icode][1], y);
-
- emit_insn (GEN_FCN (icode) (x, y));
- return;
- }
-
- /* Try widening if we can find a direct insn that way. */
-
- if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
- {
- for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
- wider_mode = GET_MODE_WIDER_MODE (wider_mode))
- {
- if (cmp_optab->handlers[(int) wider_mode].insn_code
- != CODE_FOR_nothing)
- {
- x = protect_from_queue (x, 0);
- y = protect_from_queue (y, 0);
- x = convert_modes (wider_mode, mode, x, unsignedp);
- y = convert_modes (wider_mode, mode, y, unsignedp);
- emit_cmp_insn (x, y, comparison, NULL_RTX,
- wider_mode, unsignedp, align);
- return;
- }
- }
- }
+ *px = x;
+ *py = y;
+ if (cmp_available_p (mode, comparison, y == CONST0_RTX (mode)))
+ return;
/* Handle a lib call just for the mode we are using. */
- if (cmp_optab->handlers[(int) mode].libfunc
- && class != MODE_FLOAT)
+ if (cmp_optab->handlers[(int) mode].libfunc && class != MODE_FLOAT)
{
rtx libfunc = cmp_optab->handlers[(int) mode].libfunc;
rtx result;
/* Integer comparison returns a result that must be compared against 1,
so that even if we do an unsigned compare afterward,
there is still a value that can represent the result "less than". */
- emit_cmp_insn (result, const1_rtx,
- comparison, NULL_RTX, word_mode, unsignedp, 0);
+ *px = result;
+ *py = const1_rtx;
+ *pmode = word_mode;
return;
}
if (class == MODE_FLOAT)
- emit_float_lib_cmp (x, y, comparison);
+ prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
else
abort ();
}
+/* Before emitting an insn with code ICODE, make sure that X, which is going
+ to be used for operand OPNUM of the insn, is converted from mode MODE to
+ WIDER_MODE (UNSIGNEDP determines whether it is a unsigned conversion), and
+ that it is accepted by the operand predicate. Return the new value. */
+static rtx
+prepare_operand (icode, x, opnum, mode, wider_mode, unsignedp)
+ int icode;
+ rtx x;
+ int opnum;
+ enum machine_mode mode, wider_mode;
+ int unsignedp;
+{
+ x = protect_from_queue (x, 0);
+
+ if (mode != wider_mode)
+ x = convert_modes (wider_mode, mode, x, unsignedp);
+
+ if (! (*insn_operand_predicate[icode][opnum])
+ (x, insn_operand_mode[icode][opnum]))
+ x = copy_to_mode_reg (insn_operand_mode[icode][opnum], x);
+ return x;
+}
+
+/* Subroutine of emit_cmp_and_jump_insns; this function is called when we know
+ we can do the comparison.
+ The arguments are the same as for emit_cmp_and_jump_insns; but LABEL may
+ be NULL_RTX which indicates that only a comparison is to be generated. */
+
+static void
+emit_cmp_and_jump_insn_1 (x, y, mode, comparison, unsignedp, label)
+ rtx x, y;
+ enum machine_mode mode;
+ enum rtx_code comparison;
+ int unsignedp;
+ rtx label;
+{
+ rtx test = gen_rtx_fmt_ee (comparison, mode, x, y);
+ enum mode_class class = GET_MODE_CLASS (mode);
+ enum machine_mode wider_mode = mode;
+
+ /* Try combined insns first. */
+ do
+ {
+ enum insn_code icode;
+ PUT_MODE (test, wider_mode);
+
+ /* Handle some compares against zero. */
+ icode = (int) tst_optab->handlers[(int) wider_mode].insn_code;
+ if (y == CONST0_RTX (mode) && icode != CODE_FOR_nothing)
+ {
+ x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp);
+ emit_insn (GEN_FCN (icode) (x));
+ if (label)
+ emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
+ return;
+ }
+
+ /* Handle compares for which there is a directly suitable insn. */
+
+ icode = (int) cmp_optab->handlers[(int) wider_mode].insn_code;
+ if (icode != CODE_FOR_nothing)
+ {
+ x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp);
+ y = prepare_operand (icode, y, 1, mode, wider_mode, unsignedp);
+ emit_insn (GEN_FCN (icode) (x, y));
+ if (label)
+ emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
+ return;
+ }
+
+ if (class != MODE_INT && class != MODE_FLOAT
+ && class != MODE_COMPLEX_FLOAT)
+ break;
+
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode);
+ } while (wider_mode != VOIDmode);
+
+ abort ();
+}
+
/* Generate code to compare X with Y so that the condition codes are
set and to jump to LABEL if the condition is true. If X is a
constant and Y is not a constant, then the comparison is swapped to
ensure that the comparison RTL has the canonical form.
- MODE is the mode of the inputs (in case they are const_int).
- UNSIGNEDP nonzero says that X and Y are unsigned;
- this matters if they need to be widened.
+ UNSIGNEDP nonzero says that X and Y are unsigned; this matters if they
+ need to be widened by emit_cmp_insn. UNSIGNEDP is also used to select
+ the proper branch condition code.
- If they have mode BLKmode, then SIZE specifies the size of both X and Y,
- and ALIGN specifies the known shared alignment of X and Y.
+ If X and Y have mode BLKmode, then SIZE specifies the size of both X and Y,
+ and ALIGN specifies the known shared alignment of X and Y.
- COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).
- It is ignored for fixed-point and block comparisons;
- it is used only for floating-point comparisons. */
+ MODE is the mode of the inputs (in case they are const_int).
+
+ COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). It will
+ be passed unchanged to emit_cmp_insn, then potentially converted into an
+ unsigned variant based on UNSIGNEDP to select a proper jump instruction. */
void
emit_cmp_and_jump_insns (x, y, comparison, size, mode, unsignedp, align, label)
rtx op0;
rtx op1;
- if (CONSTANT_P (x))
+ if ((CONSTANT_P (x) && ! CONSTANT_P (y))
+ || (GET_CODE (x) == CONST_INT && GET_CODE (y) != CONST_INT))
{
/* Swap operands and condition to ensure canonical RTL. */
op0 = y;
op0 = x;
op1 = y;
}
- emit_cmp_insn (op0, op1, comparison, size, mode, unsignedp, align);
- emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label));
+
+#ifdef HAVE_cc0
+ /* If OP0 is still a constant, then both X and Y must be constants. Force
+ X into a register to avoid aborting in emit_cmp_insn due to non-canonical
+ RTL. */
+ if (CONSTANT_P (op0))
+ op0 = force_reg (mode, op0);
+#endif
+
+ emit_queue ();
+ if (unsignedp)
+ comparison = unsigned_condition (comparison);
+ prepare_cmp_insn (&op0, &op1, &comparison, size, &mode, &unsignedp, align);
+ emit_cmp_and_jump_insn_1 (op0, op1, mode, comparison, unsignedp, label);
+}
+
+/* Like emit_cmp_and_jump_insns, but generate only the comparison. */
+void
+emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
+ rtx x, y;
+ enum rtx_code comparison;
+ rtx size;
+ enum machine_mode mode;
+ int unsignedp;
+ int align;
+{
+ emit_cmp_and_jump_insns (x, y, comparison, size, mode, unsignedp, align, 0);
}
/* Emit a library call comparison between floating point X and Y.
COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). */
-void
-emit_float_lib_cmp (x, y, comparison)
- rtx x, y;
- enum rtx_code comparison;
+static void
+prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp)
+ rtx *px, *py;
+ enum rtx_code *pcomparison;
+ enum machine_mode *pmode;
+ int *punsignedp;
{
+ enum rtx_code comparison = *pcomparison;
+ rtx x = *px, y = *py;
enum machine_mode mode = GET_MODE (x);
rtx libfunc = 0;
rtx result;
{
x = protect_from_queue (x, 0);
y = protect_from_queue (y, 0);
- x = convert_to_mode (wider_mode, x, 0);
- y = convert_to_mode (wider_mode, y, 0);
- emit_float_lib_cmp (x, y, comparison);
+ *px = convert_to_mode (wider_mode, x, 0);
+ *py = convert_to_mode (wider_mode, y, 0);
+ prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
return;
}
}
the return register for a spill reg. */
result = gen_reg_rtx (word_mode);
emit_move_insn (result, hard_libcall_value (word_mode));
-
- emit_cmp_insn (result, const0_rtx, comparison,
- NULL_RTX, word_mode, 0, 0);
+ *px = result;
+ *py = const0_rtx;
+ *pmode = word_mode;
+#ifdef FLOAT_LIB_COMPARE_RETURNS_BOOL
+ if (FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
+ *pcomparison = NE;
+#endif
+ *punsignedp = 0;
}
\f
/* Generate code to indirectly jump to a location given in the rtx LOC. */
{
x = gen_rtx_MEM (tmode, XEXP (x1, 0));
RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (x1);
- MEM_IN_STRUCT_P (x) = MEM_IN_STRUCT_P (x1);
- MEM_VOLATILE_P (x) = MEM_VOLATILE_P (x1);
+ MEM_COPY_ATTRIBUTES (x, x1);
copy_replacements (x1, x);
}
{
y = gen_rtx_MEM (tmode, XEXP (y1, 0));
RTX_UNCHANGING_P (y) = RTX_UNCHANGING_P (y1);
- MEM_IN_STRUCT_P (y) = MEM_IN_STRUCT_P (y1);
- MEM_VOLATILE_P (y) = MEM_VOLATILE_P (y1);
+ MEM_COPY_ATTRIBUTES (y, y1);
copy_replacements (y1, y);
}
}
correct its value by 2**bitwidth. */
do_pending_stack_adjust ();
- emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, GET_MODE (from), 0, 0);
- emit_jump_insn (gen_bge (label));
+ emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, GET_MODE (from),
+ 0, 0, label);
/* On SCO 3.2.1, ldexp rejects values outside [0.5, 1).
Rather than setting up a dconst_dot_5, let's hope SCO
/* See if we need to do the subtraction. */
do_pending_stack_adjust ();
- emit_cmp_insn (from, limit, GE, NULL_RTX, GET_MODE (from), 0, 0);
- emit_jump_insn (gen_bge (lab1));
+ emit_cmp_and_jump_insns (from, limit, GE, NULL_RTX, GET_MODE (from),
+ 0, 0, lab1);
/* If not, do the signed "fix" and branch around fixup code. */
expand_fix (to, from, 0);
{
/* Make a place for a REG_NOTE and add it. */
insn = emit_move_insn (to, to);
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_EQUAL,
- gen_rtx_fmt_e (UNSIGNED_FIX,
- GET_MODE (to),
- copy_rtx (from)),
- REG_NOTES (insn));
+ set_unique_reg_note (insn,
+ REG_EQUAL,
+ gen_rtx_fmt_e (UNSIGNED_FIX,
+ GET_MODE (to),
+ copy_rtx (from)));
}
return;
}
register optab optable;
register int first_mode;
register int last_mode;
- register char *opname;
+ register const char *opname;
register int suffix;
{
register int mode;
for (mode = first_mode; (int) mode <= (int) last_mode;
mode = (enum machine_mode) ((int) mode + 1))
{
- register char *mname = mode_name[(int) mode];
+ register const char *mname = GET_MODE_NAME(mode);
register unsigned mname_len = strlen (mname);
register char *libfunc_name
= (char *) xmalloc (2 + opname_len + mname_len + 1 + 1);
register char *p;
- register char *q;
+ register const char *q;
p = libfunc_name;
*p++ = '_';
static void
init_integral_libfuncs (optable, opname, suffix)
register optab optable;
- register char *opname;
+ register const char *opname;
register int suffix;
{
init_libfuncs (optable, SImode, TImode, opname, suffix);
static void
init_floating_libfuncs (optable, opname, suffix)
register optab optable;
- register char *opname;
+ register const char *opname;
register int suffix;
{
init_libfuncs (optable, SFmode, TFmode, opname, suffix);
fixtrunctab[i][j][1] = fixtrunctab[i][j][0];
#endif
-#ifdef EXTRA_CC_MODES
- init_mov_optab ();
-#endif
-
/* Initialize the optabs with the names of the library functions. */
init_integral_libfuncs (add_optab, "add", '3');
init_floating_libfuncs (add_optab, "add", '3');