-fvia-c -O generates mal-typed C code (casts double via unsigned
int)
Duncan Coutts
duncan.coutts at worcester.oxford.ac.uk
Sat Aug 7 13:52:38 EDT 2004
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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: GValue.hs
Type: text/x-haskell
Size: 2140 bytes
Desc: not available
Url : http://www.haskell.org/pipermail/glasgow-haskell-bugs/attachments/20040807/a905a923/GValue.bin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: GValueTypes.hs
Type: text/x-haskell
Size: 6096 bytes
Desc: not available
Url : http://www.haskell.org/pipermail/glasgow-haskell-bugs/attachments/20040807/a905a923/GValueTypes.bin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: StoreValue.hs
Type: text/x-haskell
Size: 6074 bytes
Desc: not available
Url : http://www.haskell.org/pipermail/glasgow-haskell-bugs/attachments/20040807/a905a923/StoreValue.bin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Test.hs
Type: text/x-haskell
Size: 282 bytes
Desc: not available
Url : http://www.haskell.org/pipermail/glasgow-haskell-bugs/attachments/20040807/a905a923/Test.bin
More information about the Glasgow-haskell-bugs
mailing list