packages feed

llvm-0.0.2: LLVM/Core/Value.hs

{-# LANGUAGE
    DeriveDataTypeable
  , ExistentialQuantification
  , FunctionalDependencies
  , MultiParamTypeClasses
  , UndecidableInstances
  #-}

module LLVM.Core.Value
    (
    -- * Values

    -- * Opaque wrapper for LLVM's basic value type
      AnyValue
    , DynamicValue(..)
    , mkAnyValue
    , typeOfDyn

    -- ** Type classes
    , Value(..)
    , Params(..)
    , ConstValue
    , GlobalValue
    , GlobalVariable
    , Arithmetic
    , Integer
    , Real
    , Vector

    , Global(..)
    , GlobalVar(..)
    , Function(..)
    , TypedValue(..)
    , Argument(..)

    , Instruction(..)

    -- * Constants
    , ConstInt(..)
    , ConstReal(..)
    , ConstArray(..)

    -- ** Useful functions
    , params
    , getName
    , setName
    , dumpValue
    ) where

import Control.Applicative ((<$>))
import Data.Typeable (Typeable)
import Foreign.C.String (peekCString, withCString)
import Foreign.Marshal.Array (allocaArray, peekArray)
import Foreign.Ptr (nullPtr)
import Prelude hiding (Integer, Real)
import System.IO.Unsafe (unsafePerformIO)

import qualified LLVM.Core.FFI as FFI
import LLVM.Core.Type ((:->)(..))
import qualified LLVM.Core.Type as T

-- import Debug.Trace


class Value a where
    valueRef :: a -> FFI.ValueRef
    anyValue :: a -> AnyValue

class DynamicValue a where
    fromAnyValue :: AnyValue -> a

class Params t v | t -> v where
    fromAnyList :: t -> [AnyValue] -> (v, [AnyValue])

-- | Recover the type of a value in a manner that preserves static
-- type safety.
class (T.Type t, Value v) => TypedValue v t | v -> t where
    typeOf :: v                 -- ^ value is not inspected
           -> t

data AnyValue = forall a. Value a => AnyValue a
                deriving (Typeable)

instance DynamicValue AnyValue where
    fromAnyValue = id

instance Value FFI.ValueRef where
    valueRef = id
    anyValue = AnyValue

mkAnyValue :: Value a => a -> AnyValue
mkAnyValue = AnyValue

class Value a => ConstValue a
class Value a => Arithmetic a
class Arithmetic a => Integer a
class Arithmetic a => Real a
class Arithmetic a => Vector a
class ConstValue a => GlobalValue a
class GlobalValue a => GlobalVariable a

instance Value AnyValue where
    valueRef (AnyValue a) = valueRef a
    anyValue = id

instance ConstValue AnyValue
instance GlobalValue AnyValue
instance GlobalVariable AnyValue
instance Arithmetic AnyValue
instance Integer AnyValue
instance Real AnyValue

getName :: Value v => v -> IO String
getName v = do
  namePtr <- FFI.getValueName (valueRef v)
  if namePtr == nullPtr
    then return []
    else peekCString namePtr

setName :: Value v => v -> String -> IO ()
setName v name = withCString name (FFI.setValueName (valueRef v))

dumpValue :: Value v => v -> IO ()
dumpValue = FFI.dumpValue . valueRef

newtype Instruction a = Instruction AnyValue
    deriving (DynamicValue, Typeable, Value)

newtype Global t = Global AnyValue
    deriving (ConstValue, DynamicValue, GlobalValue, Typeable, Value)

newtype GlobalVar t = GlobalVar AnyValue
    deriving (ConstValue, DynamicValue, GlobalValue, GlobalVariable,
              Typeable, Value)

fromAny :: (DynamicValue v, TypedValue v t, T.Type t) => t -> [AnyValue] -> (v, [AnyValue])
fromAny _ (x:xs) = (fromAnyValue x,xs)
fromAny _ _ = error "LLVM.Core.Value.fromAny: empty list"

globalVarType :: GlobalVar t -> t
globalVarType _ = undefined

instance T.Type t => TypedValue (GlobalVar t) t where
    typeOf = globalVarType

data Function r p = Function {
      fromFunction :: AnyValue
    }
    deriving (Typeable)

instance ConstValue (Function r p)
instance GlobalValue (Function r p)
instance GlobalVariable (Function r p)

instance DynamicValue (Function r p) where
    fromAnyValue = Function

instance Value (Function r p) where
    valueRef = valueRef . anyValue
    anyValue = fromFunction

newtype Argument t = Argument AnyValue
    deriving (DynamicValue, Typeable, Value)

instance (T.DynamicType r, T.Params p) => TypedValue (Function r p) (T.Function r p) where
    typeOf _ = T.function undefined undefined

instance (Params b c) => Params (a :-> b) (Argument a :-> c) where
    fromAnyList t (x:xs) = let (y,ys) = fromAnyList (T.cdr t) xs
                           in (Argument x :-> y,ys)
    fromAnyList _ _ = error "LLVM.Core.Value.fromAnyList(:->): empty list"

newtype ConstInt t = ConstInt AnyValue
    deriving (Arithmetic, ConstValue, DynamicValue, Integer, Typeable, Value)

instance TypedValue (ConstInt T.Int1) T.Int1 where
    typeOf = T.int1

instance TypedValue (Argument T.Int1) T.Int1 where
    typeOf = T.int1

instance TypedValue (Instruction T.Int1) T.Int1 where
    typeOf = T.int1

instance Params T.Int1 (Argument T.Int1) where
    fromAnyList = fromAny

instance TypedValue (ConstInt T.Int8) T.Int8 where
    typeOf = T.int8

instance TypedValue (Argument T.Int8) T.Int8 where
    typeOf = T.int8

instance TypedValue (Instruction T.Int8) T.Int8 where
    typeOf = T.int8

instance Params T.Int8 (Argument T.Int8) where
    fromAnyList = fromAny

instance TypedValue (ConstInt T.Int16) T.Int16 where
    typeOf = T.int16

instance TypedValue (Argument T.Int16) T.Int16 where
    typeOf = T.int16

instance Params T.Int16 (Argument T.Int16) where
    fromAnyList = fromAny

instance TypedValue (ConstInt T.Int32) T.Int32 where
    typeOf = T.int32

instance TypedValue (Argument T.Int32) T.Int32 where
    typeOf = T.int32

instance TypedValue (Instruction T.Int32) T.Int32 where
    typeOf = T.int32

instance Params T.Int32 (Argument T.Int32) where
    fromAnyList = fromAny

instance TypedValue (ConstInt T.Int64) T.Int64 where
    typeOf = T.int64

instance TypedValue (Argument T.Int64) T.Int64 where
    typeOf = T.int64

instance TypedValue (Instruction T.Int64) T.Int64 where
    typeOf = T.int64

instance Params T.Int64 (Argument T.Int64) where
    fromAnyList = fromAny

newtype ConstArray t = ConstArray AnyValue
    deriving (ConstValue, DynamicValue, Typeable, Value)

instance (T.DynamicType a) => TypedValue (ConstArray a) (T.Array a) where
    typeOf _ = T.array undefined 0

instance (T.DynamicType t) => TypedValue (Instruction (T.Array t)) (T.Array t) where
    typeOf _ = T.array undefined 0

instance (T.DynamicType t) => TypedValue (Instruction (T.Pointer t)) (T.Pointer t) where
    typeOf _ = T.pointer undefined

instance (T.DynamicType t) => Params (T.Pointer t) (Instruction (T.Pointer t)) where
    fromAnyList = fromAny

newtype ConstReal t = ConstReal AnyValue
    deriving (Arithmetic, ConstValue, DynamicValue, Real, Typeable, Value)

instance TypedValue (ConstReal T.Float) T.Float where
    typeOf = T.float

instance TypedValue (Argument T.Float) T.Float where
    typeOf = T.float

instance Params T.Float (Argument T.Float) where
    fromAnyList = fromAny

instance TypedValue (ConstReal T.Double) T.Double where
    typeOf = T.double

instance TypedValue (Argument T.Double) T.Double where
    typeOf = T.double

instance Params T.Double (Argument T.Double) where
    fromAnyList = fromAny

instance TypedValue (ConstReal T.X86Float80) T.X86Float80 where
    typeOf = T.x86Float80

instance TypedValue (Argument T.X86Float80) T.X86Float80 where
    typeOf = T.x86Float80

instance Params T.X86Float80 (Argument T.X86Float80) where
    fromAnyList = fromAny

instance TypedValue (ConstReal T.Float128) T.Float128 where
    typeOf = T.float128

instance TypedValue (Argument T.Float128) T.Float128 where
    typeOf = T.float128

instance Params T.Float128 (Argument T.Float128) where
    fromAnyList = fromAny

instance TypedValue (ConstReal T.PPCFloat128) T.PPCFloat128 where
    typeOf = T.ppcFloat128

instance TypedValue (Argument T.PPCFloat128) T.PPCFloat128 where
    typeOf = T.ppcFloat128

instance Params T.PPCFloat128 (Argument T.PPCFloat128) where
    fromAnyList = fromAny

countParams :: Function r p -> Int
countParams = fromIntegral . FFI.countParams . valueRef

listParams :: Function r p -> [AnyValue]
listParams f = unsafePerformIO $ do
  let len = countParams f
  allocaArray len $ \ptr -> do
    FFI.getParams (valueRef f) ptr
    map mkAnyValue <$> peekArray len ptr

params :: (T.DynamicType r, T.Params p, Params p v) => Function r p -> v
params f = case fromAnyList (T.params (typeOf f)) (listParams f) of
             (p, []) -> p
             _ -> error "LLVM.Core.Value.params: incompletely consumed params"

typeOfDyn :: Value a => a -> T.AnyType
typeOfDyn val = unsafePerformIO $ T.mkAnyType <$> FFI.typeOf (valueRef val)