From owner-freebsd-arm@FreeBSD.ORG Tue Jan 17 19:10:41 2012 Return-Path: Delivered-To: freebsd-arm@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 3DB50106566C for ; Tue, 17 Jan 2012 19:10:41 +0000 (UTC) (envelope-from freebsd@damnhippie.dyndns.org) Received: from qmta04.emeryville.ca.mail.comcast.net (qmta04.emeryville.ca.mail.comcast.net [76.96.30.40]) by mx1.freebsd.org (Postfix) with ESMTP id 1BA538FC1B for ; Tue, 17 Jan 2012 19:10:40 +0000 (UTC) Received: from omta18.emeryville.ca.mail.comcast.net ([76.96.30.74]) by qmta04.emeryville.ca.mail.comcast.net with comcast id Ngzx1i0061bwxycA4jAgLM; Tue, 17 Jan 2012 19:10:40 +0000 Received: from damnhippie.dyndns.org ([24.8.232.202]) by omta18.emeryville.ca.mail.comcast.net with comcast id NjAf1i00u4NgCEG8ejAg8J; Tue, 17 Jan 2012 19:10:40 +0000 Received: from [172.22.42.240] (revolution.hippie.lan [172.22.42.240]) by damnhippie.dyndns.org (8.14.3/8.14.3) with ESMTP id q0HJAcpH005065; Tue, 17 Jan 2012 12:10:38 -0700 (MST) (envelope-from freebsd@damnhippie.dyndns.org) From: Ian Lepore To: David Schultz In-Reply-To: <20120116195113.GA87187@zim.MIT.EDU> References: <20120111175516.GA99475@zim.MIT.EDU> <1326509894.48691.100.camel@revolution.hippie.lan> <20120114081214.GA14925@zim.MIT.EDU> <1326563626.1678.34.camel@revolution.hippie.lan> <20120114182933.GA17739@zim.MIT.EDU> <1326568038.1678.43.camel@revolution.hippie.lan> <20120114211039.GA18310@zim.MIT.EDU> <1326591214.1678.85.camel@revolution.hippie.lan> <20120116022647.GA36657@zim.MIT.EDU> <1326730552.1669.29.camel@revolution.hippie.lan> <20120116195113.GA87187@zim.MIT.EDU> Content-Type: text/plain Date: Tue, 17 Jan 2012 12:10:37 -0700 Message-Id: <1326827437.1669.148.camel@revolution.hippie.lan> Mime-Version: 1.0 X-Mailer: Evolution 2.26.0 FreeBSD GNOME Team Port Content-Transfer-Encoding: 7bit Cc: freebsd-arm Subject: Re: fenv.h fixes for softfloat X-BeenThere: freebsd-arm@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Porting FreeBSD to the StrongARM Processor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 17 Jan 2012 19:10:41 -0000 On Mon, 2012-01-16 at 14:51 -0500, David Schultz wrote: > On Mon, Jan 16, 2012, Ian Lepore wrote: > The problem is that double->long conversions use the __fixdfsi > function in softfloat, but there's no equivalent double->longlong. > Instead, a semi-bogus __fixdfdi function is provided in libc/quad. > It'll take a little while to come up with a good fix. Okay, so cast to 64 bits is a known problem. To see if any other problems are lurking in this area I redefined assert to be non-fatal in test-lrint and ran it so we could see the results of the later tests. It looks like there are two failures (at lines 104 and 108) that aren't related (at least obviously/directly) to the llrint failures. I'll paste the whole log in case my sense of what's "obvious" is wrong... 1..1 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0) file test-lrint.c line 96 while testing llrint: x=1 result=1 excepts=0x10 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0) file test-lrint.c line 96 while testing llrintf: x=1 result=1 excepts=0x10 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0) file test-lrint.c line 96 while testing llrintl: x=1 result=1 excepts=0x10 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0) file test-lrint.c line 97 while testing llrint: x=3.05418e+08 result=305418240 excepts=0x10 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0) file test-lrint.c line 97 while testing llrintf: x=3.05418e+08 result=305418240 excepts=0x10 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0) file test-lrint.c line 97 while testing llrintl: x=3.05418e+08 result=305418240 excepts=0x10 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 104 while testing lrint: x=2.14749e+09 result=0 excepts=0x11 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 108 while testing lrint: x=-2.14749e+09 result=0 excepts=0x11 nfassert: (llrint)(_d) == (0) || fetestexcept(FE_INVALID) file test-lrint.c line 136 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 136 while testing llrint: x=9.22337e+18 result=0 excepts=0 nfassert: (llrintf)(_d) == (0) || fetestexcept(FE_INVALID) file test-lrint.c line 137 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 137 while testing llrintf: x=9.22337e+18 result=0 excepts=0 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0) file test-lrint.c line 138 while testing llrint: x=9.22337e+18 result=9223372036854774784 excepts=0x10 nfassert: (llrint)(_d) == (0) || fetestexcept(FE_INVALID) file test-lrint.c line 140 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 140 while testing llrint: x=-9.22337e+18 result=0 excepts=0x10 nfassert: (llrintf)(_d) == (0) || fetestexcept(FE_INVALID) file test-lrint.c line 141 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 141 while testing llrintf: x=-9.22337e+18 result=0 excepts=0 ok 1 - lrint Zooming in on the two lrint failures, it looks like the rint() routine raises INEXACT and the cast to long raises INVALID. I came up with that by using instrumented code to print the state of the exception flags at various points within s_rint.c and s_lrint.c The s_lrint.c instrumentation was a problem, and I figured I'd better detail it in case it indicates some wider subtle problem. My goal was to capture and display the flags after rint(), then clean them out so I could separately display the flags raised by the cast, then blend the flags back together so that the flags seen by the caller would be the same as they would have been without my instrumentation changes: dtype fn(type x) { fenv_t env; volatile dtype d; volatile type rx; feholdexcept(&env); rx = roundit(x); printf("after rint %#x\n", fetestexcept(FE_ALL_EXCEPT)); if (fetestexcept(FE_INVALID)) feclearexcept(FE_INEXACT); feupdateenv(&env); feholdexcept(&env); printf("before cast %#x\n", fetestexcept(FE_ALL_EXCEPT)); d = (dtype)rx; printf("after cast %#x\n", fetestexcept(FE_ALL_EXCEPT)); feupdateenv(&env); printf("after update %#x\n", fetestexcept(FE_ALL_EXCEPT)); return (d); } Here are the run results (testing just lines 104 and 108, by moving them into main() making them lines 158 & 160 respectively): tflex# ./test-lrint 1..1 rint 0: except 0 rint 1: except 0 i0=0x41dfffff i1=0xffe00000 sx=0 j0=30 rint 6: except 0 i=0x3fffff j0=0x1e rint 8: except 0x10 i0=0x41dfffff i1=0xffe00000 x=2.14749e+09 w=4.50361e+15 result=2.14749e+09 after rint 0x10 before cast 0 after cast 0x1 after update 0x11 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 158 while testing lrint: x=2.14749e+09 result=0 excepts=0x11 rint 0: except 0 rint 1: except 0 i0=0xc1e00000 i1=0x100000 sx=1 j0=31 rint 6: except 0 i=0x1fffff j0=0x1f rint 8: except 0x10 i0=0xc1e00000 i1=0x100000 x=-2.14749e+09 w=-4.50361e+15 result=-2.14749e+09 after rint 0x10 before cast 0 after cast 0x1 after update 0x11 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 160 while testing lrint: x=-2.14749e+09 result=0 excepts=0x11 Okay, so all of that is what I summarized above. Here's the problem: To get that output, I had to change __softfloat_float_exception_flags to be declared as volatile in all 3 places it's mentioned (two extern decls and the definition). I tried just casting to volatile in fetestexception() and that alone wasn't enough to printf everything correctly. Before changing that var to volatile, I got this output from the instrumented code: tflex# ./test-lrint 1..1 rint 0: except 0 rint 1: except 0 i0=0x41dfffff i1=0xffe00000 sx=0 j0=30 rint 6: except 0 i=0x3fffff j0=0x1e rint 8: except 0x10 i0=0x41dfffff i1=0xffe00000 x=2.14748e+09 w=4.5036e+15 result=2.14748e+09 after rint 0 before cast 0 after cast 0x1 after update 0x11 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 158 while testing lrint: x=2.14748e+09 result=0 excepts=0x11 You can see the insanity there that drove me to try using volatile: rint() says it has raised INEXACT just before returning, but printing the flags immediately upon return from rint() showed 0. But, note that the flag variable was correct because the final feupdateenv() raised it again, so the second feholdenv() must have seen it set, right? But if you can't print the right values along the way, then the compiler is somehow feeling free to re-order the code in such a way that the printfs don't show the right values. If it could do that in such a way as to mess up the printfs, could it do so in a way that had more serious side effects? Notice also that I declared the intermidate vars 'd' and 'rx' as volatile. This was also necessary to prevent re-ordering the code in a way that ruined more than just printfs. It was deferring the cast until the return statement, making all the code trying to print flags and manipulate the fenv between the cast and the return moot. Even though the cast is accomplished with a function call, it's almost as if the optimizer isn't treating it as a normal function call that can have the side effect of modifying a global variable. Note that this last one seems like a real problem, not just "my printfs aren't right". If I add feclearexcept(FE_INVALID) right before the last feupdateenv(), then when the cast raises FE_INVALID it should get cleared and not be seen by the caller of lrint(). But because the cast gets deferred until the return, the caller does see FE_INVALID: tflex# ./test-lrint 1..1 rint 0: except 0 rint 1: except 0 i0=0x41dfffff i1=0xffe00000 sx=0 j0=30 rint 6: except 0 i=0x3fffff j0=0x1e rint 8: except 0x10 i0=0x41dfffff i1=0xffe00000 x=2.14748e+09 w=4.5036e+15 result=2.14748e+09 after rint 0x10 before cast 0 after cast 0 after update 0x10 nfassert: fetestexcept(FE_ALL_EXCEPT) == (0x0001) file test-lrint.c line 158 while testing lrint: x=2.14748e+09 result=0 excepts=0x11 Note that without 'd' being declared volatile the exception flags are 0x11 after the call even though the 0x01 flag was cleared before returning from lrint() (the global flags var was volatile for this run; from the printfs it looks like it was never set, but remember that's because the optimizer relocated the cast to the point of the return). This may all come down to a big steaming pile of compiler buggage that you can't do anything about, but I figured I'd dump out everything I've seen so that if you get future reports of odd behavior you're at least forearmed with some knowledge of the ways wonky things can happen. -- Ian