diff options
author | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-01-28 00:55:07 +0000 |
---|---|---|
committer | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-01-28 00:55:07 +0000 |
commit | 270436f389c1c862f9e0a2f3b3b928f15d8b380d (patch) | |
tree | 3611918a36fd9acca7d65920e977e8e47fd831c5 /gcc/optabs.c | |
parent | 89ea40075b3410afaa6100e82dc31632f0fbd6e1 (diff) | |
download | gcc-270436f389c1c862f9e0a2f3b3b928f15d8b380d.tar.gz |
* builtins.c (expand_builtin_copysign): New.
(expand_builtin): Call it.
* genopinit.c (optabs): Add copysign_optab.
* optabs.c (init_optabs): Initialize it.
(expand_copysign): New.
* optabs.h (OTI_copysign, copysign_optab): New.
(expand_copysign): Declare.
* config/alpha/alpha.md (UNSPEC_COPYSIGN): New.
(copysignsf3, ncopysignsf3, copysigndf3, ncopysigndf3): New.
* config/i386/i386.c (ix86_build_signbit_mask): Split from ...
(ix86_expand_fp_absneg_operator): ... here.
(ix86_split_copysign): New.
* config/i386/i386-protos.h: Update.
* config/i386/i386.md (UNSPEC_COPYSIGN): New.
(copysignsf3, copysigndf3): New.
* config/ia64/ia64.md (UNSPEC_COPYSIGN): New.
(copysignsf3, ncopysignsf3): New.
(copysigndf3, ncopysigndf3): New.
(copysignxf3, ncopysignxf3): New.
* config/ia64/ia64.c (rtx_needs_barrier): Handle UNSPEC_COPYSIGN.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@94357 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/optabs.c')
-rw-r--r-- | gcc/optabs.c | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/gcc/optabs.c b/gcc/optabs.c index e18b42bf75f..d92cc907868 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -2631,6 +2631,90 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target, OK_DEFER_POP; return target; } + +/* Expand the C99 copysign operation. OP0 and OP1 must be the same + scalar floating point mode. Return NULL if we do not know how to + expand the operation inline. */ + +rtx +expand_copysign (rtx op0, rtx op1, rtx target) +{ + enum machine_mode mode = GET_MODE (op0); + const struct real_format *fmt; + enum machine_mode imode; + int bitpos; + HOST_WIDE_INT hi, lo; + rtx last, temp; + + gcc_assert (SCALAR_FLOAT_MODE_P (mode)); + gcc_assert (GET_MODE (op1) == mode); + + /* First try to do it with a special instruction. */ + temp = expand_binop (mode, copysign_optab, op0, op1, + target, 0, OPTAB_DIRECT); + if (temp) + return temp; + + /* Otherwise, use bit operations to move the sign from one to the other. */ + if (GET_MODE_BITSIZE (mode) > 2 * HOST_BITS_PER_WIDE_INT) + return NULL_RTX; + + imode = int_mode_for_mode (mode); + if (imode == BLKmode) + return NULL_RTX; + + fmt = REAL_MODE_FORMAT (mode); + bitpos = (fmt != 0) ? fmt->signbit : -1; + if (bitpos < 0) + return NULL_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; + lo = (HOST_WIDE_INT) 1 << bitpos; + } + else + { + hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT); + lo = 0; + } + + op0 = expand_binop (imode, and_optab, + gen_lowpart (imode, op0), + immed_double_const (~lo, ~hi, imode), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + op1 = expand_binop (imode, and_optab, + gen_lowpart (imode, op1), + immed_double_const (lo, hi, imode), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + if (op0 && op1) + { + target = expand_binop (imode, ior_optab, op0, op1, NULL, + 1, OPTAB_LIB_WIDEN); + if (target) + { + target = force_reg (imode, target); + target = gen_lowpart (mode, target); + } + } + else + target = NULL_RTX; + + if (!target) + delete_insns_since (last); + + return target; +} /* Generate an instruction whose insn-code is INSN_CODE, with two operands: an output TARGET and an input OP0. @@ -4776,6 +4860,8 @@ init_optabs (void) log1p_optab = init_optab (UNKNOWN); tan_optab = init_optab (UNKNOWN); atan_optab = init_optab (UNKNOWN); + copysign_optab = init_optab (UNKNOWN); + strlen_optab = init_optab (UNKNOWN); cbranch_optab = init_optab (UNKNOWN); cmov_optab = init_optab (UNKNOWN); |