packages feed

arbb-vm-0.1.1.2: Intel/ArbbVM/Convenience.hs

{-# LANGUAGE CPP #-}

{- |  

   A module capturing common patterns in ArBB VM code emission and
   thereby making it easier to emit code.

 -}

module Intel.ArbbVM.Convenience 
 (
   ifThenElse, while, readScalarOfSize, newConstant,

   arbbSession, EmitArbb, ConvFunction,
   if_, while_, readScalar_,
   funDef_, funDefS_, -- funDefCallable_,
   op_, opImm_,  
   opDynamic_, opDynamicImm_,
   map_,  call_, 

   mapToHost_,

   const_, int32_, int64_,float32_, float64_, bool_,
   const_storable_,
   usize_, isize_, 
   incr_int32_, 
   copy_, copyImm_, 

   local_bool_, local_int32_, local_float64_, 
   global_nobind_, global_nobind_int32_,

   -- Compile does not exist in this way anymore
   --compile_, 
   execute_, serializeFunction_, finish_, 

   getBindingNull_, getScalarType_, variableFromGlobal_,
   getFunctionType_, createGlobal_, createLocal_,

   createDenseBinding_,  getDenseType_,
   getNestedType_, 

   withArray_, print_,

   doarith_, SimpleArith(V),
--   module Intel.ArbbVM.SimpleArith,

   liftIO, liftMs,

   -- These should probably be internal only:
   getCtx, getFun
 )
where

--import qualified Intel.ArbbVM as VM
import Intel.ArbbVM as VM
-- import Intel.ArbbVM.SimpleArith 

import Control.Monad
import Data.IORef
import Data.Word
import Data.Int
import Data.Serialize
import Data.ByteString.Internal
import Foreign.Marshal.Array
import Foreign.Marshal.Alloc
import Foreign.ForeignPtr
import Foreign.Storable as Storable
import Foreign.Ptr 
import C2HS

import qualified  Control.Monad.State.Strict as S 

import Debug.Trace

--------------------------------------------------------------------------------
-- The monad for emitting Arbb code.  
type EmitArbb = S.StateT ArbbEmissionState IO

-- We put the context and a stack of function types into the
-- background.  Note, we need the stack of functions because we allow
-- nested function definitions at this convenience layer.  (They will
-- all have global scope to ArBB however.)
type ArbbEmissionState = (Context, [ConvFunction]) -- BJS: ConvFunction in place of Function
-- Note: if we also include a counter in here we can do gensyms...


-- BJS: Convenient functions are pairs of inconvenient functions 
data ConvFunction = ConvFunction { executable :: Function,  -- just a wrapper 
                                   callable   :: Function } -- "The" function


#define L S.lift$

liftIO :: IO a -> EmitArbb a
liftIO = S.lift -- Allow the user to perform IO inside this monad.

arbbSession :: EmitArbb a -> IO a 
arbbSession m = 
  do ctx <- getDefaultContext
     (a,s) <- S.runStateT m (ctx,[])
     return a

-- BJS: ConvFunction (as a name)  is a bit inconvenient
getConvFun msg = 
 do (_,ls) <- S.get
    case ls of 
      [] -> error$ msg ++" when not inside a function"
      (h:t) -> return h

getFun msg = 
 do (_,ls) <- S.get
    case ls of 
      [] -> error$ msg ++" when not inside a function"
      (h:t) -> return (callable h)


getCtx = 
 do (ctx,_) <- S.get
    return ctx

------------------------------------------------------------------------------
-- Map an ArBB array into host addrspace
mapToHost_ :: Variable -> [Word64] -> RangeAccessMode -> EmitArbb (Ptr ())
mapToHost_ var pitch mode = do 
   ctx <- getCtx
   L mapToHost ctx var pitch mode 
--------------------------------------------------------------------------------
-- Convenience functions for common patterns:

opImm_ :: Opcode -> [Variable] -> [Variable] -> EmitArbb()
opImm_ code out inp = L opImm code out inp

op_ :: Opcode -> [Variable] -> [Variable] -> EmitArbb ()
op_ code out inp = 
 do fun <- getFun "Convenience.op_ cannot execute an Opcode"
    L op fun code out inp

opDynamicImm_ :: Opcode -> [Variable] -> [Variable] -> EmitArbb()
opDynamicImm_ code out inp = L opDynamicImm code out inp

opDynamic_ :: Opcode -> [Variable] -> [Variable] -> EmitArbb ()
opDynamic_ code out inp = 
  do fun <- getFun "Convenience.opDynamic_ cannot execute an Opcode"
     L opDynamic fun code out inp

if_ :: Variable -> EmitArbb a -> EmitArbb a1 -> EmitArbb ()
if_ c t e =
  do fun <- getFun "Convenience.if_ cannot execute a conditional"
     L ifBranch fun c 
     t -- op myfun ArbbOpSub [c] [a,a]
     L elseBranch fun 
     e -- op myfun ArbbOpDiv [c] [a,a]
     L endIf fun

-- | An ArBB while loop.  Must be called inside a function definition.
while_ :: (EmitArbb Variable) -> EmitArbb a -> EmitArbb a
while_ cond body = 
   do fun <- getFun "Convenience.while_ cannot execute a while loop"
      L beginLoop fun ArbbLoopWhile
      L beginLoopBlock fun ArbbLoopBlockCond
      lc <- cond
      L loopCondition fun lc 
      L beginLoopBlock fun ArbbLoopBlockBody
      result <- body 
      L endLoop fun
      return result


const_storable_ :: Storable a => ScalarType -> a -> EmitArbb Variable 
const_storable_ st n = 
  do ctx <- getCtx
     L newConstantAlt ctx st n

-- This version picks the right in-memory representation.
const_ :: Integral a => ScalarType -> a -> EmitArbb Variable 
const_ sty i =
   case sty of 
     ArbbI8  -> const_storable_ sty (fromIntegral i :: Int8)
     ArbbI16 -> const_storable_ sty (fromIntegral i :: Int16)
     ArbbI32 -> const_storable_ sty (fromIntegral i :: Int32)
     ArbbI64 -> const_storable_ sty (fromIntegral i :: Int64)

     ArbbU8  -> const_storable_ sty (fromIntegral i :: Word8)
     ArbbU16 -> const_storable_ sty (fromIntegral i :: Word16)
     ArbbU32 -> const_storable_ sty (fromIntegral i :: Word32)
     ArbbU64 -> const_storable_ sty (fromIntegral i :: Word64)

     -- This only lets you get at the integral floating point numbers:
     ArbbF32 -> const_storable_ sty (fromIntegral i :: Float)
     ArbbF64 -> const_storable_ sty (fromIntegral i :: Double)

     ArbbUsize -> const_storable_ sty (fromIntegral i :: Word)
     ArbbIsize -> const_storable_ sty (fromIntegral i :: Int)

 

readScalar_ :: (Num a, Storable a) =>  Variable -> EmitArbb a
readScalar_ v = 
  do ctx <- getCtx
     let z = 0
	 size = Storable.sizeOf z
     x <- L readScalarOfSize size ctx v 
     return (x+z)

type FunBody = [Variable] -> [Variable] -> EmitArbb ()

debug_fundef = True

{- 
  BJS:  This funDef_ situation might need some improvement. 
   - one option is create the functions both callable and not
     a  Function would need to be a pair of the "callable" function
     and the "executable" function 
   - This is a part of ArBB that is very likely to change 
  

-} 
{- 
funDef_ name outty inty userbody = 
    funDefInternal name outty inty userbody 1

funDefCallable_ name outty inty userbody = 
    funDefInternal name outty inty userbody 0 

funDefInternal :: String -> [Type] -> [Type] -> FunBody  -> Int -> EmitArbb Function
funDefInternal name outty inty userbody remote =
  do 
     ctx <- getCtx
     fnt     <- L getFunctionType ctx outty inty
     fun     <- L beginFunction ctx fnt name remote

     when debug_fundef$ print_$ "["++name++"] Function begun."
     invars  <- L forM [0 .. length inty  - 1]   (getParameter fun 0)
     outvars <- L forM [0 .. length outty - 1]   (getParameter fun 1)

     -- Push on the stack:
     S.modify (\ (c,ls) -> (c, fun:ls))

     -- Now generate body:
     when debug_fundef$ print_$ "["++name++"]  Begin body codgen..."
     userbody outvars invars
     when debug_fundef$ print_$ "["++name++"]  Done body codgen."

     -- Pop off the stack:
     S.modify (\ (c, h:t) -> (c, t))
     L endFunction fun

     -- EXPERIMENTAL!  Compile immediately!!
     when debug_fundef$ print_$ "["++name++"] Function ended. Compiling..."
     L compile fun
     when debug_fundef$ print_$ "["++name++"] Done compiling."
     return fun
-} 

is_callable   = 0
is_executable = 1 
nullfun = Function nullPtr

-- BJS: New funDef  (create a pair of funs, actual fun + wrapper) 
funDef_ :: String -> [Type] -> [Type] -> FunBody  -> EmitArbb ConvFunction
funDef_ name outty inty userbody =
  do 
     ctx <- getCtx
     
     fnt     <- L getFunctionType ctx outty inty
     fun     <- L beginFunction ctx fnt name is_callable 

     when debug_fundef$ print_$ "["++name++"] Function begun."
     invars  <- L forM [0 .. length inty  - 1]   (getParameter fun 0)
     outvars <- L forM [0 .. length outty - 1]   (getParameter fun 1)

     -- Push on the stack:
     S.modify (\ (c,ls) -> (c, (ConvFunction nullfun fun):ls))

     -- Now generate body:
     when debug_fundef$ print_$ "["++name++"]  Begin body codgen..."
     userbody outvars invars
     when debug_fundef$ print_$ "["++name++"]  Done body codgen."

     -- Pop off the stack:
     S.modify (\ (c, h:t) -> (c, t))
     L endFunction fun

     -- EXPERIMENTAL!  Compile immediately!!
     --when debug_fundef$ print_$ "["++name++"] Function ended. Compiling..."
     --L compile fun
     --when debug_fundef$ print_$ "["++name++"] Done compiling."

      -- Also create an executable wrapper 
     wrapper <- L beginFunction ctx fnt name is_executable
     inputs  <- L forM [0 .. length inty  - 1] (getParameter wrapper 0)
     outputs <- L forM [0 .. length outty - 1] (getParameter wrapper 1) 
     L callOp wrapper ArbbOpCall fun outputs inputs
     L endFunction wrapper
    
    
     return$ ConvFunction wrapper fun


-- Umm... what's a good naming convention here?
funDefS_ :: String -> [ScalarType] -> [ScalarType] -> FunBody  -> EmitArbb ConvFunction
funDefS_ name outs ins body =
  do 
     outs' <- mapM getScalarType_ outs
     ins'  <- mapM getScalarType_ ins
     funDef_ name outs' ins' body
  
{-
call_ :: Function -> [Variable] -> [Variable] -> EmitArbb ()
call_ fun out inp = 
  do -- At the point of the call the *caller* is on the top of the stack:
     caller <- getFun "Convenience.call_ cannot call function"
     when debug_fundef$ print_ "Call_: got caller function, emitting call opcode..."
     L callOp caller ArbbOpCall fun out inp
     when debug_fundef$ print_ "Call_: Done emitting call opcode."
-} 
call_ :: ConvFunction -> [Variable] -> [Variable] -> EmitArbb ()
call_ fun out inp = 
  do -- At the point of the call the *caller* is on the top of the stack:
     caller <- getFun "Convenience.call_ cannot call function"
     when debug_fundef$ print_ "Call_: got caller function, emitting call opcode..."
     L callOp caller ArbbOpCall (callable fun) out inp
     when debug_fundef$ print_ "Call_: Done emitting call opcode."



map_ :: ConvFunction -> [Variable] -> [Variable] -> EmitArbb ()
map_ fun out inp = 
  do -- At the point of the call the *caller* is on the top of the stack:
     caller <- getFun "Convenience.map_ cannot call function"
     when debug_fundef$ print_ "Map_: got caller function, emitting map opcode..."
     L callOp caller ArbbOpMap (callable fun) out inp
     when debug_fundef$ print_ "Map_: Done emitting map opcode."


--------------------------------------------------------------------------------
-- Iteration Patterns.

-- for_range_ :: Variable -> Variable -> (i -> EmitArbb ()) -> EmitArbb ()

-- This uses C-style [inclusive,exclusive) ranges.
-- for_constRange_ :: Int -> Int -> (i -> EmitArbb ()) -> EmitArbb ()
-- for_range_ start end body = do 
--    counter <- local_int32_ "counter"
--    op_ ArbbOpCopy [counter] [zer]
--    while_ (do
--        lc <- local_bool_ "loopcond"
--        op_ ArbbOpLess [lc] [counter,max]
--        return lc)
--     (op_ ArbbOpAdd [counter] [counter,one])

-- Lifting higher order ops like this is trickier.
withArray_ :: Storable a => [a] -> (Ptr a -> EmitArbb b) -> EmitArbb b
withArray_ ls body = 
 do state <- S.get
    ref   <- L newIORef state
    let body2 ptr = do (a,s2) <- S.runStateT (body ptr) state
     		       writeIORef ref s2
     		       return a
    res    <- L withArray ls body2
    state2 <- L readIORef ref
    S.put state2
    return res

print_ :: String -> EmitArbb ()
print_ = S.lift . putStrLn

--------------------------------------------------------------------------------

-- liftM for lists
liftMs :: Monad m => ([a] -> m b) -> [m a] -> m b
-- liftMs fn ls = liftM fn (sequence ls)
liftMs fn ls = 
  sequence ls >>= fn
  -- do ls' <- sequence ls 
  --    fn ls'

-- These let us lift the slew of ArbbVM functions that expect a Context as a first argument.
lift1 :: (Context -> a -> IO b)                -> a                -> EmitArbb b
lift2 :: (Context -> a -> b -> IO c)           -> a -> b           -> EmitArbb c
lift3 :: (Context -> a -> b -> c -> IO d)      -> a -> b -> c      -> EmitArbb d
lift4 :: (Context -> a -> b -> c -> d -> IO e) -> a -> b -> c -> d -> EmitArbb e

lift1 fn a       = do ctx <- getCtx; L fn ctx a 
lift2 fn a b     = do ctx <- getCtx; L fn ctx a b
lift3 fn a b c   = do ctx <- getCtx; L fn ctx a b c
lift4 fn a b c d = do ctx <- getCtx; L fn ctx a b c d

getScalarType_      = lift1 getScalarType
getDenseType_       = lift2 getDenseType  
getNestedType_      = lift1 getNestedType
variableFromGlobal_ = lift1 variableFromGlobal
getFunctionType_    = lift2 getFunctionType
createGlobal_       = lift3 createGlobal

createLocal_ :: Type -> String -> EmitArbb Variable
createLocal_ ty name = do f <- getFun "Convenience.createLocal_ cannot create local"
			  L createLocal f ty name

createDenseBinding_ = lift4 createDenseBinding

----------------------------------------
-- These are easy ones, no Context or Function argument:

-- compile_ fn      = liftIO$ compile fn
-- execute_ a b c   = liftIO$ execute a b c
finish_          = liftIO finish
--serializeFunction_ = liftIO . serializeFunction
getBindingNull_  = liftIO getBindingNull

--BJS: execute_ nolonger quite as easy
execute_ f o i = liftIO$ execute (executable f) o i  

--BJS: should be a way to serialize the wrapper also!
serializeFunction_ f = liftIO$ serializeFunction (callable f)  

-- ... TODO ...  Keep going.

--------------------------------------------------------------------------------

-- Lazy, lazy, lazy: here are even more shorthands.

int32_   :: Integral t => t -> EmitArbb Variable 
int64_   :: Integral t => t -> EmitArbb Variable 
usize_   :: Integral t => t -> EmitArbb Variable
isize_   :: Integral t => t -> EmitArbb Variable
float32_ :: Float           -> EmitArbb Variable 
float64_ :: Double          -> EmitArbb Variable 

int32_   = const_ ArbbI32 
int64_   = const_ ArbbI64 
usize_   = const_ ArbbUsize
isize_   = const_ ArbbIsize 
float32_ = const_storable_ ArbbF32 
float64_ = const_storable_ ArbbF64 



bool_ :: Bool -> EmitArbb Variable
bool_ True  = const_storable_ ArbbBoolean (1::Int32)
bool_ False = const_storable_ ArbbBoolean (0::Int32)

-- TODO... Keep going...

incr_int32_ :: Variable -> EmitArbb ()
incr_int32_ var = do one <- int32_ 1
		     op_ ArbbOpAdd [var] [var,one] 

copy_ v1 v2 = op_ ArbbOpCopy [v1] [v2]
copyImm_ v1 v2 = opImm_ ArbbOpCopy [v1] [v2]

------------------------------------------------------------

local_bool_ name = do bty <- getScalarType_ ArbbBoolean
		      createLocal_ bty name

local_int32_ name = do ity <- getScalarType_ ArbbI32
		       createLocal_ ity name

local_float64_ name = do ty <- getScalarType_ ArbbF64
		         createLocal_ ty name

global_nobind_ ty name = 
  do binding <- getBindingNull_
     g       <- createGlobal_ ty name binding
     variableFromGlobal_ g

global_nobind_int32_ name = 
  do sty <- getScalarType_ ArbbI32
     global_nobind_ sty name

--------------------------------------------------------------------------------
-- Num instance.




--------------------------------------------------------------------------------
-- OBSOLETE: These were some helpers that didn't use the EmitArbb monad.

ifThenElse :: Function -> Variable -> IO a -> IO a1 -> IO ()
ifThenElse f c t e =
  do
   ifBranch f c      
   t -- op myfun ArbbOpSub [c] [a,a]
   elseBranch f 
   e -- op myfun ArbbOpDiv [c] [a,a]
   endIf f


-- while loops
while :: Function -> (IO Variable) -> IO a1 -> IO ()
while f cond body = 
   do 
     beginLoop f ArbbLoopWhile
     beginLoopBlock f ArbbLoopBlockCond
     lc <- cond
     loopCondition f lc 

     beginLoopBlock f ArbbLoopBlockBody
     body 
     endLoop f     

-- Works not just for arrays but anything serializable:
withSerialized :: Serialize a => a -> (Ptr () -> IO b) -> IO b
withSerialized x fn =    
   withForeignPtr fptr (fn . castPtr)
 where 
   (fptr,_,_) = toForeignPtr (encode x)

newConstant :: Storable a => Context -> Type -> a -> IO Variable 
newConstant ctx t n = 
  do           
   -- Could use withSerialized possibly...
   tmp <- withArray [n] $ \x -> createConstant ctx t (castPtr x)
   variableFromGlobal ctx tmp

newConstantAlt :: Storable a => Context -> ScalarType -> a -> IO Variable 
newConstantAlt ctx st n = 
  do           
   t   <- getScalarType ctx st       
   tmp <- withArray [n] $ \x -> createConstant ctx t (castPtr x)
   variableFromGlobal ctx tmp

-- global/constant shortcuts

-- readScalarOfSize :: Storable b => Int -> Context -> Variable -> EmitArbb b
readScalarOfSize :: Storable b => Int -> Context -> Variable -> IO b
readScalarOfSize n ctx v = 
    allocaBytes n $ \ptr -> 
       do       
        readScalar ctx v ptr 
        peek (castPtr ptr)

-- TODO: readScalar of storable should be able to determine size.


----------------------------------------------------------------------------------------------------
-- Numeric instances.
----------------------------------------------------------------------------------------------------

{- 
  It is not clear that this is worth it, yet.
  This is mainly here because I want to use Data.Complex.
 -}


-- We could use a richer type for Variable in the convenience interface.
-- data VarPlus = VarPlus Variable Type 
-- 
-- The trick would be to have functions like op_ take [SimpleArith] rather than [Variable]
-- But to dealwith anything other than the "V" variant, the desired type would need to be known.
-- 

instance Show Variable where
  show v = "<ArBB_Var>"

instance Eq Variable where 
  a == b = error "equality on Variables doesn't make sense yet"

data SimpleArith = 
		   V      Variable
		 | Const  Integer
		 | ConstD Double

                 | Plus   SimpleArith SimpleArith
		 | Times  SimpleArith SimpleArith
		 | Div    SimpleArith SimpleArith
		 | Signum SimpleArith
		 | Abs    SimpleArith

		 | Expon SimpleArith
		 | Sqrt  SimpleArith
		 | Log   SimpleArith
		 | Sin   SimpleArith
		 | Cos   SimpleArith
		 | ASin  SimpleArith
		 | ACos  SimpleArith
		 | ATan  SimpleArith
		 | SinH  SimpleArith
		 | CosH  SimpleArith
		 | ASinH SimpleArith
		 | ACosH SimpleArith
		 | ATanH SimpleArith

--		 | ProperFrac SimpleArith
  -- UNFINISHED
  deriving (Show,Eq)


instance Num SimpleArith where  
  (+)         = Plus
  (*)         = Times
  signum      = Signum
  abs         = Abs
  fromInteger = Const 

instance Fractional SimpleArith where
  (/)              = Div
  fromRational rat = error "fromRational not implemented yet for SimpleArith"

instance Ord SimpleArith where 
  a < b = error "< not implemented yet for SimpleArith"

instance Real SimpleArith where 
  toRational v = error "toRational not implemented for SimpleArith"

instance Floating SimpleArith where
  pi   = ConstD pi
  exp  = Expon
  sqrt = Sqrt
  log  = Log
  -- (**) :: a -> a -> a
  -- logBase :: a -> a -> a
  sin  = Sin
  -- tan :: a -> a
  cos  = Cos
  asin = ASin
  atan = ATan
  acos = ACos
  sinh = SinH
  cosh = CosH
  asinh = ASinH
  acosh = ACosH
  atanh = ATanH

-- instance Enum SimpleArith where
-- instance Integral SimpleArith where

--class (Real a, Fractional a) => RealFrac a where
instance RealFrac SimpleArith where
--  properFraction :: Integral b => a -> (b, a)
  properFraction x = error "properFraction not implemented for SimpleArith"

#if 0

class (Real a, Enum a) => Integral a where
  quot :: a -> a -> a
  rem :: a -> a -> a
  div :: a -> a -> a
  mod :: a -> a -> a
  quotRem :: a -> a -> (a, a)
  divMod :: a -> a -> (a, a)
  toInteger :: a -> Integer

class Enum a where
  succ :: a -> a
  pred :: a -> a
  toEnum :: Int -> a
  fromEnum :: a -> Int
  enumFrom :: a -> [a]
  enumFromThen :: a -> a -> [a]
  enumFromTo :: a -> a -> [a]
  enumFromThenTo :: a -> a -> a -> [a]

class (RealFrac a, Floating a) => RealFloat a where
  floatRadix :: a -> Integer
  floatDigits :: a -> Int
  floatRange :: a -> (Int, Int)
  decodeFloat :: a -> (Integer, Int)
  encodeFloat :: Integer -> Int -> a
  exponent :: a -> Int
  significand :: a -> a
  scaleFloat :: Int -> a -> a
  isNaN :: a -> Bool
  isInfinite :: a -> Bool
  isDenormalized :: a -> Bool
  isNegativeZero :: a -> Bool
  isIEEE :: a -> Bool
  atan2 :: a -> a -> a

#endif


-- | This lets one execute simple arithmetic expressions and store the result.
--   Returns the name of a new local binding that caries the result.
doarith_ :: ScalarType -> SimpleArith -> EmitArbb Variable
doarith_ ty_ exp = 
   do ty <- getScalarType_ ty_
      let binop op a b = 
	      do tmp <- createLocal_ ty "tmp"
		 a'  <- loop a
		 b'  <- loop b
		 op_ op [tmp] [a',b']
		 return tmp
	  loop exp = 
	     case exp of 
	       V     v   -> return v
	       Const i   -> const_ ty_ i

	       Plus  a b -> binop ArbbOpAdd a b
	       Times a b -> binop ArbbOpMul a b
	       _ -> error$ "doarith_: not handled yet: "++ show exp
      loop exp


-- data ScalarType = ArbbI8
--                 | ArbbI16
--                 | ArbbI32
--                 | ArbbI64

--                 | ArbbU8
--                 | ArbbU16
--                 | ArbbU32
--                 | ArbbU64

--                 deriving (Enum,Show,Eq)