+/* Expand a floating point absolute value or negation operation via a
+ logical operation on the sign bit. */
+
+static rtx
+expand_absneg_bit (enum rtx_code code, enum machine_mode mode,
+ rtx op0, rtx target)
+{
+ const struct real_format *fmt;
+ int bitpos, word, nwords, i;
+ enum machine_mode imode;
+ HOST_WIDE_INT hi, lo;
+ rtx temp, insns;
+
+ /* The format has to have a simple sign bit. */
+ fmt = REAL_MODE_FORMAT (mode);
+ if (fmt == NULL)
+ return NULL_RTX;
+
+ bitpos = fmt->signbit;
+ if (bitpos < 0)
+ return NULL_RTX;
+
+ /* Don't create negative zeros if the format doesn't support them. */
+ if (code == NEG && !fmt->has_signed_zero)
+ return NULL_RTX;
+
+ if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
+ {
+ imode = int_mode_for_mode (mode);
+ if (imode == BLKmode)
+ return NULL_RTX;
+ word = 0;
+ nwords = 1;
+ }
+ else
+ {
+ imode = word_mode;
+
+ if (FLOAT_WORDS_BIG_ENDIAN)
+ word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD;
+ else
+ word = bitpos / BITS_PER_WORD;
+ bitpos = bitpos % BITS_PER_WORD;
+ nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / 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;
+ }
+ if (code == ABS)
+ lo = ~lo, hi = ~hi;
+
+ if (target == 0 || target == op0)
+ target = gen_reg_rtx (mode);
+
+ if (nwords > 1)
+ {
+ start_sequence ();
+
+ for (i = 0; i < nwords; ++i)
+ {
+ rtx targ_piece = operand_subword (target, i, 1, mode);
+ rtx op0_piece = operand_subword_force (op0, i, mode);
+
+ if (i == word)
+ {
+ temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
+ op0_piece,
+ immed_double_const (lo, hi, imode),
+ targ_piece, 1, OPTAB_LIB_WIDEN);
+ if (temp != targ_piece)
+ emit_move_insn (targ_piece, temp);
+ }
+ else
+ emit_move_insn (targ_piece, op0_piece);
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+
+ temp = gen_rtx_fmt_e (code, mode, copy_rtx (op0));
+ emit_no_conflict_block (insns, target, op0, NULL_RTX, temp);
+ }
+ else
+ {
+ temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
+ gen_lowpart (imode, op0),
+ immed_double_const (lo, hi, imode),
+ gen_lowpart (imode, target), 1, OPTAB_LIB_WIDEN);
+ target = lowpart_subreg_maybe_copy (mode, temp, imode);
+
+ set_unique_reg_note (get_last_insn (), REG_EQUAL,
+ gen_rtx_fmt_e (code, mode, copy_rtx (op0)));
+ }
+
+ return target;
+}
+