NetBSD Problem Report #45310

From Manuel.Bouyer@lip6.fr  Mon Aug 29 21:53:00 2011
Return-Path: <Manuel.Bouyer@lip6.fr>
Received: from mail.netbsd.org (mail.netbsd.org [204.152.190.11])
	by www.NetBSD.org (Postfix) with ESMTP id F0EF863C0E2
	for <gnats-bugs@gnats.NetBSD.org>; Mon, 29 Aug 2011 21:52:59 +0000 (UTC)
Message-Id: <20110829215255.D27A134C41@armandeche.soc.lip6.fr>
Date: Mon, 29 Aug 2011 23:52:55 +0200 (MEST)
From: Manuel.Bouyer@lip6.fr
Reply-To: Manuel.Bouyer@lip6.fr
To: gnats-bugs@gnats.NetBSD.org
Subject: some mixed int/float computations are wrong on mips64
X-Send-Pr-Version: 3.95

>Number:         45310
>Category:       port-mips
>Synopsis:       mixed int/float computations are wrong on mips64
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    port-mips-maintainer
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Aug 29 21:55:00 +0000 2011
>Closed-Date:    Thu Sep 01 19:15:18 +0000 2011
>Last-Modified:  Fri Sep 02 17:40:02 +0000 2011
>Originator:     Manuel Bouyer
>Release:        NetBSD 5.99.55
>Organization:
>Environment:
System: NetBSD cuba.soc.lip6.fr 5.99.55 NetBSD 5.99.55 (LOONGSON) #52: Mon Aug 29 21:38:41 CEST 2011 bouyer@roll:/dsk/l1/misc/bouyer/tmp/evbmips64el/obj/dsk/l1/misc/bouyer/current/src/sys/arch/evbmips/compile/LOONGSON evbmips
Architecture: mipsel
Machine: evbmips-mips64el
>Description:
	I first noticed that ftp(1) and progress(1) reported speeds of 0b/s.
	I tracked it down to ptransfer() in ftp and finally ended up with this
	test program:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

main()
{
	float a;
	off_t b;

	a = 0.544355;
	b = 4866695;

	printf("a %f b %d\n",a, (int)b);
	b /= a;
	printf("now b %d\n", (int)b);
}
	On i386 this gives:
a 0.544355 b 4866695
now b 8940297
	on mips64el:
a 0.544355 b 4866695
now b 0

	note that b has to be off_t for this to fail; with a int
	everything works as expected.
	Below is the assembly produced by gcc -S

	.section .mdebug.abiN32
	.previous
	.gnu_attribute 4, 3
	.abicalls
	.section	.debug_abbrev,"",@progbits
$Ldebug_abbrev0:
	.section	.debug_info,"",@progbits
$Ldebug_info0:
	.section	.debug_line,"",@progbits
$Ldebug_line0:
	.text
$Ltext0:
	.rdata
	.align	3
$LC1:
	.ascii	"a %f b %d\012\000"
	.globl	__extendsfdf2
	.globl	__floatdisf
	.globl	__divsf3
	.globl	__fixsfdi
	.align	3
$LC2:
	.ascii	"now b %d\012\000"
	.text
	.align	2
	.globl	main
$LFB13 = .
	.file 1 "test.c"
	.loc 1 6 0
	.set	nomips16
	.ent	main
	.type	main, @function
main:
	.frame	$fp,48,$31		# vars= 16, regs= 4/0, args= 0, gp= 0
	.mask	0xd0010000,-8
	.fmask	0x00000000,0
	.set	noreorder
	.set	nomacro
	addiu	$sp,$sp,-48
$LCFI0:
	sd	$31,40($sp)
$LCFI1:
	sd	$fp,32($sp)
$LCFI2:
	sd	$28,24($sp)
$LCFI3:
	sd	$16,16($sp)
$LCFI4:
	move	$fp,$sp
$LCFI5:
	lui	$28,%hi(%neg(%gp_rel(main)))
	addu	$28,$28,$25
	addiu	$28,$28,%lo(%neg(%gp_rel(main)))
	.loc 1 10 0
	lw	$2,%got_page($LC0)($28)
	lw	$2,%got_ofst($LC0)($2)
	sw	$2,0($fp)
	.loc 1 11 0
	li	$2,4849664			# 0x4a0000
	ori	$2,$2,0x4287
	sd	$2,8($fp)
	.loc 1 13 0
	lw	$2,%got_page($LC1)($28)
	addiu	$16,$2,%got_ofst($LC1)
	lw	$2,%call16(__extendsfdf2)($28)
	lw	$4,0($fp)
	move	$25,$2
	jalr	$25
	nop

	move	$3,$2
	ld	$2,8($fp)
	sll	$2,$2,0
	move	$4,$16
	move	$5,$3
	move	$6,$2
	lw	$2,%call16(printf)($28)
	move	$25,$2
	jalr	$25
	nop

	.loc 1 14 0
	lw	$2,%call16(__floatdisf)($28)
	ld	$4,8($fp)
	move	$25,$2
	jalr	$25
	nop

	move	$3,$2
	lw	$2,%call16(__divsf3)($28)
	move	$4,$3
	lw	$5,0($fp)
	move	$25,$2
	jalr	$25
	nop

	move	$3,$2
	lw	$2,%call16(__fixsfdi)($28)
	move	$4,$3
	move	$25,$2
	jalr	$25
	nop

	sd	$2,8($fp)
	.loc 1 15 0
	lw	$2,%got_page($LC2)($28)
	addiu	$3,$2,%got_ofst($LC2)
	ld	$2,8($fp)
	sll	$2,$2,0
	move	$4,$3
	move	$5,$2
	lw	$2,%call16(printf)($28)
	move	$25,$2
	jalr	$25
	nop

	.loc 1 16 0
	move	$sp,$fp
	ld	$31,40($sp)
	ld	$fp,32($sp)
	ld	$28,24($sp)
	ld	$16,16($sp)
	addiu	$sp,$sp,48
	j	$31
	nop

>How-To-Repeat:
	run progress(1) or ftp(1) on a mip64el, notice speed is not properly
	printed, debug
>Fix:
	I guess the issue is in one of __floatdisf, __divsf3 or __fixsfdi

>Release-Note:

>Audit-Trail:

Responsible-Changed-From-To: toolchain-manager->port-mips-manager
Responsible-Changed-By: bouyer@NetBSD.org
Responsible-Changed-When: Tue, 30 Aug 2011 08:41:45 +0000
Responsible-Changed-Why:
Recategorize as port-mips


Responsible-Changed-From-To: port-mips-manager->port-mips-maintainer
Responsible-Changed-By: bouyer@NetBSD.org
Responsible-Changed-When: Tue, 30 Aug 2011 08:49:25 +0000
Responsible-Changed-Why:
Get Responsible right


From: David Holland <dholland-bugs@netbsd.org>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: toolchain/45310: some mixed int/float computations are wrong on
 mips64
Date: Tue, 30 Aug 2011 18:49:36 +0000

 Not sent to gnats.

    ------

 From: Manuel Bouyer <bouyer@antioche.eu.org>
 To: toolchain-manager@NetBSD.org, gnats-admin@NetBSD.org,
 	netbsd-bugs@NetBSD.org
 Subject: Re: toolchain/45310: some mixed int/float computations are wrong on
 	mips64
 Date: Tue, 30 Aug 2011 13:52:30 +0200

 On Mon, Aug 29, 2011 at 09:55:00PM +0000, Manuel.Bouyer@lip6.fr wrote:
 > >Description:
 > 	I first noticed that ftp(1) and progress(1) reported speeds of 0b/s.
 > 	I tracked it down to ptransfer() in ftp and finally ended up with this
 > 	test program:
 > 
 > #include <stdio.h>
 > #include <stdlib.h>
 > #include <sys/types.h>
 > 
 > main()
 > {
 > 	float a;
 > 	off_t b;
 > 
 > 	a = 0.544355;
 > 	b = 4866695;
 > 
 > 	printf("a %f b %d\n",a, (int)b);
 > 	b /= a;
 > 	printf("now b %d\n", (int)b);
 > }
 > 	On i386 this gives:
 > a 0.544355 b 4866695
 > now b 8940297
 > 	on mips64el:
 > a 0.544355 b 4866695
 > now b 0

 Here's a second test that could allow to narrow down the issue:
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>

 main()
 {
         float a;
         int64_t b;

         a = 33.000000;
         b = a;

         printf("a %f b %d\n",a, (int)b);
 }

 cuba:/home/bouyer>./test3
 a 33.000000 b 0

 looks like the problem could be in __fixsfdi() (or one of the functions used by
 __fixsfdi()).
 When compiled -O2 the above test shows the expected result, but then the
 compiler does the float -> int64_t conversion at compile time.

 Replacing the int64_t with uint64_t results in a core dump.
 I can't get more details because of PR port-mips/45309

 -- 
 Manuel Bouyer <bouyer@antioche.eu.org>
      NetBSD: 26 ans d'experience feront toujours la difference
 --

From: Manuel Bouyer <bouyer@antioche.eu.org>
To: gnats-bugs@NetBSD.org
Cc: port-mips-maintainer@NetBSD.org, gnats-admin@NetBSD.org,
        netbsd-bugs@NetBSD.org
Subject: Re: toolchain/45310: some mixed int/float computations are wrong
 on mips64
Date: Wed, 31 Aug 2011 15:26:31 +0200

 On Tue, Aug 30, 2011 at 06:50:04PM +0000, David Holland wrote:
 >  Here's a second test that could allow to narrow down the issue:
 >  #include <stdio.h>
 >  #include <stdlib.h>
 >  #include <sys/types.h>
 >  
 >  main()
 >  {
 >          float a;
 >          int64_t b;
 >  
 >          a = 33.000000;
 >          b = a;
 >  
 >          printf("a %f b %d\n",a, (int)b);
 >  }
 >  
 >  cuba:/home/bouyer>./test3
 >  a 33.000000 b 0
 >  
 >  looks like the problem could be in __fixsfdi() (or one of the functions used by
 >  __fixsfdi()).

 I __fixsfdi() uses __fixunssfdi() which uses double to uint casts, which is
 the problem as shown by this new test:
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>

 main()
 {
         double a;
 	uint b;

 	a = 33.000000;
 	b = a;

 	printf("a %f b %d\n",a, b);
 }

 cuba:/home/bouyer> gcc -o test4 test4.c
 cuba:/home/bouyer> ./test4
 a 33.000000 b 0

 tracking this down, I ended up in fixunsgen_ieee754.c:__fixunsgen32.
 It's called with
 exp=5, sign=0, mant_dig=53, fracbits=20, frac={0, 0, 32768}

 as exp - mant_dig is larger than 32, __fixunsgen32 returns 0.
 mant_dig is a constant from mips/float.h; I've no idea if it's right or
 wrong here ...

 -- 
 Manuel Bouyer <bouyer@antioche.eu.org>
      NetBSD: 26 ans d'experience feront toujours la difference
 --

From: Manuel Bouyer <bouyer@antioche.eu.org>
To: port-mips-maintainer@NetBSD.org
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Subject: Re: toolchain/45310: some mixed int/float computations are wrong
 on mips64
Date: Wed, 31 Aug 2011 15:47:57 +0200

 On Wed, Aug 31, 2011 at 01:30:06PM +0000, Manuel Bouyer wrote:
 >  tracking this down, I ended up in fixunsgen_ieee754.c:__fixunsgen32.
 >  It's called with
 >  exp=5, sign=0, mant_dig=53, fracbits=20, frac={0, 0, 32768}
 >  
 >  as exp - mant_dig is larger than 32, __fixunsgen32 returns 0.
 >  mant_dig is a constant from mips/float.h; I've no idea if it's right or
 >  wrong here ...

 It's DBL_MANT_DIG which is __DBL_MANT_DIG__ which is a gcc constant;
 it seems to be the same for o32 ABI.
 I also tested this on i386, and I get the exact same result.
 So my guess would be that __fixunsgen32() is buggy ...

 -- 
 Manuel Bouyer <bouyer@antioche.eu.org>
      NetBSD: 26 ans d'experience feront toujours la difference
 --

From: Manuel Bouyer <bouyer@antioche.eu.org>
To: port-mips-maintainer@NetBSD.org, gnats-bugs@NetBSD.org,
        netbsd-bugs@NetBSD.org
Cc: 
Subject: Re: toolchain/45310: some mixed int/float computations are wrong on
 mips64
Date: Wed, 31 Aug 2011 22:05:27 +0200

 --p4qYPpj5QlsIQJ0K
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: inline

 On Wed, Aug 31, 2011 at 01:30:06PM +0000, Manuel Bouyer wrote:
 >  I __fixsfdi() uses __fixunssfdi() which uses double to uint casts, which is
 >  the problem as shown by this new test:
 >  #include <stdio.h>
 >  #include <stdlib.h>
 >  #include <sys/types.h>
 >  
 >  main()
 >  {
 >          double a;
 >  	uint b;
 >  
 >  	a = 33.000000;
 >  	b = a;
 >  
 >  	printf("a %f b %d\n",a, b);
 >  }
 >  
 >  cuba:/home/bouyer> gcc -o test4 test4.c
 >  cuba:/home/bouyer> ./test4
 >  a 33.000000 b 0
 >  
 >  tracking this down, I ended up in fixunsgen_ieee754.c:__fixunsgen32.
 >  It's called with
 >  exp=5, sign=0, mant_dig=53, fracbits=20, frac={0, 0, 32768}
 >  
 >  as exp - mant_dig is larger than 32, __fixunsgen32 returns 0.

 No, exp - mant_dig is -48 so it's not larger than 32.
 The problem seems to be that in __fixunsgen32, both mant_dig and sizeof()
 are unsigned so gcc does an unsigned comparison.
 Casting to (long) makes it behaves as expected, and the
 change in the assembly is:
 -       lw      $3,32($fp)
         lw      $2,40($fp)
 +       lw      $3,32($fp)
         subu    $2,$3,$2
 -       sltu    $2,$2,32
 +       slt     $2,$2,32
         bne     $2,$0,$L3
         nop

 So unless gcc is doing the wrong thing here, I'd guess that the
 attached patch is the right fix. With this, my test programs passes,
 ftp(1) and progress(1) properly reports the download speed, and a pkgsrc
 perl build which was failing is now making progress.

 -- 
 Manuel Bouyer <bouyer@antioche.eu.org>
      NetBSD: 26 ans d'experience feront toujours la difference
 --

 --p4qYPpj5QlsIQJ0K
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: attachment; filename=diff

 Index: fixunsgen_ieee754.c
 ===================================================================
 RCS file: /cvsroot/src/lib/libc/gen/fixunsgen_ieee754.c,v
 retrieving revision 1.1
 diff -u -p -u -r1.1 fixunsgen_ieee754.c
 --- fixunsgen_ieee754.c	9 Jul 2011 02:30:27 -0000	1.1
 +++ fixunsgen_ieee754.c	31 Aug 2011 19:51:02 -0000
 @@ -66,7 +66,8 @@ FIXUNSNAME(__fixunsgen)(int exp, bool si
  	 * to zero.  If the exponent is so large that it is a multiple of
  	 * 2^N, then x module 2^N will be 0.
  	 */
 -	if (__predict_false(exp < 0 || exp - mant_dig > sizeof(UINTXX_T)*8-1))
 +	if (__predict_false(exp < 0 ||
 +	    exp - (long)mant_dig > (long)sizeof(UINTXX_T)*8-1))
  		return 0;

  	/*

 --p4qYPpj5QlsIQJ0K--

State-Changed-From-To: open->closed
State-Changed-By: bouyer@NetBSD.org
State-Changed-When: Thu, 01 Sep 2011 19:15:18 +0000
State-Changed-Why:
fixed with fixunsgen_ieee754.c 1.2


From: David Laight <david@l8s.co.uk>
To: Manuel Bouyer <bouyer@antioche.eu.org>
Cc: port-mips-maintainer@NetBSD.org, gnats-bugs@NetBSD.org,
	netbsd-bugs@NetBSD.org
Subject: Re: toolchain/45310: some mixed int/float computations are wrong on mips64
Date: Fri, 2 Sep 2011 18:37:53 +0100

 On Wed, Aug 31, 2011 at 10:05:27PM +0200, Manuel Bouyer wrote:
 >
 > -	if (__predict_false(exp < 0 || exp - mant_dig > sizeof(UINTXX_T)*8-1))
 > +	if (__predict_false(exp < 0 ||
 > +	    exp - (long)mant_dig > (long)sizeof(UINTXX_T)*8-1))
 >  		return 0;

 As a complete aside to the problem being discussed, I discovered that
 the __predict_false() above won't necessarily have the desired effect.

 I had to change some code to (effectively):
 	if (__predict_false(exp < 0) ||
 	    __predict_false(exp - (long)mant_dig > (long)sizeof(UINTXX_T)*8-1)))
 in order to get the desired static branch prediction.

 	David

 -- 
 David Laight: david@l8s.co.uk

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.