%
% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
%
\section[Foreign]{Foreign calls}
\begin{code}
module ForeignCall (
ForeignCall(..), isSafeForeignCall,
Safety(..), playSafe, playInterruptible,
CExportSpec(..), CLabelString, isCLabelString, pprCLabelString,
CCallSpec(..),
CCallTarget(..), isDynamicTarget,
CCallConv(..), defaultCCallConv, ccallConvToInt, ccallConvAttribute,
Header(..), CType(..),
) where
import FastString
import Binary
import Outputable
import Module
import Data.Char
import Data.Data
\end{code}
%************************************************************************
%* *
\subsubsection{Data types}
%* *
%************************************************************************
\begin{code}
newtype ForeignCall = CCall CCallSpec
deriving Eq
isSafeForeignCall :: ForeignCall -> Bool
isSafeForeignCall (CCall (CCallSpec _ _ safe)) = playSafe safe
instance Outputable ForeignCall where
ppr (CCall cc) = ppr cc
\end{code}
\begin{code}
data Safety
= PlaySafe
| PlayInterruptible
| PlayRisky
deriving ( Eq, Show, Data, Typeable )
instance Outputable Safety where
ppr PlaySafe = ptext (sLit "safe")
ppr PlayInterruptible = ptext (sLit "interruptible")
ppr PlayRisky = ptext (sLit "unsafe")
playSafe :: Safety -> Bool
playSafe PlaySafe = True
playSafe PlayInterruptible = True
playSafe PlayRisky = False
playInterruptible :: Safety -> Bool
playInterruptible PlayInterruptible = True
playInterruptible _ = False
\end{code}
%************************************************************************
%* *
\subsubsection{Calling C}
%* *
%************************************************************************
\begin{code}
data CExportSpec
= CExportStatic
CLabelString
CCallConv
deriving (Data, Typeable)
data CCallSpec
= CCallSpec CCallTarget
CCallConv
Safety
deriving( Eq )
\end{code}
The call target:
\begin{code}
data CCallTarget
= StaticTarget
CLabelString
(Maybe PackageId)
Bool
| DynamicTarget
deriving( Eq, Data, Typeable )
isDynamicTarget :: CCallTarget -> Bool
isDynamicTarget DynamicTarget = True
isDynamicTarget _ = False
\end{code}
Stuff to do with calling convention:
ccall: Caller allocates parameters, *and* deallocates them.
stdcall: Caller allocates parameters, callee deallocates.
Function name has @N after it, where N is number of arg bytes
e.g. _Foo@8
ToDo: The stdcall calling convention is x86 (win32) specific,
so perhaps we should emit a warning if it's being used on other
platforms.
See: http://www.programmersheaven.com/2/Calling-conventions
\begin{code}
data CCallConv = CCallConv | CApiConv | StdCallConv | PrimCallConv
deriving (Eq, Data, Typeable)
instance Outputable CCallConv where
ppr StdCallConv = ptext (sLit "stdcall")
ppr CCallConv = ptext (sLit "ccall")
ppr CApiConv = ptext (sLit "capi")
ppr PrimCallConv = ptext (sLit "prim")
defaultCCallConv :: CCallConv
defaultCCallConv = CCallConv
ccallConvToInt :: CCallConv -> Int
ccallConvToInt StdCallConv = 0
ccallConvToInt CCallConv = 1
ccallConvToInt CApiConv = panic "ccallConvToInt CApiConv"
ccallConvToInt (PrimCallConv {}) = panic "ccallConvToInt PrimCallConv"
\end{code}
Generate the gcc attribute corresponding to the given
calling convention (used by PprAbsC):
\begin{code}
ccallConvAttribute :: CCallConv -> SDoc
ccallConvAttribute StdCallConv = text "__attribute__((__stdcall__))"
ccallConvAttribute CCallConv = empty
ccallConvAttribute CApiConv = empty
ccallConvAttribute (PrimCallConv {}) = panic "ccallConvAttribute PrimCallConv"
\end{code}
\begin{code}
type CLabelString = FastString
pprCLabelString :: CLabelString -> SDoc
pprCLabelString lbl = ftext lbl
isCLabelString :: CLabelString -> Bool
isCLabelString lbl
= all ok (unpackFS lbl)
where
ok c = isAlphaNum c || c == '_' || c == '.'
\end{code}
Printing into C files:
\begin{code}
instance Outputable CExportSpec where
ppr (CExportStatic str _) = pprCLabelString str
instance Outputable CCallSpec where
ppr (CCallSpec fun cconv safety)
= hcat [ ifPprDebug callconv, ppr_fun fun ]
where
callconv = text "{-" <> ppr cconv <> text "-}"
gc_suf | playSafe safety = text "_GC"
| otherwise = empty
ppr_fun (StaticTarget fn mPkgId isFun)
= text (if isFun then "__pkg_ccall"
else "__pkg_ccall_value")
<> gc_suf
<+> (case mPkgId of
Nothing -> empty
Just pkgId -> ppr pkgId)
<+> pprCLabelString fn
ppr_fun DynamicTarget
= text "__dyn_ccall" <> gc_suf <+> text "\"\""
\end{code}
\begin{code}
newtype Header = Header FastString
deriving (Eq, Data, Typeable)
instance Outputable Header where
ppr (Header h) = quotes $ ppr h
data CType = CType (Maybe Header)
FastString
deriving (Data, Typeable)
instance Outputable CType where
ppr (CType mh ct) = hDoc <+> ftext ct
where hDoc = case mh of
Nothing -> empty
Just h -> ppr h
\end{code}
%************************************************************************
%* *
\subsubsection{Misc}
%* *
%************************************************************************
\begin{code}
instance Binary ForeignCall where
put_ bh (CCall aa) = put_ bh aa
get bh = do aa <- get bh; return (CCall aa)
instance Binary Safety where
put_ bh PlaySafe = do
putByte bh 0
put_ bh PlayInterruptible = do
putByte bh 1
put_ bh PlayRisky = do
putByte bh 2
get bh = do
h <- getByte bh
case h of
0 -> do return PlaySafe
1 -> do return PlayInterruptible
_ -> do return PlayRisky
instance Binary CExportSpec where
put_ bh (CExportStatic aa ab) = do
put_ bh aa
put_ bh ab
get bh = do
aa <- get bh
ab <- get bh
return (CExportStatic aa ab)
instance Binary CCallSpec where
put_ bh (CCallSpec aa ab ac) = do
put_ bh aa
put_ bh ab
put_ bh ac
get bh = do
aa <- get bh
ab <- get bh
ac <- get bh
return (CCallSpec aa ab ac)
instance Binary CCallTarget where
put_ bh (StaticTarget aa ab ac) = do
putByte bh 0
put_ bh aa
put_ bh ab
put_ bh ac
put_ bh DynamicTarget = do
putByte bh 1
get bh = do
h <- getByte bh
case h of
0 -> do aa <- get bh
ab <- get bh
ac <- get bh
return (StaticTarget aa ab ac)
_ -> do return DynamicTarget
instance Binary CCallConv where
put_ bh CCallConv = do
putByte bh 0
put_ bh StdCallConv = do
putByte bh 1
put_ bh PrimCallConv = do
putByte bh 2
put_ bh CApiConv = do
putByte bh 3
get bh = do
h <- getByte bh
case h of
0 -> do return CCallConv
1 -> do return StdCallConv
2 -> do return PrimCallConv
_ -> do return CApiConv
instance Binary CType where
put_ bh (CType mh fs) = do put_ bh mh
put_ bh fs
get bh = do mh <- get bh
fs <- get bh
return (CType mh fs)
instance Binary Header where
put_ bh (Header h) = put_ bh h
get bh = do h <- get bh
return (Header h)
\end{code}