Quick Q: do all FFI (non-primop) calls involve State# and RealWorld?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Quick Q: do all FFI (non-primop) calls involve State# and RealWorld?

Christopher Done-2

Hi all,

I tried compiling this file:

{-# LANGUAGE NoImplicitPrelude #-}
-- | Demonstrate various use of the FFI.
module Foreign where
import Foreign.C
foreign import ccall "math.h sin" sin :: CDouble -> CDouble
it :: CDouble
it = sin 1

And I’ve noticed that the annotated type given for this foreign op in Core, is (# State# RealWorld, CDouble #), whereas I would have expected e.g. CDouble.

Meanwhile, the foreign op call is passed a RealWorld argument.

Additionally, code that consumes the result of this foreign call expects a (# CDouble #) as a return value.

So there are some assumptions I put in my interpreter to test this FFI call out:

  1. Despite claiming to return the real world in a tuple, it actually should just return an unboxed tuple of the value.
  2. It should ignore the RealWorld argument entirely.

I assume, if I were to lift this function into returning IO, that I should indeed return the RealWorld argument given. So the lesson is:

All FFI functions accept a RealWorld, and may return a 2-tuple of State# RealWorld if it’s impure, else it’ll return a 1-tuple of the value. Correct?

Can someone confirm that my observations are right? Also, if so, is there somewhere I can read more about this?

Cheers

Chris


_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
Reply | Threaded
Open this post in threaded view
|

Re: Quick Q: do all FFI (non-primop) calls involve State# and RealWorld?

Csaba Hruska
Hi,

I've also observed that in the final lowered STG the State# is always passed to effectful primops and FFI calls, but the returning new State# is removed from the result type. The State# has VoidRep representation in in cmm, so no register gets allocated for it and eventually the State# function argument is compiled to nothing in the machine code. i.e. the compilation steps for the code above is:
foreign import ccall "math.h sin" sin :: CDouble -> CDouble
  1.  Initial STG type is: CDouble -> State# RealWorld -> (# State# RealWorld, CDouble #)
  2.  Lowered STG type is: CDouble -> State# RealWorld -> (# CDouble #)
  3.  FFI C function should be: double sin(double x);
Regards,
Csaba

On Mon, Oct 28, 2019 at 10:59 AM Christopher Done <[hidden email]> wrote:

Hi all,

I tried compiling this file:

{-# LANGUAGE NoImplicitPrelude #-}
-- | Demonstrate various use of the FFI.
module Foreign where
import Foreign.C
foreign import ccall "math.h sin" sin :: CDouble -> CDouble
it :: CDouble
it = sin 1

And I’ve noticed that the annotated type given for this foreign op in Core, is (# State# RealWorld, CDouble #), whereas I would have expected e.g. CDouble.

Meanwhile, the foreign op call is passed a RealWorld argument.

Additionally, code that consumes the result of this foreign call expects a (# CDouble #) as a return value.

So there are some assumptions I put in my interpreter to test this FFI call out:

  1. Despite claiming to return the real world in a tuple, it actually should just return an unboxed tuple of the value.
  2. It should ignore the RealWorld argument entirely.

I assume, if I were to lift this function into returning IO, that I should indeed return the RealWorld argument given. So the lesson is:

All FFI functions accept a RealWorld, and may return a 2-tuple of State# RealWorld if it’s impure, else it’ll return a 1-tuple of the value. Correct?

Can someone confirm that my observations are right? Also, if so, is there somewhere I can read more about this?

Cheers

Chris

_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
Reply | Threaded
Open this post in threaded view
|

Re: Quick Q: do all FFI (non-primop) calls involve State# and RealWorld?

Christopher Done-2

Aha, thanks Csaba. So I’m not losing my marbles. The AST has a type
signature of the “initial” but implements the “lowered”. So with
-ddump-stg we can observe it:

The version claimed in the type signature (returning a tuple):

Foreign.it :: Foreign.C.Types.CDouble
[GblId] =
    [] \u []
        case ds_r1HA of {
          GHC.Types.D# ds2_s1HW [Occ=Once] ->
              case __pkg_ccall_GC main [ds2_s1HW GHC.Prim.realWorld#] of {
                (#,#) _ [Occ=Dead] ds4_s1I0 [Occ=Once] -> GHC.Types.D# [ds4_s1I0];
              };
        };

The final “lowered” version:

Foreign.it :: Foreign.C.Types.CDouble
[GblId] =
    [] \u []
        case ds_r1HA of {
          GHC.Types.D# ds2_s1HW [Occ=Once] ->
              case __pkg_ccall_GC main [ds2_s1HW GHC.Prim.realWorld#] of {
                Unit# ds4_s1I0 [Occ=Once] -> GHC.Types.D# [ds4_s1I0];
              };
        };

Cheers!

Chris


_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
Reply | Threaded
Open this post in threaded view
|

Re: Quick Q: do all FFI (non-primop) calls involve State# and RealWorld?

Christopher Done-2
For anyone interested, here's a complete list of all foreign imports at the STG level from base and integer-simple:


They all have type (# State# RealWorld #) or (# State# RealWorld, <something> #).

On Tue, 5 Nov 2019 at 15:18, Christopher Done <[hidden email]> wrote:

Aha, thanks Csaba. So I’m not losing my marbles. The AST has a type
signature of the “initial” but implements the “lowered”. So with
-ddump-stg we can observe it:

The version claimed in the type signature (returning a tuple):

Foreign.it :: Foreign.C.Types.CDouble
[GblId] =
    [] \u []
        case ds_r1HA of {
          GHC.Types.D# ds2_s1HW [Occ=Once] ->
              case __pkg_ccall_GC main [ds2_s1HW GHC.Prim.realWorld#] of {
                (#,#) _ [Occ=Dead] ds4_s1I0 [Occ=Once] -> GHC.Types.D# [ds4_s1I0];
              };
        };

The final “lowered” version:

Foreign.it :: Foreign.C.Types.CDouble
[GblId] =
    [] \u []
        case ds_r1HA of {
          GHC.Types.D# ds2_s1HW [Occ=Once] ->
              case __pkg_ccall_GC main [ds2_s1HW GHC.Prim.realWorld#] of {
                Unit# ds4_s1I0 [Occ=Once] -> GHC.Types.D# [ds4_s1I0];
              };
        };

Cheers!

Chris


_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs