Desugaring for "foreign import prim"
[ghc-hetmet.git] / compiler / deSugar / DsForeign.lhs
index d7096ae..9dea2ad 100644 (file)
@@ -6,13 +6,6 @@
 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"
@@ -25,17 +18,19 @@ import DsMonad
 
 import HsSyn
 import DataCon
-import MachOp
-import SMRep
 import CoreUtils
 import Id
 import Literal
 import Module
 import Name
 import Type
+import TyCon
 import Coercion
 import TcType
+import Var
 
+import CmmExpr
+import CmmUtils
 import HscTypes
 import ForeignCall
 import TysWiredIn
@@ -76,27 +71,28 @@ dsForeigns []
 dsForeigns fos = do
     fives <- mapM do_ldecl fos
     let
-        (hs, cs, hdrs, idss, bindss) = unzip5 fives
+        (hs, cs, idss, bindss) = unzip4 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)),
+             (vcat cs $$ vcat fe_init_code),
            (concat bindss))
   where
    do_ldecl (L loc decl) = putSrcSpanDs loc (do_decl decl)
             
    do_decl (ForeignImport id _ spec) = do
       traceIf (text "fi start" <+> ppr id)
-      (bs, h, c, mbhd) <- dsFImport (unLoc id) spec
+      (bs, h, c) <- dsFImport (unLoc id) spec
       traceIf (text "fi end" <+> ppr id)
-      return (h, c, maybeToList mbhd, [], bs)
+      return (h, c, [], bs)
 
    do_decl (ForeignExport (L _ id) _ (CExport (CExportStatic ext_nm cconv))) = do
       (h, c, _, _) <- dsFExport id (idType id) ext_nm cconv False
-      return (h, c, [], [id], [])
+      return (h, c, [id], [])
+
+   do_decl d = pprPanic "dsForeigns/do_decl" (ppr d)
 \end{code}
 
 
@@ -127,51 +123,58 @@ because it exposes the boxing to the call site.
 \begin{code}
 dsFImport :: Id
          -> ForeignImport
-         -> DsM ([Binding], SDoc, SDoc, Maybe FastString)
-dsFImport id (CImport cconv safety header lib spec) = do
-    (ids, h, c) <- dsCImport id spec cconv safety no_hdrs
-    return (ids, h, c, if no_hdrs then Nothing else Just header)
-  where
-    no_hdrs = nullFS header
+         -> DsM ([Binding], SDoc, SDoc)
+dsFImport id (CImport cconv safety _ _ spec) = do
+    (ids, h, c) <- dsCImport id spec cconv safety
+    return (ids, h, c)
 
   -- FIXME: the `lib' field is needed for .NET ILX generation when invoking
   --       routines that are external to the .NET runtime, but GHC doesn't
   --       support such calls yet; if `nullFastString lib', the value was not given
 dsFImport id (DNImport spec) = do
-    (ids, h, c) <- dsFCall id (DNCall spec) True {- No headers -}
-    return (ids, h, c, Nothing)
+    (ids, h, c) <- dsFCall id (DNCall spec)
+    return (ids, h, c)
 
 dsCImport :: Id
          -> CImportSpec
          -> CCallConv
          -> Safety
-         -> Bool       -- True <=> no headers in the f.i decl
          -> DsM ([Binding], SDoc, SDoc)
-dsCImport id (CLabel cid) _ _ no_hdrs = do
-   (resTy, foRhs) <- resultWrapper (idType id)
+dsCImport id (CLabel cid) cconv _ = do
+   let ty = idType id
+       fod = case splitTyConApp_maybe (repType ty) of
+             Just (tycon, _)
+              | tyConUnique tycon == funPtrTyConKey ->
+                 IsFunction
+             _ -> IsData
+   (resTy, foRhs) <- resultWrapper ty
    ASSERT(fromJust resTy `coreEqType` addrPrimTy)    -- typechecker ensures this
-    let rhs = foRhs (mkLit (MachLabel cid Nothing)) in
-    return ([(setImpInline no_hdrs id, rhs)], empty, empty)
-dsCImport id (CFunction target) cconv safety no_hdrs
-  = dsFCall id (CCall (CCallSpec target cconv safety)) no_hdrs
-dsCImport id CWrapper cconv _ _
+    let
+        rhs = foRhs (Lit (MachLabel cid stdcall_info fod))
+        stdcall_info = fun_type_arg_stdcall_info cconv ty
+    in
+    return ([(id, rhs)], empty, empty)
+
+dsCImport id (CFunction target) cconv@PrimCallConv safety
+  = dsPrimCall id (CCall (CCallSpec target cconv safety))
+dsCImport id (CFunction target) cconv safety
+  = dsFCall id (CCall (CCallSpec target cconv safety))
+dsCImport id CWrapper cconv _
   = dsFExportDynamic id cconv
 
-setImpInline :: Bool   -- True <=> No #include headers 
-                       -- in the foreign import declaration
-            -> Id -> Id
--- If there is a #include header in the foreign import
--- we make the worker non-inlinable, because we currently
--- don't keep the #include stuff in the CCallId, and hence
--- it won't be visible in the importing module, which can be
--- fatal. 
--- (The #include stuff is just collected from the foreign import
---  decls in a module.)
--- If you want to do cross-module inlining of the c-calls themselves,
--- put the #include stuff in the package spec, not the foreign 
--- import decl.
-setImpInline True  id = id
-setImpInline False id = id `setInlinePragma` NeverActive
+-- For stdcall labels, if the type was a FunPtr or newtype thereof,
+-- then we need to calculate the size of the arguments in order to add
+-- the @n suffix to the label.
+fun_type_arg_stdcall_info :: CCallConv -> Type -> Maybe Int
+fun_type_arg_stdcall_info StdCallConv ty
+  | Just (tc,[arg_ty]) <- splitTyConApp_maybe (repType ty),
+    tyConUnique tc == funPtrTyConKey
+  = let
+       (_tvs,sans_foralls)        = tcSplitForAllTys arg_ty
+       (fe_arg_tys, _orig_res_ty) = tcSplitFunTys sans_foralls
+    in Just $ sum (map (widthInBytes . typeWidth . typeCmmType . getPrimTyOf) fe_arg_tys)
+fun_type_arg_stdcall_info _other_conv _
+  = Nothing
 \end{code}
 
 
@@ -182,7 +185,8 @@ setImpInline False id = id `setInlinePragma` NeverActive
 %************************************************************************
 
 \begin{code}
-dsFCall fn_id fcall no_hdrs = do
+dsFCall :: Id -> ForeignCall -> DsM ([(Id, Expr TyVar)], SDoc, SDoc)
+dsFCall fn_id fcall = do
     let
         ty                   = idType fn_id
         (tvs, fun_ty)        = tcSplitForAllTys ty
@@ -207,7 +211,6 @@ dsFCall fn_id fcall no_hdrs = do
 
         augmentResultDs
           | forDotnet = do
-                err_res <- newSysLocalDs addrPrimTy
                 return (\ (mb_res_ty, resWrap) ->
                               case mb_res_ty of
                                 Nothing -> (Just (mkTyConApp (tupleTyCon Unboxed 1)
@@ -229,8 +232,7 @@ dsFCall fn_id fcall no_hdrs = do
         worker_ty     = mkForAllTys tvs (mkFunTys (map idType work_arg_ids) ccall_result_ty)
         the_ccall_app = mkFCall ccall_uniq fcall val_args ccall_result_ty
         work_rhs      = mkLams tvs (mkLams work_arg_ids the_ccall_app)
-        work_id       = setImpInline no_hdrs $  -- See comments with setImpInline
-                        mkSysLocal FSLIT("$wccall") work_uniq worker_ty
+        work_id       = mkSysLocal (fsLit "$wccall") work_uniq worker_ty
 
         -- Build the wrapper
         work_app     = mkApps (mkVarApps (Var work_id) tvs) val_args
@@ -243,6 +245,39 @@ dsFCall fn_id fcall no_hdrs = do
 
 %************************************************************************
 %*                                                                     *
+\subsection{Primitive calls}
+%*                                                                     *
+%************************************************************************
+
+This is for `@foreign import prim@' declarations.
+
+Currently, at the core level we pretend that these primitive calls are
+foreign calls. It may make more sense in future to have them as a distinct
+kind of Id, or perhaps to bundle them with PrimOps since semantically and
+for calling convention they are really prim ops.
+
+\begin{code}
+dsPrimCall :: Id -> ForeignCall -> DsM ([(Id, Expr TyVar)], SDoc, SDoc)
+dsPrimCall fn_id fcall = do
+    let
+        ty                   = idType fn_id
+        (tvs, fun_ty)        = tcSplitForAllTys ty
+        (arg_tys, io_res_ty) = tcSplitFunTys fun_ty
+                -- Must use tcSplit* functions because we want to
+                -- see that (IO t) in the corner
+
+    args <- newSysLocalsDs arg_tys
+
+    ccall_uniq <- newUnique
+    let
+        call_app = mkFCall ccall_uniq fcall (map Var args) io_res_ty
+        rhs      = mkLams tvs (mkLams args call_app)
+    return ([(fn_id, rhs)], empty, empty)
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
 \subsection{Foreign export}
 %*                                                                     *
 %************************************************************************
@@ -254,7 +289,7 @@ The function that does most of the work for `@foreign export@' declarations.
 For each `@foreign export foo@' in a module M we generate:
 \begin{itemize}
 \item a C function `@foo@', which calls
-\item a Haskell stub `@M.$ffoo@', which calls
+\item a Haskell stub `@M.\$ffoo@', which calls
 \end{itemize}
 the user-written Haskell function `@M.foo@'.
 
@@ -288,7 +323,7 @@ dsFExport fn_id ty ext_name cconv isDyn=  do
     (res_ty,             -- t
      is_IO_res_ty) <-    -- Bool
         case tcSplitIOType_maybe orig_res_ty of
-           Just (ioTyCon, res_ty, co) -> return (res_ty, True)
+           Just (_ioTyCon, res_ty, _co) -> return (res_ty, True)
                    -- The function already returns IO t
                    -- ToDo: what about the coercion?
            Nothing                    -> return (orig_res_ty, False) 
@@ -360,12 +395,12 @@ dsFExportDynamic id cconv = do
          -}
         adj_args      = [ mkIntLitInt (ccallConvToInt cconv)
                         , Var stbl_value
-                        , mkLit (MachLabel fe_nm mb_sz_args)
-                        , mkLit (mkStringLit typestring)
+                        , Lit (MachLabel fe_nm mb_sz_args IsFunction)
+                        , Lit (mkMachString typestring)
                         ]
           -- name of external entry point providing these services.
           -- (probably in the RTS.) 
-        adjustor   = FSLIT("createAdjustor")
+        adjustor   = fsLit "createAdjustor"
         
           -- 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
@@ -387,7 +422,7 @@ dsFExportDynamic id cconv = do
                         , Lam stbl_value ccall_adj
                         ]
 
-        fed = (id `setInlinePragma` NeverActive, io_app)
+        fed = (id `setInlineActivation` NeverActive, io_app)
                -- Never inline the f.e.d. function, because the litlit
                -- might not be in scope in other modules.
 
@@ -429,24 +464,31 @@ mkFExportCBits :: FastString
                  )
 mkFExportCBits c_nm maybe_target arg_htys res_hty is_IO_res_ty cc 
  = (header_bits, c_bits, type_string,
-    sum [ machRepByteWidth rep | (_,_,_,rep) <- aug_arg_info] -- all the args
+    sum [ widthInBytes (typeWidth rep) | (_,_,_,rep) <- aug_arg_info] -- all the args
+        -- NB. the calculation here isn't strictly speaking correct.
+        -- We have a primitive Haskell type (eg. Int#, Double#), and
+        -- we want to know the size, when passed on the C stack, of
+        -- the associated C type (eg. HsInt, HsDouble).  We don't have
+        -- this information to hand, but we know what GHC's conventions
+        -- are for passing around the primitive Haskell types, so we
+        -- use that instead.  I hope the two coincide --SDM
     )
  where
   -- list the arguments to the C function
   arg_info :: [(SDoc,          -- arg name
                SDoc,           -- C type
                Type,           -- Haskell type
-               MachRep)]       -- the MachRep
+               CmmType)]       -- the CmmType
   arg_info  = [ let stg_type = showStgType ty in
                 (arg_cname n stg_type,
                  stg_type,
                  ty, 
-                typeMachRep (getPrimTyOf ty))
+                typeCmmType (getPrimTyOf ty))
              | (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))
+                      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
@@ -468,7 +510,7 @@ mkFExportCBits c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
 
   stable_ptr_arg = 
        (text "the_stableptr", text "StgStablePtr", undefined,
-        typeMachRep (mkStablePtrPrimTy alphaTy))
+        typeCmmType (mkStablePtrPrimTy alphaTy))
 
   -- stuff to do with the return type of the C function
   res_hty_is_unit = res_hty `coreEqType` unitTy        -- Look through any newtypes
@@ -480,17 +522,21 @@ mkFExportCBits c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
   pprCconv = case cc of
                CCallConv   -> empty
                StdCallConv -> text (ccallConvAttribute cc)
+                _           -> panic ("mkFExportCBits/pprCconv " ++ showPpr cc)
+
+  header_bits = ptext (sLit "extern") <+> fun_proto <> semi
 
-  header_bits = ptext SLIT("extern") <+> fun_proto <> semi
+  fun_args
+    | null aug_arg_info = text "void"
+    | otherwise         = hsep $ punctuate comma
+                               $ map (\(nm,ty,_,_) -> ty <+> nm) aug_arg_info
 
   fun_proto
     | libffi
-      = ptext SLIT("void") <+> ftext c_nm <> 
-          parens (ptext SLIT("void *cif STG_UNUSED, void* resp, void** args, void* the_stableptr"))
+      = 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)))
+      = cResType <+> pprCconv <+> ftext c_nm <> parens fun_args
 
   -- the target which will form the root of what we ask rts_evalIO to run
   the_cfun
@@ -531,33 +577,33 @@ mkFExportCBits c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
     fun_proto  $$
     vcat 
      [ lbrace
-     ,   ptext SLIT("Capability *cap;")
+     ,   ptext (sLit "Capability *cap;")
      ,   declareResult
      ,   declareCResult
      ,   text "cap = rts_lock();"
          -- create the application + perform it.
-     ,   ptext SLIT("cap=rts_evalIO") <> parens (
+     ,   ptext (sLit "cap=rts_evalIO") <> parens (
                cap <>
-               ptext SLIT("rts_apply") <> parens (
+               ptext (sLit "rts_apply") <> parens (
                    cap <>
                    text "(HaskellObj)"
                 <> ptext (if is_IO_res_ty 
-                               then SLIT("runIO_closure")
-                               else SLIT("runNonIO_closure"))
+                               then (sLit "runIO_closure")
+                               else (sLit "runNonIO_closure"))
                 <> comma
                 <> expr_to_run
                ) <+> comma
               <> text "&ret"
             ) <> semi
-     ,   ptext SLIT("rts_checkSchedStatus") <> parens (doubleQuotes (ftext c_nm)
+     ,   ptext (sLit "rts_checkSchedStatus") <> parens (doubleQuotes (ftext c_nm)
                                                <> comma <> text "cap") <> semi
      ,   assignCResult
-     ,   ptext SLIT("rts_unlock(cap);")
+     ,   ptext (sLit "rts_unlock(cap);")
      ,   if res_hty_is_unit then empty
             else if libffi 
                   then char '*' <> parens (cResType <> char '*') <> 
-                       ptext SLIT("resp = cret;")
-                  else ptext SLIT("return cret;")
+                       ptext (sLit "resp = cret;")
+                  else ptext (sLit "return cret;")
      , rbrace
      ]
 
@@ -582,15 +628,6 @@ foreignExportInitialiser hs_fn =
     ]
 
 
--- NB. the calculation here isn't strictly speaking correct.
--- We have a primitive Haskell type (eg. Int#, Double#), and
--- we want to know the size, when passed on the C stack, of
--- the associated C type (eg. HsInt, HsDouble).  We don't have
--- this information to hand, but we know what GHC's conventions
--- are for passing around the primitive Haskell types, so we
--- use that instead.  I hope the two coincide --SDM
-typeMachRep ty = argMachRep (typeCgRep ty)
-
 mkHObj :: Type -> SDoc
 mkHObj t = text "rts_mk" <> text (showFFIType t)
 
@@ -607,6 +644,8 @@ showFFIType t = getOccString (getName tc)
            Just (tc,_) -> tc
            Nothing     -> pprPanic "showFFIType" (ppr t)
 
+insertRetAddr :: CCallConv -> [(SDoc, SDoc, Type, CmmType)]
+                           -> [(SDoc, SDoc, Type, CmmType)]
 #if !defined(x86_64_TARGET_ARCH)
 insertRetAddr CCallConv args = ret_addr_arg : args
 insertRetAddr _ args = args
@@ -616,16 +655,19 @@ insertRetAddr _ args = args
 -- need to flush a register argument to the stack (See rts/Adjustor.c for
 -- details).
 insertRetAddr CCallConv args = go 0 args
-  where  go 6 args = ret_addr_arg : args
+  where  go :: Int -> [(SDoc, SDoc, Type, CmmType)]
+                   -> [(SDoc, SDoc, Type, CmmType)]
+         go 6 args = ret_addr_arg : args
         go n (arg@(_,_,_,rep):args)
-         | I64 <- rep = arg : go (n+1) args
+         | cmmEqType_ignoring_ptrhood rep b64 = arg : go (n+1) args
          | otherwise  = arg : go n     args
-        go n [] = []
+        go _ [] = []
 insertRetAddr _ args = args
 #endif
 
+ret_addr_arg :: (SDoc, SDoc, Type, CmmType)
 ret_addr_arg = (text "original_return_addr", text "void*", undefined, 
-               typeMachRep addrPrimTy)
+               typeCmmType addrPrimTy)
 
 -- This function returns the primitive type associated with the boxed
 -- type argument to a foreign export (eg. Int ==> Int#).
@@ -656,7 +698,7 @@ primTyDescChar ty
      WordRep     -> unsigned_word
      Int64Rep    -> 'L'
      Word64Rep   -> 'l'
-     AddrRep     -> unsigned_word
+     AddrRep     -> 'p'
      FloatRep    -> 'f'
      DoubleRep   -> 'd'
      _           -> pprPanic "primTyDescChar" (ppr ty)