synthesizer-llvm-0.3: src/Synthesizer/LLVM/Frame/StereoInterleaved.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{- |
Represent a vector of Stereo values in two vectors
that store the values in an interleaved way.
That is:
> vector0[0] = left[0]
> vector0[1] = right[0]
> vector0[2] = left[1]
> vector0[3] = right[1]
> vector1[0] = left[2]
> vector1[1] = right[2]
> vector1[2] = left[3]
> vector1[3] = right[3]
This representation is not very useful for computation,
but necessary as intermediate representation for interfacing with memory.
SSE/SSE2 have the instructions UNPACK(L|H)P(S|D) that interleave efficiently.
-}
module Synthesizer.LLVM.Frame.StereoInterleaved (
T,
Value,
interleave,
deinterleave,
) where
import qualified Synthesizer.LLVM.Frame.Stereo as Stereo
import qualified LLVM.Extra.Arithmetic as A
import qualified LLVM.Extra.Class as Class
import qualified LLVM.Core as LLVM
import LLVM.Extra.Class
(Undefined, undefTuple,
MakeValueTuple, valueTupleOf, )
import LLVM.Core
(Vector, Struct, IsSized, )
import LLVM.Util.Loop (Phi, phis, addPhis, )
import qualified LLVM.Extra.Memory as Memory
-- import qualified LLVM.Extra.Control as C
-- import qualified LLVM.Extra.Arithmetic as A
import qualified LLVM.Extra.Vector as Vector
import qualified Data.TypeLevel.Num as TypeNum
import Foreign.Ptr (castPtr, )
import qualified Foreign.Storable as St
-- import Data.Word (Word32, )
import Control.Monad (liftM2, )
import Control.Applicative (liftA2, )
import Data.Tuple.HT (mapPair, )
data T n a = Cons (Vector n a) (Vector n a)
data Value n a = Value (LLVM.Value (Vector n a)) (LLVM.Value (Vector n a))
interleave ::
(LLVM.IsPrimitive a, TypeNum.Pos n) =>
Stereo.T (LLVM.Value (Vector n a)) ->
LLVM.CodeGenFunction r (Value n a)
interleave x =
uncurry (liftM2 Value) .
mapPair (Vector.assemble, Vector.assemble) .
splitAt (Vector.sizeInTuple x) .
concatMap (\s -> [Stereo.left s, Stereo.right s]) =<<
Vector.extractAll x
deinterleave ::
(LLVM.IsPrimitive a, TypeNum.Pos n) =>
Value n a ->
LLVM.CodeGenFunction r (Stereo.T (LLVM.Value (Vector n a)))
deinterleave (Value v0 v1) =
Vector.assemble .
(let aux (l:r:xs) = Stereo.cons l r : aux xs
aux [] = []
aux _ = error "odd number of stereo elements"
in aux) =<<
liftM2 (++)
(Vector.extractAll v0)
(Vector.extractAll v1)
instance
(TypeNum.Pos n, LLVM.IsPrimitive a, St.Storable a) =>
St.Storable (T n a) where
sizeOf ~(Cons v0 v1) = St.sizeOf v0 + St.sizeOf v1
alignment ~(Cons v _) = St.alignment v
peek ptr =
let p = castPtr ptr
in liftM2 Cons
(St.peekElemOff p 0)
(St.peekElemOff p 1)
poke ptr (Cons v0 v1) =
let p = castPtr ptr
in St.pokeElemOff p 0 v0 >>
St.pokeElemOff p 1 v1
instance (TypeNum.Pos n, LLVM.IsPrimitive a) => Class.Zero (Value n a) where
zeroTuple = Value (LLVM.value LLVM.zero) (LLVM.value LLVM.zero)
instance (TypeNum.Pos n, LLVM.IsPrimitive a) => Undefined (Value n a) where
undefTuple = Value (LLVM.value LLVM.undef) (LLVM.value LLVM.undef)
{-
Can only be implemented by ifThenElse
since the atomic 'select' command wants a bool vector.
instance (TypeNum.Pos n, LLVM.IsPrimitive a, Phi a) => C.Select (Value n a) where
select b (Value x0 x1) (Value y0 y1) =
liftM2 Value
(C.select b x0 y0)
(C.select b x1 y1)
instance LLVM.CmpRet a b => LLVM.CmpRet (Stereo.T a) (Stereo.T b) where
-}
instance (TypeNum.Pos n, LLVM.IsPrimitive a, LLVM.IsConst a) =>
MakeValueTuple (T n a) (Value n a) where
valueTupleOf (Cons v0 v1) =
Value
(LLVM.valueOf v0)
(LLVM.valueOf v1)
instance (TypeNum.Pos n, LLVM.IsPrimitive a) => Phi (Value n a) where
phis bb = mapV (phis bb)
addPhis bb = zipV (\_ _ -> ()) (addPhis bb)
{-
instance Vector.ShuffleMatch n (Value n a) where
shuffleMatch = Vector.shuffleMatchAccess
-}
{-
We cannot make an instance of Vector.Access,
since we defined the functional dependencies in a way,
that vector size and element type uniquely defines the vector type.
instance Vector.Access n (Stereo.T a) (Value n a) where
insert ::
LLVM.Value Word32 ->
LLVM.Value a ->
Value n a ->
LLVM.CodeGenFunction r (Value n a)
insert k a (Value v0 v1) = do
k20 <- A.add k k
k21 <- A.inc k20
select (k20<Vector.size v0) ...
Vector.insert k20 (Stereo.left a) v >>=
Vector.insert k21 (Stereo.right a)
extract ::
LLVM.Value Word32 ->
Value n a ->
LLVM.CodeGenFunction r (LLVM.Value a)
extract k (Value v0 v1) = do
k20 <- A.add k k
k21 <- A.inc k20
liftM2 Value
(Vector.extract k20 v)
(Vector.extract k21 v)
With this instance,
both 'interleave' and 'deinterleave' could then be written this way:
Vector.assemble =<<
mapM (flip Vector.extract x) (take (Vector.size x) [0..])
-}
memory ::
(TypeNum.Pos n, LLVM.IsPrimitive a, LLVM.IsPrimitive am,
Memory.FirstClass a am) =>
Memory.Record r (Struct (Vector n am, (Vector n am, ()))) (Value n a)
memory =
liftA2 Value
(Memory.element (\(Value v _) -> v) TypeNum.d0)
(Memory.element (\(Value _ v) -> v) TypeNum.d1)
instance
(TypeNum.Pos n,
Memory.FirstClass a am,
LLVM.IsPrimitive a, IsSized a as,
TypeNum.Mul n as vs, TypeNum.Pos vs,
LLVM.IsPrimitive am, IsSized am amsize,
TypeNum.Mul n amsize vmsize, TypeNum.Pos vmsize) =>
Memory.C (Value n a) (Struct (Vector n am, (Vector n am, ()))) where
load = Memory.loadRecord memory
store = Memory.storeRecord memory
decompose = Memory.decomposeRecord memory
compose = Memory.composeRecord memory
{- |
This instance allows to run @arrange@ on interleaved stereo vectors.
-}
instance
(TypeNum.Pos n, LLVM.IsPrimitive a, LLVM.IsArithmetic a) =>
A.Additive (Value n a) where
zero = Value A.zero A.zero
add = zipV Value A.add
sub = zipV Value A.sub
neg = mapV A.neg
mapV :: (Monad m) =>
(LLVM.Value (Vector n a) -> m (LLVM.Value (Vector n a))) ->
Value n a -> m (Value n a)
mapV f (Value x0 x1) =
liftM2 Value (f x0) (f x1)
zipV :: (Monad m) =>
(c -> c -> d) ->
(LLVM.Value (Vector n a) ->
LLVM.Value (Vector n b) ->
m c) ->
Value n a ->
Value n b ->
m d
zipV g f (Value x0 x1) (Value y0 y1) =
liftM2 g (f x0 y0) (f x1 y1)