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"
import HsSyn
import DataCon
-import MachOp
-import SMRep
import CoreUtils
+import CoreUnfold
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
import FastString
import Config
import Constants
-
+import OrdList
import Data.Maybe
import Data.List
\end{code}
-- the occurrence analyser will sort it all out
dsForeigns :: [LForeignDecl Id]
- -> DsM (ForeignStubs, [Binding])
+ -> DsM (ForeignStubs, OrdList Binding)
dsForeigns []
- = return (NoStubs, [])
+ = return (NoStubs, nilOL)
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)),
- (concat bindss))
+ (vcat cs $$ vcat fe_init_code),
+ foldr (appOL . toOL) nilOL 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], [])
\end{code}
\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
-
- -- 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)
+ -> DsM ([Binding], SDoc, SDoc)
+dsFImport id (CImport cconv safety _ spec) = do
+ (ids, h, c) <- dsCImport id spec cconv safety
+ 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}
%************************************************************************
\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
let
work_arg_ids = [v | Var v <- val_args] -- All guaranteed to be vars
- forDotnet =
- case fcall of
- DNCall{} -> True
- _ -> False
-
- topConDs
- | forDotnet = Just <$> dsLookupGlobalId checkDotnetResName
- | otherwise = return Nothing
-
- augmentResultDs
- | forDotnet = do
- err_res <- newSysLocalDs addrPrimTy
- return (\ (mb_res_ty, resWrap) ->
- case mb_res_ty of
- Nothing -> (Just (mkTyConApp (tupleTyCon Unboxed 1)
- [ addrPrimTy ]),
- resWrap)
- Just x -> (Just (mkTyConApp (tupleTyCon Unboxed 2)
- [ x, addrPrimTy ]),
- resWrap))
- | otherwise = return id
-
- augment <- augmentResultDs
- topCon <- topConDs
- (ccall_result_ty, res_wrapper) <- boxResult augment topCon io_res_ty
+ (ccall_result_ty, res_wrapper) <- boxResult io_res_ty
ccall_uniq <- newUnique
work_uniq <- newUnique
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
wrapper_body = foldr ($) (res_wrapper work_app) arg_wrappers
- wrap_rhs = mkInlineMe (mkLams (tvs ++ args) wrapper_body)
+ wrap_rhs = mkLams (tvs ++ args) wrapper_body
+ fn_id_w_inl = fn_id `setIdUnfolding` mkInlineUnfolding (Just (length args)) wrap_rhs
- return ([(work_id, work_rhs), (fn_id, wrap_rhs)], empty, empty)
+ return ([(work_id, work_rhs), (fn_id_w_inl, wrap_rhs)], empty, empty)
\end{code}
%************************************************************************
%* *
+\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}
%* *
%************************************************************************
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@'.
(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)
-}
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
, 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.
)
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
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
cResType | res_hty_is_unit = text "void"
| otherwise = showStgType res_hty
+ -- when the return type is integral and word-sized or smaller, it
+ -- must be assigned as type ffi_arg (#3516). To see what type
+ -- libffi is expecting here, take a look in its own testsuite, e.g.
+ -- libffi/testsuite/libffi.call/cls_align_ulonglong.c
+ ffi_cResType
+ | is_ffi_arg_type = text "ffi_arg"
+ | otherwise = cResType
+ where
+ res_ty_key = getUnique (getName (typeTyCon res_hty))
+ is_ffi_arg_type = res_ty_key `notElem`
+ [floatTyConKey, doubleTyConKey,
+ int64TyConKey, word64TyConKey]
+
-- Now we can cook up the prototype for the exported function.
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
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);")
- , 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 "rts_unlock(cap);")
+ , ppUnless res_hty_is_unit $
+ if libffi
+ then char '*' <> parens (ffi_cResType <> char '*') <>
+ ptext (sLit "resp = cret;")
+ else ptext (sLit "return cret;")
, rbrace
]
]
--- 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)
showStgType t = text "Hs" <> text (showFFIType t)
showFFIType :: Type -> String
-showFFIType t = getOccString (getName tc)
- where
- tc = case tcSplitTyConApp_maybe (repType t) of
- Just (tc,_) -> tc
- Nothing -> pprPanic "showFFIType" (ppr t)
+showFFIType t = getOccString (getName (typeTyCon t))
+
+typeTyCon :: Type -> TyCon
+typeTyCon ty = case tcSplitTyConApp_maybe (repType ty) of
+ Just (tc,_) -> tc
+ Nothing -> pprPanic "DsForeign.typeTyCon" (ppr ty)
+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
-- 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#).
WordRep -> unsigned_word
Int64Rep -> 'L'
Word64Rep -> 'l'
- AddrRep -> unsigned_word
+ AddrRep -> 'p'
FloatRep -> 'f'
DoubleRep -> 'd'
_ -> pprPanic "primTyDescChar" (ppr ty)