%
% (c) 1999, University of Glasgow & Sigbjorn Finne
%
Backend which emits C stubs compatible with Hugs' 'primitive' declarations.
\begin{code}
module HugsCodeGen ( hugsCodeGen ) where
import AbstractH
import AbsHUtils ( splitFunTys, isIOTy )
import PP
import BasicTypes
import Utils ( traceIf )
import Opts ( optVerbose, optLongLongIsInteger,
optGenHeader, optOneModulePerInterface
)
import List ( nub, intersperse )
import Utils ( notNull, dropSuffix )
\end{code}
\begin{code}
hugsCodeGen :: String
-> [HTopDecl]
-> String
hugsCodeGen c_nm ls = showPPDoc (hCode c_nm ls) ([],[])
type HugsStubCode = PPDoc ([(String,Int)],[String])
getStubEnv :: ([(String,Int)] -> HugsStubCode) -> HugsStubCode
getStubEnv cont env@(e,_) = cont e env
getDllEnv :: ([String] -> HugsStubCode) -> HugsStubCode
getDllEnv cont env@(_,e) = cont e env
addToStubEnv :: String -> Type -> HugsStubCode -> HugsStubCode
addToStubEnv nm ty cont (env,dlls) = cont (((nm,arity):env),dlls)
where
(args,res) = splitFunTys ty
sizeofArg (TyCon tc) | optLongLongIsInteger && qName tc == "Integer" = (2::Int)
sizeofArg _ = 1
size_args = sum (map sizeofArg args)
arity
| isIOTy res = size_args + 2
| otherwise = size_args
addToDllEnv :: String -> HugsStubCode -> HugsStubCode
addToDllEnv nm cont (env,dlls) = cont (env,nm:dlls)
hCode :: String -> [HTopDecl] -> HugsStubCode
hCode c_nm xs = whizz xs
where
whizz [] =
getDllEnv $ \ dlls ->
let dlls_real = nub (filter notNull dlls) in
traceIf (optVerbose && notNull dlls_real)
("\nStubs depend on entry points from the following DLLs/libraries:\n " ++
showList dlls_real (
"\nyou may need to adjust your command-line when compiling the stubs to" ++
"\ntake this into account.")) $
getStubEnv $ \ env ->
genTrailer env
whizz (HLit _ : ls) = whizz ls
whizz (CLit s : ls)
| not optGenHeader = text s $$ whizz ls
| otherwise = whizz ls
whizz (HInclude s : ls) = text "#include" <+> text (escapeString s) $$
whizz ls
whizz (HMod hm : ls) = hMod c_nm hm (whizz ls)
escapeString s@('"':_) = s
escapeString s@('<':_) = s
escapeString s = show s
hMod :: String -> HModule -> HugsStubCode -> HugsStubCode
hMod c_nm (HModule _ _ _ _ d) cont
| optGenHeader && not optOneModulePerInterface =
text "#include" <+> text (show (dropSuffix c_nm ++ ".h")) $$ code
| otherwise = code
where
code = hDecl d cont
hDecl :: HDecl -> HugsStubCode -> HugsStubCode
hDecl (AndDecl d1 d2) cont = hDecl d1 (hDecl d2 cont)
hDecl (Primitive _ cc lspec nm ty _ c_args c_res) cont =
addToDllEnv dll_name $
addToStubEnv nm ty $
tdefFun lspec cc c_args c_res $$
primHeader nm $$
lbrace $$
argAndResDecls ty c_args c_res $$ argAssign ty c_args $$
performCall False lspec c_args ty $$
pushResult c_res ty $$
rbrace $$
cont
where
(dll_name, _, _, _) = lspec
tdefFun (_,Nothing,_,_) _ _ _ = empty
tdefFun (_,Just _,fnm,_) cconv cargs cres =
text "extern" <+> text (snd cres) <+> ppCallConv True cconv <+> text fnm <+>
parens (hsep (intersperse comma (map (text.snd) cargs))) <> semi
hDecl (PrimCast cc nm ty _ c_args c_res) cont =
addToStubEnv nm ty $
tdefFunTy nm cc c_args c_res $$
primHeader nm $$
lbrace $$
argAndResDecls ty c_args c_res $$
text (nm++"__funptr __funptr__;") $$
argAssign ty c_args $$
text ("__funptr__ = ("++nm++"__funptr)arg0;") $$
performCall True ("", Nothing, "__funptr__", Nothing) c_args ty $$
pushResult c_res ty $$
rbrace $$
cont
hDecl (Include s) cont = text ("#include " ++ s) $$ cont
hDecl (CCode s) cont
| not optGenHeader = text s $$ cont
| otherwise = cont
hDecl _ cont = cont
\end{code}
\begin{code}
primHeader :: Name -> HugsStubCode
primHeader nm = text "primFun" <> parens (text nm)
argAndResDecls :: Type -> [(Bool,String)] -> (Bool,String) -> HugsStubCode
argAndResDecls ty c_args c_res = ppDecls (zipWith declArg [0..] c_args) $$ declRes
where
(_, res) = splitFunTys ty
declRes
| noResult = empty
| otherwise = text (snd c_res) <+> text "res" <> semi
declArg n (is_struct,t)
| is_struct = text t <> char '*' <+> ppArg False n
| otherwise = text t <+> ppArg False n
noResult =
case res of
(TyApply (TyCon _) [TyCon tc]) -> qName tc == "()"
_ -> False
ppArg :: Bool -> Int -> HugsStubCode
ppArg isStructTy n
| isStructTy = text ("*arg"++show n)
| otherwise = text ("arg"++show n)
ppCTy :: Type -> HugsStubCode
ppCTy ty =
case ty of
TyVar _ tv -> text (degrokNm (qName tv))
TyCon tc
| qName tc == "()" -> empty
| otherwise -> text (degrokNm (qName tc))
TyApply (TyCon tc) [_] | qName tc == "StablePtr" -> text "StablePtr"
TyApply (TyCon tc) [_] | qName tc == "Ptr" -> text "Addr"
TyApply (TyCon tc) [_] | qName tc == "FunPtr" -> text "Addr"
TyApply (TyCon tc) [_] | qName tc == "ForeignPtr" -> text "Foreign"
TyApply _ [t] -> ppCTy t
TyApply t _ -> ppCTy t
TyList _ -> text "void*"
TyTuple _ -> text "void*"
TyFun _ _ -> text "void*"
TyCtxt _ t -> ppCTy t
where
degrokNm nm =
case nm of
'I':'n':'t':_ -> "Int"
'W':'o':'r':'d':_ -> "Word"
'F':'o':_ -> "Foreign"
"Char" -> "Char"
"Double" -> "Double"
"Float" -> "Float"
_ -> "Addr"
argAssign :: Type -> [(Bool,String)] -> HugsStubCode
argAssign ty c_args = ppDecls (zipWith3 declArg [0..] args c_args)
where
(args, _) = splitFunTys ty
declArg n t@(TyCon tc) (_, c_ty)
| optLongLongIsInteger && qName tc == "Integer"
= ppArg False n <+> equals <+>
parens (text c_ty) <>
parens (text"hugs->get" <> ppCTy t <> text "()") <> semi $$
ppArg False n <+> text ">>= 32" <> semi $$
ppArg False n <+> text "+=" <+> parens (text c_ty) <>
parens (text"hugs->get" <> ppCTy t <> text "()")
declArg n t (is_struct, c_ty)
= ppArg False n <+> equals <+>
parens ppr_c_ty <>
parens (text"hugs->get" <> ppCTy t <> text "()")
where
ppr_c_ty
| is_struct = text c_ty <> char '*'
| otherwise = text c_ty
\end{code}
\begin{code}
performCall :: Bool -> LocSpec -> [(Bool,String)] -> Type -> HugsStubCode
performCall is_dyn (_,_, fun, _) c_args ty =
ppAssign res <> text fun <> pp_fun_args <> semi
where
(args, res) = splitFunTys ty
pp_fun_args
| not (isIOTy res) && null args = empty
| otherwise = ppTuple fun_args
fun_args
| is_dyn = tail funArgs
| otherwise = funArgs
funArgs = zipWith funArg [0..] c_args
funArg n (isStructTy,_) = ppArg isStructTy n
ppAssign (TyApply (TyCon _) [TyCon tc])
| qName tc == "()" = empty
ppAssign _ = text "res" <+> equals
\end{code}
\begin{code}
pushResult :: (Bool,String) -> Type -> HugsStubCode
pushResult (isStructTy, c_ty) ty =
assignRes $$
if isPure then
empty
else
text "hugs_returnIO" <> parens no_of_args <> semi
where
(_, res) = splitFunTys ty
isPure =
case res of
TyApply _ _ -> False
_ -> True
noResult =
case res of
(TyApply (TyCon _) [TyCon tc]) -> qName tc == "()"
_ -> False
isIntegerRes =
case res of
(TyApply (TyCon _) [TyCon tc]) -> qName tc == "Integer"
_ -> False
assignRes
| noResult = empty
| isIntegerRes = text "hugs->putInt" <> parens ( text "(unsigned int)res" ) <> semi $$
text "hugs->putInt" <> parens ( text "(unsigned int)(res >> 32)" ) <> semi
| otherwise = text "hugs->put" <> (ppCTy res) <>
parens (the_result) <> semi
the_result
| isStructTy = text "copyBytes" <> parens (
text "sizeof" <> parens (text c_ty) <>
text ", &res")
| otherwise = parens (text c_ty) <> text "res"
no_of_args
| noResult = text "0"
| isIntegerRes = text "2"
| otherwise = text "1"
\end{code}
\begin{code}
tdefFunTy :: Name -> CallConv -> [(Bool,String)] -> (Bool,String) -> HugsStubCode
tdefFunTy nm cc c_args c_res =
text "typedef" <+> ppResultTy <+>
parens ( ppCallConv True cc <+> char '*' <+>
text (nm++"__funptr")) <+>
ppTuple ppArgs <> semi
where
ppResultTy = text (snd c_res)
ppArgs = zipWith pp_arg [1..] (tail c_args)
pp_arg n (_, t) = text t <+> ppArg False n
\end{code}
\begin{code}
genTrailer :: [(String,Int)] -> HugsStubCode
genTrailer [] = empty
genTrailer ls =
genPrimTable ls $$
text "static struct hugs_primInfo prims = { 0, primTable, 0 };" $$
text "#ifdef __cplusplus" $$
text "extern \"C\" {" $$
text "#endif" $$
text "DLLEXPORT(void) initModule(HugsAPI4 *);" $$
text "DLLEXPORT(void) initModule(HugsAPI4 *hugsAPI) {" $$
text " hugs = hugsAPI;" $$
text " hugs->registerPrims(&prims);" $$
text "}" $$
text "#ifdef __cplusplus" $$
text "}" $$
text "#endif"
genPrimTable :: [(String,Int)] -> HugsStubCode
genPrimTable ls =
text "static struct hugs_primitive primTable[] = {" $$
nest 2 (vsep (map genPrim ls)) $$
nest 2 (text "{0,0,0}") $$
text "};"
where
genPrim (nm, arity) =
lbrace <> text (show nm) <> comma <> text (show arity) <> comma <>
text nm <> text "},"
\end{code}