%
% Copyright (C) 1997 Thomas Nordin and Alastair Reid
%
\begin{code}
module Casm
( Casm(..)
, BaseTy(..)
, lookupBaseTy, ppBaseTy, baseTyToCall, baseTyToRtn, baseToCType
, Param(..)
, ppCasm
, Target(..)
) where
import Pretty
import PrettyUtils
import Maybe( fromMaybe )
import Target( Target(..) )
\end{code}
%************************************************************************
%* *
\subsection{Data structures}
%* *
%************************************************************************
\begin{code}
data Casm
= Casm
String -- a unique label - in case we need to generate decls
Bool -- Are we going to be GC safe?
Doc -- initialisation code
Doc -- C code
--Doc -- cleanup code
[Param] -- arguments
[Param] -- results
\end{code}
\begin{code}
data BaseTy
= Int
| Char
| Bool
| Float
| Double
| PackedString
| Word
| Addr
| StablePtr
| Foreign String -- freeing function
deriving (Show)
lookupBaseTy :: String -> BaseTy
lookupBaseTy s = fromMaybe err (lookup s baseNames)
where
err = error ("unrecognised basic type %%" ++ s)
-- No entry for Foreign!
baseNames :: [(String, BaseTy)]
baseNames =
[ ("Int", Int)
, ("Char", Char)
, ("Bool", Bool)
, ("Float", Float)
, ("Double", Double)
, ("PackedString", PackedString)
, ("Word", Word)
, ("Addr", Addr)
, ("StablePtr", StablePtr)
]
ppBaseTy :: BaseTy -> Doc
ppBaseTy b = text ("%%" ++ show b)
baseToCType :: BaseTy -> String
baseToCType Int = "int"
baseToCType Char = "char"
baseToCType Bool = "int"
baseToCType Float = "float"
baseToCType Double = "double"
baseToCType PackedString = "char *"
baseToCType Word = "unsigned int"
baseToCType Addr = "void *"
baseToCType StablePtr = "StablePtr"
baseToCType (Foreign _) = "void *"
baseTyToCall :: BaseTy -> String
baseTyToCall Int = "GET_INT_VALUE(nodeptr)"
baseTyToCall Char = "GET_CHAR_VALUE(nodeptr)"
baseTyToCall Bool = "GET_BOOL_VALUE(nodeptr)"
baseTyToCall Float = "get_float_value(nodeptr)"
baseTyToCall Double = "get_double_value(nodeptr)"
baseTyToCall PackedString = "getPackedString(nodeptr)"
baseTyToCall Word = "GET_INT_VALUE(nodeptr)"
baseTyToCall Addr = "getVoidStar(nodeptr)"
baseTyToCall StablePtr = "stableInsert(getStablePtr(nodeptr))"
baseTyToCall (Foreign _) = "(derefForeignObj((ForeignObj*)GET_INT_VALUE(nodeptr)))"
baseTyToRtn :: BaseTy -> String -> String
baseTyToRtn Int v = "nhc_mkInt("++v++")"
baseTyToRtn Char v = "nhc_mkChar("++v++")"
baseTyToRtn Bool v = "nhc_mkBool("++v++")"
baseTyToRtn Float v = "nhc_mkFloat("++v++")"
baseTyToRtn Double v = "nhc_mkDouble("++v++")"
baseTyToRtn PackedString v = "nhc_mkString("++v++")"
baseTyToRtn Word v = "nhc_mkInt("++v++")"
baseTyToRtn Addr v = "nhc_mkAddr("++v++")"
baseTyToRtn StablePtr v = "nhc_mkStablePtr(stableRef("++v++"))"
baseTyToRtn (Foreign f) v = "nhc_mkCInt((int)allocForeignObj((void*)"++v++",(gcCval)"++f++",gcNow))"
\end{code}
\begin{code}
data Param =
Param
String -- Haskell Name
String -- C Expression
BaseTy -- Haskell type
deriving Show
getBaseTy :: Param -> BaseTy
getBaseTy (Param _ _ b) = b
\end{code}
%************************************************************************
%* *
\subsection{The main entry points}
%* *
%************************************************************************
ToDo:
o If the arg names overlap with the result names, we should either:
a) not redeclare the result holder (assuming the same name is used)
or
b) report an error
We should not:
c) ignore it
since it is almost certainly not what the user wanted and they
can easily rename to avoid the warning.
\begin{code}
ppCasm :: Target -> Casm -> (Doc, Doc, Doc, Doc)
ppCasm Hugs (Casm name gcsafe init ccode args results) =
( text name <+> textline [ a | Param a _ _ <- args ]
, text "primitive" <+> text name <+> text "::" <+> typ
, ppPrimDecl name
( init
$$ vcatMap unpack args
$$ ppBlock
( ccode
$$ vcatMap pack (reverse results)
$$ apply "hugs_returnIO" (text $ show $ length results) <> semi
)
)
, ppStruct [doubleQuotes $ text name, text (show arity), text name]
)
where
typ = ppType (map getBaseTy args) (map getBaseTy results)
-- The IO monad takes 2 extra arguments
arity = length args + 2
ppCasm GHC (Casm name gcsafe init ccode args results) =
( wrap (
casm
( declareResult
$$ init
$$ hsep (zipWith initArg args [0..])
$$ ppBlock
( ccode <> semi
$$ push
)
) [ text a | Param a _ _ <- args ]
$$ pop
$$ text "returnPrimIO" <+> ppTuple [ text n | (Param n _ _) <- results ]
)
, empty
, empty
, empty
)
where
wrap d = text "primIOToIO(" $$ indent d <> text ")"
casm | gcsafe = casmgcsafe
| otherwise = casmstd
(declareResult, push, pop) = resultPassing results
initArg (Param n _ _) p = ppAssign n (text ("%" ++ show p))
ppCasm NHC (Casm name gcsafe init ccode args results) =
( text name <+> textline [ a | Param a _ _ <- args ]
, text name <+> text "primitive" <+> text arity <+> text "::" <+> typ
, ppPrimDecl name
( init
$$ vcatMap unpack args
$$ ppBlock
( ccode
$$ vcatMap pack (reverse results)
$$ apply "hugs_returnIO" (text $ show $ length results) <> semi
)
)
, ppStruct [doubleQuotes $ text name, text (show arity), text name]
)
where
typ = ppType (map getBaseTy args) (map getBaseTy results)
arity = show (length args)
\end{code}
%************************************************************************
%* *
\subsection{Hugs code for constructing a casm}
%* *
%************************************************************************
The only complication in printing the type is that we replace Stable
Pointers with type variables. Fortunately, any type variables will do
(since the type var doesn't affect how we pack or unpack) so we just
use @a1@ ... @am@ for the arguments and @r1@ ... @rn@ for the results.
\begin{code}
ppType :: [BaseTy] -> [BaseTy] -> Doc
ppType args res = (zipWith baseToType atvs args ++ [r]) `sepdBy` text " -> "
where
r = text "IO" <+> ppTuple (zipWith baseToType rtvs res)
atvs = [ 'a':show i | i <- [1..] ]
rtvs = [ 'r':show i | i <- [1..] ]
baseToType :: String -> BaseTy -> Doc
baseToType tv StablePtr = text "StablePtr" <+> text tv
baseToType tv Word = text tv
baseToType tv (Foreign _) = text "ForeignObj"
baseToType tv b = text (show b)
\end{code}
\begin{code}
unpack :: Param -> Doc
unpack (Param _ expr (Foreign free)) = ppAssign expr (text "hugs->getForeign()")
unpack (Param _ expr hty) = ppAssign expr src
where
src = text ("hugs->get" ++ show hty ++ "()")
pack :: Param -> Doc
pack (Param _ expr (Foreign free))
= text "hugs->putForeign" <> ppTuple [text expr, text free] <> semi
pack (Param _ expr hty)
= apply ("hugs->put" ++ show hty) (text expr) <> semi
\end{code}
\begin{code}
ppPrimDecl :: String -> Doc -> Doc
ppPrimDecl name body
= apply "PROTO_PRIM" (text name) <> semi
$$ apply "primFun" (text name)
$$ char '{'
$$ indent body
$$ char '}'
\end{code}
%************************************************************************
%* *
\subsection{GHC code for returning multiple results from a casm}
%* *
%************************************************************************
resultPassing decides which policy to use for returning results from C.
There are three possibilities:
\begin{enumerate}
\item Returning nothing is easy:
casm ``...'' >> returnPrimIO ()
\item Returning one thing is easy too:
casm ``...'' >>= \ x -> returnPrimIO x
\item Returning many things is tricky. We pack up all the bits into a
struct and return a pointer to the struct. We then read each returned
value out of the struct. Blech!
casm ``static struct { int res1; float res2; } gc_result;
...;
gc_result.x = x;
gc_result.y = y;
%r = &gc_result;'' >>= \ gc_result ->
casm ``%r = (struct { int res1; float res2; }*)%0 -> x;'' (gc_result::Addr) >>= \ x ->
casm ``%r = (struct { int res1; float res2; }*)%0 -> y;'' (gc_result::Addr) >>= \ y ->
returnPrimIO (x,y)
\end{itemize}
\begin{code}
resultPassing :: [Param] -> ( Doc -- How to declare the var
, Doc -- How to save the vars in C
, Doc) -- How to get them back in Haskell
resultPassing [] = -- No vars
( empty
, empty
, text ">>"
)
resultPassing [x@(Param n _ _)] = -- One var
( empty
, ppAssign "%r" (text n)
, textline [">>= \\ ", n, " ->"]
)
resultPassing xs = -- Many vars
( text "static" <+> structTy <+> text "gc_result;"
, vcatMap copyIn xs $$ text "%r = &gc_result;"
, text ">>= \\ gc_result ->" $$ vcatMap copyOut xs
)
where
structTy = text "struct" <+> braces (semiList (map decl xs))
where
decl :: Param -> Doc
decl (Param n _ b) = ppCDecl (baseToCType b) n
-- Copy a return value into corresponding place in struct.
copyIn :: Param -> Doc
copyIn (Param n e k) = ppAssign ("gc_result." ++ n) (text e)
-- Copy a return value out of its place in the struct.
copyOut :: Param -> Doc
copyOut (Param n _ k) =
(casmstd (ppAssign "%r" ("%0" `deRef` n)) [text "(gc_result :: Addr)"])
<+> textline [">>= \\", n, "->"]
where
deRef arg n = ppCast (structTy <> char '*') (text arg) <> text "->" <> text n
\end{code}
%************************************************************************
%* *
\subsection{GHC Specific Utilities}
%* *
%************************************************************************
\begin{code}
casmstd :: Doc -> [Doc] -> Doc
casmstd d args = (("_casm_ ``", "''") `around` indent d) <+> hsep args
casmgcsafe :: Doc -> [Doc] -> Doc
casmgcsafe d args = (("_casm_GC_ ``", "''") `around` indent d) <+> hsep args
\end{code}
%************************************************************************
%* *
\subsection{Utilities}
%* *
%************************************************************************
\begin{code}
apply :: String -> Doc -> Doc
apply f e = text f <> parens e
\end{code}
%************************************************************************
%* *
\subsection{Tests}
%* *
%************************************************************************
\begin{code}
tst t = putStrLn $ render (call $$ decl $$ entry $$ c)
where
(call, decl, entry, c) = ppCasm Hugs t
t1 = Casm
"foo"
False
(text "int x; void* q; char p; void* q")
(text "p = foo(&q,x,y);")
--(text "free q")
[ Param "x" "x" Int , Param "q" "q" Addr]
[ Param "p" "'p'" Char, Param "q" "q" Addr]
\end{code}
|