Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/greencard/Casm.lhs

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


%
% 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}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].