Optionally use libffi to implement 'foreign import "wrapper"' (#793)
[ghc-hetmet.git] / compiler / deSugar / DsForeign.lhs
index 5d47921..19c5d49 100644 (file)
@@ -1,11 +1,18 @@
 %
+% (c) The University of Glasgow 2006
 % (c) The AQUA Project, Glasgow University, 1998
 %
-\section[DsCCall]{Desugaring \tr{foreign} declarations}
 
-Expanding out @foreign import@ and @foreign export@ declarations.
+Desugaring foreign declarations (see also DsCCall).
 
 \begin{code}
+{-# OPTIONS -w #-}
+-- The above warning supression flag is a temporary kludge.
+-- While working on this module you are encouraged to remove it and fix
+-- any warnings in the module. See
+--     http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
+-- for details
+
 module DsForeign ( dsForeigns ) where
 
 #include "HsVersions.h"
@@ -13,47 +20,36 @@ import TcRnMonad    -- temp
 
 import CoreSyn
 
-import DsCCall         ( dsCCall, mkFCall, boxResult, unboxArg, resultWrapper )
+import DsCCall
 import DsMonad
 
-import HsSyn           ( ForeignDecl(..), ForeignExport(..), LForeignDecl,
-                         ForeignImport(..), CImportSpec(..) )
-import DataCon         ( splitProductType_maybe )
-#ifdef DEBUG
-import DataCon         ( dataConSourceArity )
-import Type            ( isUnLiftedType )
-#endif
-import MachOp          ( machRepByteWidth, MachRep(..) )
-import SMRep           ( argMachRep, typeCgRep )
-import CoreUtils       ( exprType, mkInlineMe )
-import Id              ( Id, idType, idName, mkSysLocal, setInlinePragma )
-import Literal         ( Literal(..), mkStringLit )
-import Module          ( moduleNameFS, moduleName )
-import Name            ( getOccString, NamedThing(..) )
-import Type            ( repType, coreEqType )
-import TcType          ( Type, mkFunTys, mkForAllTys, mkTyConApp,
-                         mkFunTy, tcSplitTyConApp_maybe, tcSplitIOType_maybe,
-                         tcSplitForAllTys, tcSplitFunTys, tcTyConAppArgs,
-                         isBoolTy
-                       )
-
-import BasicTypes       ( Boxity(..) )
-import HscTypes                ( ForeignStubs(..) )
-import ForeignCall     ( ForeignCall(..), CCallSpec(..), 
-                         Safety(..), 
-                         CExportSpec(..), CLabelString,
-                         CCallConv(..), ccallConvToInt,
-                         ccallConvAttribute
-                       )
-import TysWiredIn      ( unitTy, tupleTyCon )
-import TysPrim         ( addrPrimTy, mkStablePtrPrimTy, alphaTy, intPrimTy )
-import PrelNames       ( stablePtrTyConName, newStablePtrName, bindIOName,
-                         checkDotnetResName )
-import BasicTypes      ( Activation( NeverActive ) )
-import SrcLoc          ( Located(..), unLoc )
+import HsSyn
+import DataCon
+import MachOp
+import SMRep
+import CoreUtils
+import Id
+import Literal
+import Module
+import Name
+import Type
+import Coercion
+import TcType
+
+import HscTypes
+import ForeignCall
+import TysWiredIn
+import TysPrim
+import PrelNames
+import BasicTypes
+import SrcLoc
 import Outputable
-import Maybe           ( fromJust, isNothing )
 import FastString
+import Config
+import Constants
+
+import Data.Maybe
+import Data.List
 \end{code}
 
 Desugaring of @foreign@ declarations is naturally split up into
@@ -78,32 +74,31 @@ dsForeigns :: [LForeignDecl Id]
 dsForeigns [] 
   = returnDs (NoStubs, [])
 dsForeigns fos
-  = foldlDs combine (ForeignStubs empty empty [] [], []) fos
- where
-  combine stubs (L loc decl) = putSrcSpanDs loc (combine1 stubs decl)
-
-  combine1 (ForeignStubs acc_h acc_c acc_hdrs acc_feb, acc_f) 
-          (ForeignImport id _ spec)
+  = do 
+    fives <- mapM do_ldecl fos
+    let
+        (hs, cs, hdrs, idss, bindss) = unzip5 fives
+        fe_ids = concat idss
+        fe_init_code = map foreignExportInitialiser fe_ids
+    --
+    return (ForeignStubs 
+             (vcat hs)
+             (vcat cs $$ vcat fe_init_code)
+             (nub (concat hdrs)),
+           (concat bindss))
+  where
+   do_ldecl (L loc decl) = putSrcSpanDs loc (do_decl decl)
+            
+   do_decl (ForeignImport id _ spec)
     = traceIf (text "fi start" <+> ppr id)     `thenDs` \ _ ->
       dsFImport (unLoc id) spec                        `thenDs` \ (bs, h, c, mbhd) -> 
       traceIf (text "fi end" <+> ppr id)       `thenDs` \ _ ->
-      returnDs (ForeignStubs (h $$ acc_h)
-                            (c $$ acc_c)
-                            (addH mbhd acc_hdrs)
-                            acc_feb, 
-               bs ++ acc_f)
-
-  combine1 (ForeignStubs acc_h acc_c acc_hdrs acc_feb, acc_f) 
-          (ForeignExport (L _ id) _ (CExport (CExportStatic ext_nm cconv)))
+      returnDs (h, c, maybeToList mbhd, [], bs)
+
+   do_decl (ForeignExport (L _ id) _ (CExport (CExportStatic ext_nm cconv)))
     = dsFExport id (idType id) 
                ext_nm cconv False                 `thenDs` \(h, c, _, _) ->
-      returnDs (ForeignStubs (h $$ acc_h) (c $$ acc_c) acc_hdrs (id:acc_feb), 
-               acc_f)
-
-  addH Nothing  ls = ls
-  addH (Just e) ls
-   | e `elem` ls = ls
-   | otherwise   = e:ls
+      returnDs (h, c, [], [id], [])
 \end{code}
 
 
@@ -278,7 +273,7 @@ dsFExport :: Id                     -- Either the exported Id,
                                --         the first argument's stable pointer
          -> DsM ( SDoc         -- contents of Module_stub.h
                 , SDoc         -- contents of Module_stub.c
-                , [MachRep]    -- primitive arguments expected by stub function
+                , String       -- string describing type to pass to createAdj.
                 , Int          -- size of args to stub function
                 )
 
@@ -296,8 +291,9 @@ dsFExport fn_id ty ext_name cconv isDyn
        -- If it's IO t, return         (t, True)
        -- If it's plain t, return      (t, False)
      (case tcSplitIOType_maybe orig_res_ty of
-       Just (ioTyCon, res_ty) -> returnDs (res_ty, True)
+       Just (ioTyCon, res_ty, co) -> returnDs (res_ty, True)
                -- The function already returns IO t
+               -- ToDo: what about the coercion?
        Nothing                -> returnDs (orig_res_ty, False) 
                -- The function returns t
      )                                 `thenDs` \ (res_ty,             -- t
@@ -324,7 +320,7 @@ f :: Fun -> IO (FunPtr Fun)
 f cback =
    bindIO (newStablePtr cback)
           (\StablePtr sp# -> IO (\s1# ->
-              case _ccall_ createAdjustor cconv sp# ``f_helper'' s1# of
+              case _ccall_ createAdjustor cconv sp# ``f_helper'' <arg info> s1# of
                  (# s2#, a# #) -> (# s2#, A# a# #)))
 
 foreign import "&f_helper" f_helper :: FunPtr (StablePtr Fun -> Fun)
@@ -353,21 +349,14 @@ dsFExportDynamic id cconv
      dsLookupGlobalId newStablePtrName         `thenDs` \ newStablePtrId ->
      dsLookupTyCon stablePtrTyConName          `thenDs` \ stable_ptr_tycon ->
      let
-       mk_stbl_ptr_app = mkApps (Var newStablePtrId) [ Type arg_ty, Var cback ]
        stable_ptr_ty   = mkTyConApp stable_ptr_tycon [arg_ty]
        export_ty       = mkFunTy stable_ptr_ty arg_ty
      in
      dsLookupGlobalId bindIOName               `thenDs` \ bindIOId ->
      newSysLocalDs stable_ptr_ty               `thenDs` \ stbl_value ->
      dsFExport id export_ty fe_nm cconv True   
-               `thenDs` \ (h_code, c_code, arg_reps, args_size) ->
+               `thenDs` \ (h_code, c_code, typestring, args_size) ->
      let
-      stbl_app cont ret_ty = mkApps (Var bindIOId)
-                                   [ Type stable_ptr_ty
-                                   , Type ret_ty       
-                                   , mk_stbl_ptr_app
-                                   , cont
-                                   ]
        {-
         The arguments to the external function which will
        create a little bit of (template) code on the fly
@@ -378,18 +367,12 @@ dsFExportDynamic id cconv
       adj_args      = [ mkIntLitInt (ccallConvToInt cconv)
                      , Var stbl_value
                      , mkLit (MachLabel fe_nm mb_sz_args)
-                      , mkLit (mkStringLit arg_type_info)
+                      , mkLit (mkStringLit typestring)
                      ]
         -- name of external entry point providing these services.
        -- (probably in the RTS.) 
       adjustor  = FSLIT("createAdjustor")
       
-      arg_type_info = map repCharCode arg_reps
-      repCharCode F32 = 'f'
-      repCharCode F64 = 'd'
-      repCharCode I64 = 'l'
-      repCharCode _   = 'i'
-
        -- Determine the number of bytes of arguments to the stub function,
        -- so that we can attach the '@N' suffix to its label if it is a
        -- stdcall on Windows.
@@ -398,15 +381,19 @@ dsFExportDynamic id cconv
                      _           -> Nothing
 
      in
-     dsCCall adjustor adj_args PlayRisky io_res_ty     `thenDs` \ ccall_adj ->
+     dsCCall adjustor adj_args PlayRisky (mkTyConApp io_tc [res_ty])  `thenDs` \ ccall_adj ->
        -- PlayRisky: the adjustor doesn't allocate in the Haskell heap or do a callback
-     let ccall_adj_ty = exprType ccall_adj
-         ccall_io_adj = mkLams [stbl_value]                 $
-                       Note (Coerce io_res_ty ccall_adj_ty)
-                            ccall_adj
-         io_app = mkLams tvs    $
-                 mkLams [cback] $
-                 stbl_app ccall_io_adj res_ty
+
+     let io_app = mkLams tvs               $
+                 Lam cback                 $          
+                 mkCoerceI (mkSymCoI co)   $
+                 mkApps (Var bindIOId)
+                        [ Type stable_ptr_ty
+                        , Type res_ty       
+                        , mkApps (Var newStablePtrId) [ Type arg_ty, Var cback ]
+                        , Lam stbl_value ccall_adj
+                        ]
+
         fed = (id `setInlinePragma` NeverActive, io_app)
                -- Never inline the f.e.d. function, because the litlit
                -- might not be in scope in other modules.
@@ -414,11 +401,12 @@ dsFExportDynamic id cconv
      returnDs ([fed], h_code, c_code)
 
  where
-  ty                   = idType id
-  (tvs,sans_foralls)   = tcSplitForAllTys ty
-  ([arg_ty], io_res_ty)        = tcSplitFunTys sans_foralls
-  [res_ty]             = tcTyConAppArgs io_res_ty
-       -- Must use tcSplit* to see the (IO t), which is a newtype
+  ty                      = idType id
+  (tvs,sans_foralls)      = tcSplitForAllTys ty
+  ([arg_ty], fn_res_ty)           = tcSplitFunTys sans_foralls
+  Just (io_tc, res_ty, co) = tcSplitIOType_maybe fn_res_ty
+       -- Must have an IO type; hence Just
+       -- co : fn_res_ty ~ IO res_ty
 
 toCName :: Id -> String
 toCName i = showSDoc (pprCode CStyle (ppr (idName i)))
@@ -443,12 +431,11 @@ mkFExportCBits :: FastString
               -> CCallConv 
               -> (SDoc, 
                   SDoc,
-                  [MachRep],   -- the argument reps
+                  String,      -- the argument reps
                   Int          -- total size of arguments
                  )
 mkFExportCBits c_nm maybe_target arg_htys res_hty is_IO_res_ty cc 
- = (header_bits, c_bits, 
-    [rep | (_,_,_,rep) <- arg_info],  -- just the real args
+ = (header_bits, c_bits, type_string,
     sum [ machRepByteWidth rep | (_,_,_,rep) <- aug_arg_info] -- all the args
     )
  where
@@ -457,9 +444,28 @@ mkFExportCBits c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
                SDoc,           -- C type
                Type,           -- Haskell type
                MachRep)]       -- the MachRep
-  arg_info  = [ (text ('a':show n), showStgType ty, ty, 
+  arg_info  = [ let stg_type = showStgType ty in
+                (arg_cname n stg_type,
+                 stg_type,
+                 ty, 
                 typeMachRep (getPrimTyOf ty))
-             | (ty,n) <- zip arg_htys [1..] ]
+             | (ty,n) <- zip arg_htys [1::Int ..] ]
+
+  arg_cname n stg_ty
+        | libffi    = char '*' <> parens (stg_ty <> char '*') <> 
+                      ptext SLIT("args") <> brackets (int (n-1))
+        | otherwise = text ('a':show n)
+
+  -- generate a libffi-style stub if this is a "wrapper" and libffi is enabled
+  libffi = cLibFFI && isNothing maybe_target
+
+  type_string
+      -- libffi needs to know the result type too:
+      | libffi    = primTyDescChar res_hty : arg_type_string
+      | otherwise = arg_type_string
+
+  arg_type_string = [primTyDescChar ty | (_,_,ty,_) <- arg_info]
+                -- just the real args
 
   -- add some auxiliary args; the stable ptr in the wrapper case, and
   -- a slot for the dummy return address in the wrapper + ccall case
@@ -484,7 +490,12 @@ mkFExportCBits c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
 
   header_bits = ptext SLIT("extern") <+> fun_proto <> semi
 
-  fun_proto = cResType <+> pprCconv <+> ftext c_nm <>
+  fun_proto
+    | libffi
+      = ptext SLIT("void") <+> ftext c_nm <> 
+          parens (ptext SLIT("void *cif STG_UNUSED, void* resp, void** args, void* the_stableptr"))
+    | otherwise
+      = cResType <+> pprCconv <+> ftext c_nm <>
              parens (hsep (punctuate comma (map (\(nm,ty,_,_) -> ty <+> nm) 
                                                  aug_arg_info)))
 
@@ -520,28 +531,6 @@ mkFExportCBits c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
           Just hs_fn -> text "extern StgClosure " <> ppr hs_fn <> text "_closure" <> semi
 
    
-   -- Initialise foreign exports by registering a stable pointer from an
-   -- __attribute__((constructor)) function.
-   -- The alternative is to do this from stginit functions generated in
-   -- codeGen/CodeGen.lhs; however, stginit functions have a negative impact
-   -- on binary sizes and link times because the static linker will think that
-   -- all modules that are imported directly or indirectly are actually used by
-   -- the program.
-   -- (this is bad for big umbrella modules like Graphics.Rendering.OpenGL)
-
-  initialiser
-     = case maybe_target of
-          Nothing -> empty
-          Just hs_fn ->
-            vcat
-             [ text "static void stginit_export_" <> ppr hs_fn
-                  <> text "() __attribute__((constructor));"
-             , text "static void stginit_export_" <> ppr hs_fn <> text "()"
-             , braces (text "getStablePtr"
-                <> parens (text "(StgPtr) &" <> ppr hs_fn <> text "_closure")
-                <> semi)
-             ]
-
   -- finally, the whole darn thing
   c_bits =
     space $$
@@ -549,33 +538,56 @@ mkFExportCBits c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
     fun_proto  $$
     vcat 
      [ lbrace
-     ,   text "Capability *cap;"
+     ,   ptext SLIT("Capability *cap;")
      ,   declareResult
      ,   declareCResult
      ,   text "cap = rts_lock();"
          -- create the application + perform it.
-     ,   text "cap=rts_evalIO" <> parens (
+     ,   ptext SLIT("cap=rts_evalIO") <> parens (
                cap <>
-               text "rts_apply" <> parens (
+               ptext SLIT("rts_apply") <> parens (
                    cap <>
                    text "(HaskellObj)"
-                <> text (if is_IO_res_ty 
-                               then "runIO_closure" 
-                               else "runNonIO_closure")
+                <> ptext (if is_IO_res_ty 
+                               then SLIT("runIO_closure")
+                               else SLIT("runNonIO_closure"))
                 <> comma
                 <> expr_to_run
                ) <+> comma
               <> text "&ret"
             ) <> semi
-     ,   text "rts_checkSchedStatus" <> parens (doubleQuotes (ftext c_nm)
+     ,   ptext SLIT("rts_checkSchedStatus") <> parens (doubleQuotes (ftext c_nm)
                                                <> comma <> text "cap") <> semi
      ,   assignCResult
-     ,   text "rts_unlock(cap);"
+     ,   ptext SLIT("rts_unlock(cap);")
      ,   if res_hty_is_unit then empty
-            else text "return cret;"
+            else if libffi 
+                  then char '*' <> parens (cResType <> char '*') <> 
+                       ptext SLIT("resp = cret;")
+                  else ptext SLIT("return cret;")
      , rbrace
-     ] $$
-    initialiser
+     ]
+
+
+foreignExportInitialiser :: Id -> SDoc
+foreignExportInitialiser hs_fn =
+   -- Initialise foreign exports by registering a stable pointer from an
+   -- __attribute__((constructor)) function.
+   -- The alternative is to do this from stginit functions generated in
+   -- codeGen/CodeGen.lhs; however, stginit functions have a negative impact
+   -- on binary sizes and link times because the static linker will think that
+   -- all modules that are imported directly or indirectly are actually used by
+   -- the program.
+   -- (this is bad for big umbrella modules like Graphics.Rendering.OpenGL)
+   vcat
+    [ text "static void stginit_export_" <> ppr hs_fn
+         <> text "() __attribute__((constructor));"
+    , text "static void stginit_export_" <> ppr hs_fn <> text "()"
+    , braces (text "getStablePtr"
+       <> parens (text "(StgPtr) &" <> ppr hs_fn <> text "_closure")
+       <> semi)
+    ]
+
 
 -- NB. the calculation here isn't strictly speaking correct.
 -- We have a primitive Haskell type (eg. Int#, Double#), and
@@ -638,4 +650,26 @@ getPrimTyOf ty
      _other -> pprPanic "DsForeign.getPrimTyOf" (ppr ty)
   where
        rep_ty = repType ty
+
+-- represent a primitive type as a Char, for building a string that
+-- described the foreign function type.  The types are size-dependent,
+-- e.g. 'W' is a signed 32-bit integer.
+primTyDescChar :: Type -> Char
+primTyDescChar ty
+ | ty `coreEqType` unitTy = 'v'
+ | otherwise
+ = case typePrimRep (getPrimTyOf ty) of
+     IntRep     -> signed_word
+     WordRep     -> unsigned_word
+     Int64Rep    -> 'L'
+     Word64Rep   -> 'l'
+     AddrRep     -> unsigned_word
+     FloatRep    -> 'f'
+     DoubleRep   -> 'd'
+     _           -> pprPanic "primTyDescChar" (ppr ty)
+  where
+    (signed_word, unsigned_word)
+       | wORD_SIZE == 4  = ('W','w')
+       | wORD_SIZE == 8  = ('L','l')
+       | otherwise       = panic "primTyDescChar"
 \end{code}