FPU: Be more careful about preserving low-order bits in multiply-add instrs

Add code to check whether bits of S which don't get shifted into R are
non-zero, and set X if they are, so that rounding in multiply-add
instructions works correctly.  This needs to be done after normalization
in the case of very small results, where potentially all the non-zero
bits in S do get shifted into R.

Also fix an incorrect test case, and add another multiply-add test case.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
pull/457/head
Paul Mackerras 1 month ago
parent 59992eab90
commit b122577a4e

@ -1003,6 +1003,7 @@ begin
variable exp_huge : std_ulogic; variable exp_huge : std_ulogic;
variable clz : std_ulogic_vector(5 downto 0); variable clz : std_ulogic_vector(5 downto 0);
variable set_x : std_ulogic; variable set_x : std_ulogic;
variable set_xs : std_ulogic;
variable mshift : signed(EXP_BITS-1 downto 0); variable mshift : signed(EXP_BITS-1 downto 0);
variable need_check : std_ulogic; variable need_check : std_ulogic;
variable msb : std_ulogic; variable msb : std_ulogic;
@ -1056,6 +1057,7 @@ begin
variable bneg : std_ulogic; variable bneg : std_ulogic;
variable ci : std_ulogic; variable ci : std_ulogic;
variable rormr : std_ulogic_vector(63 downto 0); variable rormr : std_ulogic_vector(63 downto 0);
variable sorms : std_ulogic_vector(55 downto 0);
begin begin
v := r; v := r;
v.complete := '0'; v.complete := '0';
@ -1358,6 +1360,7 @@ begin
invalid := '0'; invalid := '0';
zero_divide := '0'; zero_divide := '0';
set_x := '0'; set_x := '0';
set_xs := '0';
qnan_result := '0'; qnan_result := '0';
set_a := '0'; set_a := '0';
set_a_exp := '0'; set_a_exp := '0';
@ -1931,6 +1934,8 @@ begin
f_to_multiply.valid <= r.first; f_to_multiply.valid <= r.first;
opsel_r <= RES_MULT; opsel_r <= RES_MULT;
set_r := '1'; set_r := '1';
opsel_s <= S_MULT;
set_s := '1';
if multiply_to_f.valid = '1' then if multiply_to_f.valid = '1' then
v.state := FINISH; v.state := FINISH;
end if; end if;
@ -1971,6 +1976,7 @@ begin
when FMADD_2 => when FMADD_2 =>
-- Product is potentially bigger here -- Product is potentially bigger here
-- r.shift = addend exp - product exp + 64, r.r = r.b.mantissa -- r.shift = addend exp - product exp + 64, r.r = r.b.mantissa
-- R contains B, S contains 0
set_s := '1'; set_s := '1';
opsel_s <= S_SHIFT; opsel_s <= S_SHIFT;
set_x := '1'; set_x := '1';
@ -2408,9 +2414,6 @@ begin


when FINISH => when FINISH =>
-- r.shift = 0 -- r.shift = 0
if r.is_multiply = '1' and px_nz = '1' then
v.x := '1';
end if;
-- set shift to new_exp - min_exp (N.B. rs_norm overrides this) -- set shift to new_exp - min_exp (N.B. rs_norm overrides this)
rs_sel1 <= RSH1_NE; rs_sel1 <= RSH1_NE;
rs_con2 <= RSCON2_MINEXP; rs_con2 <= RSCON2_MINEXP;
@ -2420,6 +2423,7 @@ begin
v.state := NORMALIZE; v.state := NORMALIZE;
else else
set_x := '1'; set_x := '1';
set_xs := r.is_multiply;
if exp_tiny = '1' then if exp_tiny = '1' then
v.state := ROUND_UFLOW; v.state := ROUND_UFLOW;
elsif exp_huge = '1' and r.fpscr(FPSCR_OE) = '0' then elsif exp_huge = '1' and r.fpscr(FPSCR_OE) = '0' then
@ -2441,6 +2445,7 @@ begin
rs_con2 <= RSCON2_MINEXP; rs_con2 <= RSCON2_MINEXP;
rs_neg2 <= '1'; rs_neg2 <= '1';
set_x := '1'; set_x := '1';
set_xs := r.is_multiply;
if exp_tiny = '1' then if exp_tiny = '1' then
v.state := ROUND_UFLOW; v.state := ROUND_UFLOW;
elsif exp_huge = '1' and r.fpscr(FPSCR_OE) = '0' then elsif exp_huge = '1' and r.fpscr(FPSCR_OE) = '0' then
@ -2485,6 +2490,7 @@ begin
re_sel2 <= REXP2_NE; re_sel2 <= REXP2_NE;
re_set_result <= '1'; re_set_result <= '1';
set_x := '1'; set_x := '1';
set_xs := r.is_multiply;
v.state := ROUNDING; v.state := ROUNDING;


when ROUND_OFLOW_DIS => when ROUND_OFLOW_DIS =>
@ -3309,6 +3315,16 @@ begin
end if; end if;
v.x := v.x or rormr(to_integer(unsigned(mshift(5 downto 0)))); v.x := v.x or rormr(to_integer(unsigned(mshift(5 downto 0))));
end if; end if;
-- Test if there are non-zero bits in S which won't get shifted into R
if set_xs = '1' and not is_X(r.shift) and r.shift < to_signed(56, EXP_BITS) then
if r.shift > to_signed(0, EXP_BITS) then
mshift := to_signed(55, EXP_BITS) - r.shift;
else
mshift := to_signed(55, EXP_BITS);
end if;
sorms := r.s or std_ulogic_vector(- signed(r.s));
v.x := v.x or sorms(to_integer(unsigned(mshift(5 downto 0))));
end if;
asign := '0'; asign := '0';
case opsel_a is case opsel_a is
when AIN_A => when AIN_A =>

@ -1543,9 +1543,9 @@ struct fmavals {
/* +(1 + 2^-52) * +(1 + 2^-52) +- +1.0 -> +(2 + 2^-51), +2^-51, -(2 + 2^-51), -2^-51 */ /* +(1 + 2^-52) * +(1 + 2^-52) +- +1.0 -> +(2 + 2^-51), +2^-51, -(2 + 2^-51), -2^-51 */
{ 0x3ff0000000000001, 0x3ff0000000000001, 0x3ff0000000000000, FPS_RN_NEAR, { 0x3ff0000000000001, 0x3ff0000000000001, 0x3ff0000000000000, FPS_RN_NEAR,
0x4000000000000001, 0x3cc0000000000000, 0xc000000000000001, 0xbcc0000000000000 }, 0x4000000000000001, 0x3cc0000000000000, 0xc000000000000001, 0xbcc0000000000000 },
/* +(1 + 3*2^-52) * +(1 + 2^-51) +- +1.0 -> +(2 + 2^-50), +5 * 2^-52 + 2^-101, -, - */ /* +(1 + 3*2^-52) * +(1 + 2^-51) +- +1.0 -> +(2 + 3*2^-51), +5 * 2^-52 + 2^-101, -, - */
{ 0x3ff0000000000003, 0x3ff0000000000002, 0x3ff0000000000000, FPS_RN_NEAR, { 0x3ff0000000000003, 0x3ff0000000000002, 0x3ff0000000000000, FPS_RN_NEAR,
0x4000000000000002, 0x3cd4000000000002, 0xc000000000000002, 0xbcd4000000000002 }, 0x4000000000000003, 0x3cd4000000000002, 0xc000000000000003, 0xbcd4000000000002 },
/* +2.443e-77 * 2.828 +- 6.909e-77 -> -1.402e-93, +1.382e-76, +1.402e-93, -1.382e-76 */ /* +2.443e-77 * 2.828 +- 6.909e-77 -> -1.402e-93, +1.382e-76, +1.402e-93, -1.382e-76 */
{ 0x3006a09e667f3bcc, 0x4006a09e667f3bcd, 0xb020000000000000, FPS_RN_NEAR, { 0x3006a09e667f3bcc, 0x4006a09e667f3bcd, 0xb020000000000000, FPS_RN_NEAR,
0xaca765753908cd20, 0x3030000000000000, 0x2ca765753908cd20, 0xb030000000000000 }, 0xaca765753908cd20, 0x3030000000000000, 0x2ca765753908cd20, 0xb030000000000000 },
@ -1615,6 +1615,9 @@ struct fmavals {
/* 1 * -1 + tiny -> -1 + delta, -1, 1 - delta, 1 */ /* 1 * -1 + tiny -> -1 + delta, -1, 1 - delta, 1 */
{ 0x3ff0000000000000, 0xbff0000000000000, 0x00000000b2200102, FPS_RN_CEIL, { 0x3ff0000000000000, 0xbff0000000000000, 0x00000000b2200102, FPS_RN_CEIL,
0xbfefffffffffffff, 0xbff0000000000000, 0x3fefffffffffffff, 0x3ff0000000000000 }, 0xbfefffffffffffff, 0xbff0000000000000, 0x3fefffffffffffff, 0x3ff0000000000000 },
/* from random exec tests */
{ 0x43eff79000000000, 0x00000000000000ff, 0x0000000000000081, FPS_RN_CEIL,
0x014fd79870000001, 0x014fd79870000000, 0x814fd79870000001, 0x814fd79870000000 },
}; };


int test23(long arg) int test23(long arg)

Binary file not shown.
Loading…
Cancel
Save