% % (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 -- (base)name of output file -> [HTopDecl] -- Haskell decls to derive Hugs stubs from. -> 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 -- catches (IO 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 -- hack to cope with constants | 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}