/* Expand the basic unary and binary arithmetic operations, for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "reload.h"
#include "ggc.h"
#include "real.h"
+#include "basic-block.h"
/* Each optab contains info on how this target machine
can perform a particular operation
enum optab_methods));
static rtx expand_vector_unop PARAMS ((enum machine_mode, optab, rtx, rtx,
int));
+static rtx widen_clz PARAMS ((enum machine_mode, rtx, rtx));
+static rtx expand_parity PARAMS ((enum machine_mode, rtx, rtx));
\f
/* Add a REG_EQUAL note to the last insn in INSNS. TARGET is being set to
the result of operation CODE applied to OP0 (and OP1 if it is a binary
copy_rtx (xop0),
copy_rtx (xop1)));
}
+ else
+ target = xtarget;
return target;
}
return expand_unop (mode, unop, op0, target, unsignedp);
}
+/* Try calculating
+ (clz:narrow x)
+ as
+ (clz:wide (zero_extend:wide x)) - ((width wide) - (width narrow)). */
+static rtx
+widen_clz (mode, op0, target)
+ enum machine_mode mode;
+ rtx op0;
+ rtx target;
+{
+ enum mode_class class = GET_MODE_CLASS (mode);
+ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ {
+ enum machine_mode wider_mode;
+ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if (clz_optab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ {
+ rtx xop0, temp, last;
+
+ last = get_last_insn ();
+
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ xop0 = widen_operand (op0, wider_mode, mode, true, false);
+ temp = expand_unop (wider_mode, clz_optab, xop0, NULL_RTX, true);
+ if (temp != 0)
+ temp = expand_binop (wider_mode, sub_optab, temp,
+ GEN_INT (GET_MODE_BITSIZE (wider_mode)
+ - GET_MODE_BITSIZE (mode)),
+ target, true, OPTAB_DIRECT);
+ if (temp == 0)
+ delete_insns_since (last);
+
+ return temp;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Try calculating (parity x) as (and (popcount x) 1), where
+ popcount can also be done in a wider mode. */
+static rtx
+expand_parity (mode, op0, target)
+ enum machine_mode mode;
+ rtx op0;
+ rtx target;
+{
+ enum mode_class class = GET_MODE_CLASS (mode);
+ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ {
+ enum machine_mode wider_mode;
+ for (wider_mode = mode; wider_mode != VOIDmode;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if (popcount_optab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ {
+ rtx xop0, temp, last;
+
+ last = get_last_insn ();
+
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ xop0 = widen_operand (op0, wider_mode, mode, true, false);
+ temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX,
+ true);
+ if (temp != 0)
+ temp = expand_binop (wider_mode, and_optab, temp, GEN_INT (1),
+ target, true, OPTAB_DIRECT);
+ if (temp == 0)
+ delete_insns_since (last);
+
+ return temp;
+ }
+ }
+ }
+ return 0;
+}
+
/* Generate code to perform an operation specified by UNOPTAB
on operand OP0, with result having machine-mode MODE.
/* It can't be done in this mode. Can we open-code it in a wider mode? */
+ /* Widening clz needs special treatment. */
+ if (unoptab == clz_optab)
+ {
+ temp = widen_clz (mode, op0, target);
+ if (temp)
+ return temp;
+ else
+ goto try_libcall;
+ }
+
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))
HOST_WIDE_INT hi, lo;
rtx last = get_last_insn ();
+ /* Handle targets with different FP word orders. */
+ if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+ {
+ int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
+ int word = nwords - (bitpos / BITS_PER_WORD) - 1;
+ bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
+ }
+
if (bitpos < HOST_BITS_PER_WIDE_INT)
{
hi = 0;
}
}
+ /* Try calculating parity (x) as popcount (x) % 2. */
+ if (unoptab == parity_optab)
+ {
+ temp = expand_parity (mode, op0, target);
+ if (temp)
+ return temp;
+ }
+
+ try_libcall:
/* Now try a library call in this mode. */
if (unoptab->handlers[(int) mode].libfunc)
{
rtx insns;
rtx value;
+ enum machine_mode outmode = mode;
+
+ /* All of these functions return small values. Thus we choose to
+ have them return something that isn't a double-word. */
+ if (unoptab == ffs_optab || unoptab == clz_optab || unoptab == ctz_optab
+ || unoptab == popcount_optab || unoptab == parity_optab)
+ outmode = TYPE_MODE (integer_type_node);
start_sequence ();
/* Pass 1 for NO_QUEUE so we don't lose any increments
if the libcall is cse'd or moved. */
value = emit_library_call_value (unoptab->handlers[(int) mode].libfunc,
- NULL_RTX, LCT_CONST, mode, 1, op0, mode);
+ NULL_RTX, LCT_CONST, outmode,
+ 1, op0, mode);
insns = get_insns ();
end_sequence ();
- target = gen_reg_rtx (mode);
+ target = gen_reg_rtx (outmode);
emit_libcall_block (insns, target, value,
gen_rtx_fmt_e (unoptab->code, mode, op0));
temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
unsignedp);
+ /* If we are generating clz using wider mode, adjust the
+ result. */
+ if (unoptab == clz_optab && temp != 0)
+ temp = expand_binop (wider_mode, sub_optab, temp,
+ GEN_INT (GET_MODE_BITSIZE (wider_mode)
+ - GET_MODE_BITSIZE (mode)),
+ target, true, OPTAB_DIRECT);
+
if (temp)
{
if (class != MODE_INT)
HOST_WIDE_INT hi, lo;
rtx last = get_last_insn ();
+ /* Handle targets with different FP word orders. */
+ if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+ {
+ int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
+ int word = nwords - (bitpos / BITS_PER_WORD) - 1;
+ bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
+ }
+
if (bitpos < HOST_BITS_PER_WIDE_INT)
{
hi = 0;
/* Encapsulate the block so it gets manipulated as a unit. */
if (!flag_non_call_exceptions || !may_trap_p (equiv))
{
- REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last,
- REG_NOTES (first));
- REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first,
- REG_NOTES (last));
+ /* We can't attach the REG_LIBCALL and REG_RETVAL notes
+ when the encapsulated region would not be in one basic block,
+ i.e. when there is a control_flow_insn_p insn between FIRST and LAST.
+ */
+ bool attach_libcall_retval_notes = true;
+ next = NEXT_INSN (last);
+ for (insn = first; insn != next; insn = NEXT_INSN (insn))
+ if (control_flow_insn_p (insn))
+ {
+ attach_libcall_retval_notes = false;
+ break;
+ }
+
+ if (attach_libcall_retval_notes)
+ {
+ REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last,
+ REG_NOTES (first));
+ REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first,
+ REG_NOTES (last));
+ }
}
}
\f
wider mode. If the integer mode is wider than the mode of FROM,
we can do the conversion signed even if the input is unsigned. */
- for (imode = GET_MODE (from); imode != VOIDmode;
- imode = GET_MODE_WIDER_MODE (imode))
- for (fmode = GET_MODE (to); fmode != VOIDmode;
- fmode = GET_MODE_WIDER_MODE (fmode))
+ for (fmode = GET_MODE (to); fmode != VOIDmode;
+ fmode = GET_MODE_WIDER_MODE (fmode))
+ for (imode = GET_MODE (from); imode != VOIDmode;
+ imode = GET_MODE_WIDER_MODE (imode))
{
int doing_unsigned = unsignedp;
const char *opname;
int suffix;
{
- init_libfuncs (optable, SImode, TImode, opname, suffix);
+ init_libfuncs (optable, word_mode,
+ mode_for_size (2*BITS_PER_WORD, MODE_INT, 0),
+ opname, suffix);
}
/* Initialize the libfunc fields of an entire group of entries in some
const char *opname;
int suffix;
{
- init_libfuncs (optable, SFmode, TFmode, opname, suffix);
+ enum machine_mode fmode, dmode, lmode;
+
+ fmode = float_type_node ? TYPE_MODE (float_type_node) : VOIDmode;
+ dmode = double_type_node ? TYPE_MODE (double_type_node) : VOIDmode;
+ lmode = long_double_type_node ? TYPE_MODE (long_double_type_node) : VOIDmode;
+
+ if (fmode != VOIDmode)
+ init_libfuncs (optable, fmode, fmode, opname, suffix);
+ if (dmode != fmode && dmode != VOIDmode)
+ init_libfuncs (optable, dmode, dmode, opname, suffix);
+ if (lmode != dmode && lmode != VOIDmode)
+ init_libfuncs (optable, lmode, lmode, opname, suffix);
}
rtx
smax_optab = init_optab (SMAX);
umin_optab = init_optab (UMIN);
umax_optab = init_optab (UMAX);
+ pow_optab = init_optab (UNKNOWN);
+ atan2_optab = init_optab (UNKNOWN);
/* These three have codes assigned exclusively for the sake of
have_insn_for. */
addcc_optab = init_optab (UNKNOWN);
one_cmpl_optab = init_optab (NOT);
ffs_optab = init_optab (FFS);
+ clz_optab = init_optab (CLZ);
+ ctz_optab = init_optab (CTZ);
+ popcount_optab = init_optab (POPCOUNT);
+ parity_optab = init_optab (PARITY);
sqrt_optab = init_optab (SQRT);
floor_optab = init_optab (UNKNOWN);
ceil_optab = init_optab (UNKNOWN);
init_floating_libfuncs (negv_optab, "neg", '2');
init_integral_libfuncs (one_cmpl_optab, "one_cmpl", '2');
init_integral_libfuncs (ffs_optab, "ffs", '2');
+ init_integral_libfuncs (clz_optab, "clz", '2');
+ init_integral_libfuncs (ctz_optab, "ctz", '2');
+ init_integral_libfuncs (popcount_optab, "popcount", '2');
+ init_integral_libfuncs (parity_optab, "parity", '2');
/* Comparison libcalls for integers MUST come in pairs, signed/unsigned. */
init_integral_libfuncs (cmp_optab, "cmp", '2');