logo       

-fvia-c -O generates mal-typed C code (casts double via unsigned int): msg#00005

lang.haskell.glasgow.bugs

Subject: -fvia-c -O generates mal-typed C code (casts double via unsigned int)

Hi,

Sorry this is a rather complicated bug report but I think I've tracked
it down, please bear with me. :-)

It involves C code, FFI and optimisations (all seem to be required).
Nice.

The problem shows up as a double value loosing precision when it is sent
to C land and back. It's because ghc generates C code that casts the
value via an unsigned word when it gets it back from C land.

Attached is a small collection of modules which exhibit the problem. If
I make the test case much smaller, the problem goes away. It's not too
complicated code though. Some imports were hacked around and a few bits
commented out to get it out of a larger library. The test program sends
(-1) :: Double to C land and back and gets something rather different
back.

For some context, it's part of the gtk2hs bindings. It deals with a data
type (GValue) on the C side that is a 'variant' type. A big union of
primitive types with an enum field to say what is in the union. The code
implements peek/poke for this type. The peek/poke code does the obvious
thing which is a case expression on the Haskell sum-type value or the
type field of the C value. In each branch of the case we make an FFI
call to do the actual getting / setting:

peek gvPtr = do
gtype <- liftM (toEnum.fromIntegral::Word32 -> TMType) $
(\hsc_ptr -> peekByteOff hsc_ptr 0) gvPtr
case gtype of
TMuint -> liftM GVuint $ valueGetUInt gvPtr
TMint -> liftM GVint $ valueGetInt gvPtr
...

valueGetUInt, valueGetInt, etc are wrapper around FFI calls.

The problem is on the peek side of things. Lets compile everything and
look at the generated C code:

ghc -ffi -C -O GValue.hs '-#include <glib-object.h>'
-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
ghc -ffi -C -O GValueTypes.hs '-#include <glib-object.h>'
-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
ghc -ffi -C -O StoreValue.hs '-#include <glib-object.h>'
-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include

Now ghc is clever enough to inline the trivial wrappers functions in
GValueTypes.hs so that the actual C calls are made from StoreValue.hc /
StoreValue.o.

gcc emits some warnings when thic C code is compiled, which gives us a
hint at the problem we'll find:
/tmp/ghc18212.hc: In function `StoreValue_zdwpeek_entry':
/tmp/ghc18212.hc:2225: warning: assignment makes integer from pointer without a
cast
/tmp/ghc18212.hc:2227: warning: assignment makes pointer from integer without a
cast
/tmp/ghc18212.hc:2267: warning: assignment discards qualifiers from pointer
target type
/tmp/ghc18212.hc:2269: warning: assignment makes integer from pointer without a
cast
/tmp/ghc18212.hc:2271: warning: assignment makes pointer from integer without a
cast
/tmp/ghc18212.hc:2289: warning: assignment makes integer from pointer without a
cast
/tmp/ghc18212.hc:2291: warning: assignment makes pointer from integer without a
cast

If we look at the call site:

case 60:
_B0_=(StgAddr)(*Sp);
*Sp=(W_)((P_)&s6Vx_info);
{
StgDouble _ccall_result;
StgAddr _ccall_arg1=_B0_;
CALLER_SAVE_SYSTEM
_ccall_result = (g_value_get_double((_ccall_arg1)));
CALLER_RESTORE_SYSTEM
_B3_=_ccall_result;
}
ASSIGN_DBL((W_*)(Sp-2),_B3_);
Sp=Sp-2;
JMP_(ENTRY_CODE((P_)(Sp[2])));
break;

What is the type of _B3_ ? Well it turns out to be
W_ _B3_;
which I assume is a machine word. Of course this means that C will
happily and silently convert the double to an unsigned int. The same
_B3_ variable is in scope for all the other branches of the C switch
statement, that is why gcc complains at some of the other call sites
(they're casting a pointer to int or the other way round). Sadly the
double case is valid C so gcc does not complain.

In GValueTypes.hc we get similar code to make the ffi calls, but because
they are in separate functions there, they each get their own _B3_
variable of the right local type. It is only because of optimisations
that the ffi calls have been moved together into one big switch
statement and the _B3_ variable has been moved outside of each case
branch that we get the problem. It might work if each branch got its own
_B3_ variable or if all the assignments to/from it were cast
appropriately so that C does not perform any implicit promotion /
narrowing.

Duncan

Attachment: GValue.hs
Description: Text Data

Attachment: GValueTypes.hs
Description: Text Data

Attachment: StoreValue.hs
Description: Text Data

Attachment: Test.hs
Description: Text Data

_______________________________________________
Glasgow-haskell-bugs mailing list
Glasgow-haskell-bugs@xxxxxxxxxxx
http://www.haskell.org/mailman/listinfo/glasgow-haskell-bugs
<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

News | FAQ | advertise