aboutsummaryrefslogtreecommitdiff
path: root/gcc/optabs.c
diff options
context:
space:
mode:
authorRichard Kenner <kenner@gcc.gnu.org>1994-05-30 10:33:41 -0400
committerRichard Kenner <kenner@gcc.gnu.org>1994-05-30 10:33:41 -0400
commita48fb61b5bbb8ae2f821875a07d0ac8ceb334e02 (patch)
treeb790afda3ca6646caed60b65fd68a88b86616a76 /gcc/optabs.c
parent344a822046c08d2cb6a80eada0dbcbcf77cb6a44 (diff)
downloadgcc-a48fb61b5bbb8ae2f821875a07d0ac8ceb334e02.zip
gcc-a48fb61b5bbb8ae2f821875a07d0ac8ceb334e02.tar.gz
gcc-a48fb61b5bbb8ae2f821875a07d0ac8ceb334e02.tar.bz2
(expand_float): Avoid double-rounding when float is is narrower than
int. From-SVN: r7386
Diffstat (limited to 'gcc/optabs.c')
-rw-r--r--gcc/optabs.c47
1 files changed, 45 insertions, 2 deletions
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 7b263bc..b3b6ea9 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -3070,11 +3070,51 @@ expand_float (to, from, unsignedp)
if (GET_MODE_BITSIZE (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
&& can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing)
break;
+
if (fmode == VOIDmode)
{
- /* There is no such mode. Pretend the target is wide enough.
- This may cause rounding problems, unfortunately. */
+ /* There is no such mode. Pretend the target is wide enough. */
fmode = GET_MODE (to);
+
+ /* Avoid double-rounding when TO is narrower than FROM. */
+ if ((significand_size (fmode) + 1)
+ < GET_MODE_BITSIZE (GET_MODE (from)))
+ {
+ rtx temp1;
+ rtx neglabel = gen_label_rtx ();
+
+ imode = GET_MODE (from);
+ do_pending_stack_adjust ();
+
+ /* Test whether the sign bit is set. */
+ emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, imode, 0, 0);
+ emit_jump_insn (gen_blt (neglabel));
+
+ /* The sign bit is not set. Convert as signed. */
+ expand_float (target, from, 0);
+ emit_jump_insn (gen_jump (label));
+
+ /* The sign bit is set.
+ Convert to a usable (positive signed) value by shifting right
+ one bit, while remembering if a nonzero bit was shifted
+ out; i.e., compute (from & 1) | (from >> 1). */
+
+ emit_label (neglabel);
+ temp = expand_binop (imode, and_optab, from, const1_rtx,
+ 0, 1, 0);
+ temp1 = expand_binop (imode, lshr_optab, from, const1_rtx,
+ from, 1, 0);
+ temp = expand_binop (imode, ior_optab, temp, temp1,
+ temp, 1, 0);
+ expand_float (target, temp, 0);
+
+ /* Multiply by 2 to undo the shift above. */
+ target = expand_binop (fmode, add_optab, target, target,
+ target, 0, 0);
+ do_pending_stack_adjust ();
+ emit_label (label);
+ goto done;
+ }
}
/* If we are about to do some arithmetic to correct for an
@@ -3102,6 +3142,7 @@ expand_float (to, from, unsignedp)
target, 0, OPTAB_LIB_WIDEN);
if (temp != target)
emit_move_insn (target, temp);
+
do_pending_stack_adjust ();
emit_label (label);
}
@@ -3183,6 +3224,8 @@ expand_float (to, from, unsignedp)
gen_rtx (FLOAT, GET_MODE (to), from));
}
+ done:
+
/* Copy result to requested destination
if we have been computing in a temp location. */