llvm-tf-9.2.0.1: private/LLVM/ExecutionEngine/Marshal.hs
module LLVM.ExecutionEngine.Marshal (
Marshal(..),
MarshalVector(..),
sizeOf,
alignment,
StructFields,
sizeOfArray,
pokeList,
with,
alloca,
Stored(..),
castToStoredPtr,
castFromStoredPtr,
-- * for testing
expandBits,
gatherBits,
adjustSign,
chop,
cut,
split,
merge,
) where
import qualified LLVM.Core.Vector as Vector ()
import qualified LLVM.Core.Data as Data
import qualified LLVM.Core.Type as Type
import qualified LLVM.Core.Proxy as LP
import qualified LLVM.ExecutionEngine.Target as Target
import LLVM.ExecutionEngine.Target (TargetData)
import LLVM.Core.Data (Ptr)
import qualified LLVM.Target.Native as Native
import qualified LLVM.FFI.Core as FFI
import qualified Type.Data.Num.Decimal.Number as Dec
import Type.Base.Proxy (Proxy(Proxy))
import qualified Foreign.Storable as Store
import qualified Foreign
import Foreign.StablePtr (StablePtr)
import Foreign.Ptr (FunPtr)
import System.IO.Unsafe (unsafePerformIO)
import qualified Control.Monad.Trans.State as MS
import Control.Applicative (liftA2, pure, (<$>))
import qualified Data.Traversable as Trav
import qualified Data.Foldable as Fold
import qualified Data.List.HT as ListHT
import Data.Bits (shiftL, shiftR, testBit, (.&.))
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Word (Word8, Word16, Word32, Word64, Word)
targetData :: TargetData
targetData =
unsafePerformIO $ Native.initializeNativeTarget >> Target.getTargetData
sizeOf :: (Type.IsType a) => LP.Proxy a -> Int
sizeOf = Target.storeSizeOfType targetData . Type.unsafeTypeRef
alignment :: (Type.IsType a) => LP.Proxy a -> Int
alignment = Target.abiAlignmentOfType targetData . Type.unsafeTypeRef
sizeOfArray :: (Type.IsType a) => LP.Proxy a -> Int -> Int
sizeOfArray proxy n =
Target.abiSizeOfType targetData (Type.unsafeTypeRef proxy) * n
{- |
Exchange data via memory in a format that is compatible with LLVM's data layout.
Prominent differences to 'Foreign.Storable' are:
* LLVM's @i1@ requires a byte in memory,
whereas Haskell's 'Bool' occupies a 32-bit word with 'Foreign.poke'.
* LLVM's @<4 x i8>@ orders vector elements depending on machine endianess,
whereas 'Foreign.poke' uses ascending order
which is compatible with arrays.
This class also supports 'Data.Struct', 'Data.Vector', 'Data.Array'.
-}
class (Type.IsType a) => Marshal a where
peek :: Ptr a -> IO a
poke :: Ptr a -> a -> IO ()
peekPrimitive :: (Type.Storable a) => Ptr a -> IO a
peekPrimitive = Store.peek . Type.toPtr
pokePrimitive :: (Type.Storable a) => Ptr a -> a -> IO ()
pokePrimitive = Store.poke . Type.toPtr
instance Marshal Float where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Double where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Int where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Int8 where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Int16 where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Int32 where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Int64 where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Word where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Word8 where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Word16 where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Word32 where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal Word64 where
peek = peekPrimitive; poke = pokePrimitive
instance (Store.Storable a) => Marshal (Foreign.Ptr a) where
peek = peekPrimitive; poke = pokePrimitive
instance (Type.IsType a) => Marshal (Ptr a) where
peek = peekPrimitive; poke = pokePrimitive
instance (Type.IsFunction a) => Marshal (FunPtr a) where
peek = peekPrimitive; poke = pokePrimitive
instance Marshal (StablePtr a) where
peek = peekPrimitive; poke = pokePrimitive
instance (Type.Positive d) => Marshal (Data.WordN d) where
peek ptr =
fmap (Data.WordN . merge 8 . map toInteger . word8s) $
peekVectorGen ptr $ sizeOf (proxyFromPtr ptr)
poke ptr (Data.WordN a) =
pokeVectorGen ptr . word8s . map fromInteger .
take (sizeOf (proxyFromPtr ptr)) . split 8 $ a
instance (Type.Positive d) => Marshal (Data.IntN d) where
peek ptr =
fmap (consIntN Proxy . merge 8 . map toInteger . word8s) $
peekVectorGen ptr $ sizeOf (proxyFromPtr ptr)
poke ptr a =
pokeVectorGen ptr . word8s . map fromInteger .
take (sizeOf (proxyFromPtr ptr)) . split 8 $ deconsIntN Proxy a
cut :: Int -> Integer -> Integer
cut n w = (shiftL 1 n - 1) .&. w
split :: Int -> Integer -> [Integer]
split n = map (cut n) . iterate (flip shiftR n)
merge :: Int -> [Integer] -> Integer
merge m xs = sum $ zipWith shiftL xs $ iterate (m+) 0
instance Marshal Bool where
peek = fmap (/= 0) . Store.peek . castBoolPtr
poke ptr a = Store.poke (castBoolPtr ptr) (fromIntegral $ fromEnum a)
castBoolPtr :: Ptr Bool -> Foreign.Ptr Word8
castBoolPtr = Foreign.castPtr . Data.uncheckedToPtr
instance
(Type.Natural n, Marshal a, Type.IsSized a) =>
Marshal (Data.Array n a) where
peek = peekArray Proxy LP.Proxy
poke = pokeArray Fold.toList
peekArray ::
(Type.Natural n, Marshal a) =>
Proxy n -> LP.Proxy a ->
Ptr (Data.Array n a) -> IO (Data.Array n a)
peekArray n proxy =
let step = Target.abiSizeOfType targetData $ Type.unsafeTypeRef proxy
in \ptr ->
fmap Data.Array $ mapM peek $
take (Dec.integralFromProxy n) $
iterate (flip plusPtr step) (castElemPtr ptr)
pokeArray :: (Marshal a) => (f a -> [a]) -> Ptr (f a) -> f a -> IO ()
pokeArray toList ptr = pokeList (castElemPtr ptr) . toList
pokeList :: (Marshal a) => Ptr a -> [a] -> IO ()
pokeList = pokeListAux LP.Proxy
pokeListAux :: (Marshal a) => LP.Proxy a -> Ptr a -> [a] -> IO ()
pokeListAux proxy =
let step = Target.abiSizeOfType targetData $ Type.unsafeTypeRef proxy
in \ptr -> sequence_ . zipWith poke (iterate (flip plusPtr step) ptr)
castElemPtr :: Ptr (f a) -> Ptr a
castElemPtr = Data.uncheckedFromPtr . Foreign.castPtr . Data.uncheckedToPtr
instance
(Type.Positive n, MarshalVector a) =>
Marshal (Data.Vector n a) where
peek = peekVector
poke = pokeVector
class (Type.IsPrimitive a) => MarshalVector a where
peekVector ::
(Type.Positive n) =>
Ptr (Data.Vector n a) -> IO (Data.Vector n a)
pokeVector ::
(Type.Positive n) =>
Ptr (Data.Vector n a) -> Data.Vector n a -> IO ()
instance MarshalVector Bool where
peekVector ptr =
fmap (vectorFromList . expandBits) $
peekVectorGen ptr $ sizeOf (proxyFromPtr ptr)
pokeVector ptr = pokeVectorGen ptr . gatherBits . Fold.toList
expandBits :: [Word8] -> [Bool]
expandBits = concatMap (\byte -> map (testBit byte) [0..7])
vectorFromList :: (Type.Positive n) => [a] -> Data.Vector n a
vectorFromList =
MS.evalState $ Trav.sequence $ pure $ MS.state $ \(y:ys) -> (y,ys)
gatherBits :: [Bool] -> [Word8]
gatherBits =
map (sum . zipWith (flip shiftL) [0..] . map (fromIntegral . fromEnum)) .
ListHT.sliceVertical 8
instance (Type.Positive d) => MarshalVector (Data.WordN d) where
peekVector ptr = fmap Data.WordN <$> peekNVector Proxy ptr
pokeVector ptr = pokeNVector Proxy ptr . fmap (\(Data.WordN x) -> x)
instance (Type.Positive d) => MarshalVector (Data.IntN d) where
peekVector ptr = fmap (consIntN Proxy) <$> peekNVector Proxy ptr
pokeVector ptr = pokeNVector Proxy ptr . fmap (deconsIntN Proxy)
consIntN :: (Type.Positive d) => Proxy d -> Integer -> Data.IntN d
consIntN proxy = Data.IntN . adjustSign (Dec.integralFromProxy proxy)
deconsIntN :: (Type.Positive d) => Proxy d -> Data.IntN d -> Integer
deconsIntN proxy (Data.IntN a) = cut (Dec.integralFromProxy proxy) a
adjustSign :: Int -> Integer -> Integer
adjustSign d =
let range = shiftL 1 d
in \a -> if a < div range 2 then a else a-range
peekNVector ::
(Type.Positive n, Type.Positive d, Type.IsPrimitive (intn d)) =>
Proxy d -> Ptr (Data.Vector n (intn d)) -> IO (Data.Vector n Integer)
peekNVector proxy ptr =
fmap (vectorFromList . chop 8 (Dec.integralFromProxy proxy) .
map toInteger . word8s) $
peekVectorGen ptr $ sizeOf (proxyFromPtr ptr)
pokeNVector ::
(Type.Positive n, Type.Positive d, Type.IsPrimitive (intn d)) =>
Proxy d ->
Ptr (Data.Vector n (intn d)) -> Data.Vector n Integer -> IO ()
pokeNVector proxy ptr =
pokeVectorGen ptr . take (sizeOf (proxyFromPtr ptr)) . word8s .
map fromInteger . chop (Dec.integralFromProxy proxy) 8 . Fold.toList
word8s :: [Word8] -> [Word8]
word8s = id
proxyFromPtr :: Ptr a -> LP.Proxy a
proxyFromPtr _ = LP.Proxy
chop :: Int -> Int -> [Integer] -> [Integer]
chop m n =
concat . snd .
Trav.mapAccumL
(\(valid,acc) x ->
let newAcc = acc + cut n (shiftL x valid)
nextValid = valid+m
in if nextValid<n
then ((nextValid, newAcc), [])
else
case divMod nextValid n of
(chunks,remd) ->
((remd, shiftR x (m-remd)),
(newAcc :) $
map (cut n . shiftR x) $
take (chunks-1) $ iterate (n+) (n-valid)))
(0,0) .
(++ repeat 0)
instance MarshalVector Float where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Double where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Word where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Word8 where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Word16 where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Word32 where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Word64 where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Int where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Int8 where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Int16 where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Int32 where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
instance MarshalVector Int64 where
peekVector = peekVectorAuto Proxy
pokeVector ptr = pokeVectorGen ptr . Fold.toList
peekVectorAuto ::
(Type.Positive n, Type.IsPrimitive a, Store.Storable a) =>
Proxy n -> Ptr (Data.Vector n a) -> IO (Data.Vector n a)
peekVectorAuto proxy ptr =
fmap vectorFromList $ peekVectorGen ptr $ Dec.integralFromProxy proxy
peekVectorGen ::
(Type.IsType b, Store.Storable chunk) =>
Ptr b -> Int -> IO [chunk]
peekVectorGen = peekVectorAux LP.Proxy (error "vector")
peekVectorAux ::
(Type.IsType b, Store.Storable chunk) =>
LP.Proxy b -> chunk -> Ptr b -> Int -> IO [chunk]
peekVectorAux proxy dummyChunk =
let (offset,step) = arrayParams proxy dummyChunk
in \ptr n ->
mapM (Store.peekByteOff (Data.uncheckedToPtr ptr)) $
take n $ iterate (step+) offset
pokeVectorGen ::
(Type.IsType b, Store.Storable chunk) =>
Ptr b -> [chunk] -> IO ()
pokeVectorGen = pokeVectorAux LP.Proxy (error "vector")
pokeVectorAux ::
(Type.IsType b, Store.Storable chunk) =>
LP.Proxy b -> chunk -> Ptr b -> [chunk] -> IO ()
pokeVectorAux proxy dummyChunk =
let (offset,step) = arrayParams proxy dummyChunk
in \ptr xs ->
sequence_ $
zipWith (Store.pokeByteOff (Data.uncheckedToPtr ptr))
(iterate (step+) offset) xs
arrayParams ::
(Type.IsType b, Store.Storable chunk) =>
LP.Proxy b -> chunk -> (Int,Int)
arrayParams proxy dummyChunk =
let chunkSize = Store.sizeOf dummyChunk
in if Target.littleEndian targetData
then (0, chunkSize)
else (sizeOf proxy - chunkSize, -chunkSize)
instance (StructFields fields) => Marshal (Data.Struct fields) where
peek = withPtrProxy $ \proxy ->
let typeRef = Type.unsafeTypeRef proxy
in fmap Data.Struct . peekStruct typeRef 0
poke = withPtrProxy $ \proxy ->
let typeRef = Type.unsafeTypeRef proxy
pokePlain = pokeStruct typeRef 0
in \ptr (Data.Struct as) -> pokePlain ptr as
withPtrProxy :: (LP.Proxy a -> Ptr a -> b) -> Ptr a -> b
withPtrProxy act = act LP.Proxy
class (Type.StructFields fields) => StructFields fields where
peekStruct :: FFI.TypeRef -> Int -> Ptr struct -> IO fields
pokeStruct :: FFI.TypeRef -> Int -> Ptr struct -> fields -> IO ()
instance
(Marshal a, Type.IsSized a, StructFields as) =>
StructFields (a,as) where
peekStruct typeRef i =
let offset = Target.offsetOfElement targetData typeRef i
peekIs = peekStruct typeRef (i+1)
in \ptr -> liftA2 (,) (peek $ plusPtr ptr offset) (peekIs ptr)
pokeStruct typeRef i =
let offset = Target.offsetOfElement targetData typeRef i
pokeIs = pokeStruct typeRef (i+1)
in \ptr (a,as) -> poke (plusPtr ptr offset) a >> pokeIs ptr as
instance StructFields () where
peekStruct _type _i _ptr = return ()
pokeStruct _type _i _ptr () = return ()
plusPtr :: Ptr a -> Int -> Ptr b
plusPtr ptr offset =
Data.uncheckedFromPtr $ Foreign.plusPtr (Data.uncheckedToPtr ptr) offset
with :: (Marshal a) => a -> (Ptr a -> IO b) -> IO b
with a act = alloca $ \ptr -> poke ptr a >> act ptr
alloca :: (Type.IsType a) => (Ptr a -> IO b) -> IO b
alloca = allocaAux LP.Proxy
allocaAux :: (Type.IsType a) => LP.Proxy a -> (Ptr a -> IO b) -> IO b
allocaAux proxy f =
Foreign.allocaBytesAligned (sizeOf proxy) (alignment proxy)
(f . Data.uncheckedFromPtr)
{- |
Provide @Marshal@ functionality through Haskell's 'Storable' interface.
Thus, @'Ptr' a@ is equivalent to @'Foreign.Ptr' ('Stored' a)@.
You may e.g. use a @'Foreign.ForeignPtr' ('Stored' a)@
to manage LLVM data with Haskell's garbage collector.
-}
newtype Stored a = Stored {getStored :: a}
castToStoredPtr :: Ptr a -> Foreign.Ptr (Stored a)
castToStoredPtr = Foreign.castPtr . Data.uncheckedToPtr
castFromStoredPtr :: Foreign.Ptr (Stored a) -> Ptr a
castFromStoredPtr = Data.uncheckedFromPtr . Foreign.castPtr
instance (Marshal a) => Store.Storable (Stored a) where
sizeOf = sizeOf . proxyFromStored
alignment = alignment . proxyFromStored
peek = fmap Stored . peek . castFromStoredPtr
poke ptr = poke (castFromStoredPtr ptr) . getStored
proxyFromStored :: Stored a -> LP.Proxy a
proxyFromStored _ = LP.Proxy