synthesizer-llvm-0.3: src/Synthesizer/LLVM/Simple/Value.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module Synthesizer.LLVM.Simple.Value (
T, decons,
twoPi,
lift0, lift1, lift2,
constantValue, constant,
Flatten(flatten, unfold), flattenTraversable, unfoldFunctor,
) where
import qualified LLVM.Extra.ScalarOrVector as SoV
import qualified LLVM.Extra.Arithmetic as A
import LLVM.Core
(CodeGenFunction,
Value, valueOf, CmpRet,
IsArithmetic, IsConst, IsType, IsFloating, )
import qualified LLVM.Core as LLVM
import qualified Synthesizer.Basic.Phase as Phase
import qualified Data.Vault as Vault
import qualified Control.Monad.Trans.Class as MT
import qualified Control.Monad.Trans.State as MS
import Control.Monad (liftM2, liftM3, )
import qualified Synthesizer.LLVM.Frame as Frame
import qualified Synthesizer.LLVM.Frame.Stereo as Stereo
-- import qualified Algebra.NormedSpace.Maximum as NormedMax
import qualified Algebra.NormedSpace.Euclidean as NormedEuc
import qualified Algebra.NormedSpace.Sum as NormedSum
import qualified Algebra.Transcendental as Trans
import qualified Algebra.Algebraic as Algebraic
import qualified Algebra.RealRing as RealRing
import qualified Algebra.Absolute as Absolute
import qualified Algebra.Module as Module
import qualified Algebra.Field as Field
import qualified Algebra.Ring as Ring
import qualified Algebra.Additive as Additive
import qualified Number.Complex as Complex
import qualified Data.Traversable as Trav
import System.IO.Unsafe (unsafePerformIO, )
import NumericPrelude.Numeric
import NumericPrelude.Base
{-
The @r@ type parameter must be hidden and forall-quantified
because otherwise we would need an impossible type
where we have to quantify for @r@ and @t@ in different scopes
while having a class constraint that involves both of them.
> osci ::
> (RealRing.C (Value.T r t),
> IsFirstClass t, IsSized t size, IsFloating t,
> IsPrimitive t, IsConst t) =>
> (forall r. Wave.T (Value.T r t) (Value.T r y)) ->
> t -> t -> T (Value y)
-}
newtype T a = Cons {code :: forall r. Compute r (Value a)}
decons :: T a -> (forall r. LLVM.CodeGenFunction r (Value a))
decons value =
MS.evalStateT (code value) Vault.empty
type Compute r a =
MS.StateT Vault.Vault (LLVM.CodeGenFunction r) a
consUnique :: (forall r. Compute r (Value a)) -> T a
consUnique code0 =
unsafePerformIO $
fmap (consKey code0) Vault.newKey
consKey :: (forall r. Compute r (Value a)) -> Vault.Key (Value a) -> T a
consKey code0 key =
Cons (do
ma <- MS.gets (Vault.lookup key)
case ma of
Just a -> return a
Nothing -> do
a <- code0
MS.modify (Vault.insert key a)
return a)
{- |
We do not require a numeric prelude superclass,
thus also LLVM only types like vectors are instances.
-}
instance (IsArithmetic a, IsType a) => Additive.C (T a) where
zero = constantValue (LLVM.value LLVM.zero)
(+) = lift2 A.add
(-) = lift2 A.sub
negate = lift1 A.neg
instance (IsArithmetic a, SoV.IntegerConstant a) =>
Ring.C (T a) where
one = constantValue (A.fromInteger' 1)
(*) = lift2 A.mul
fromInteger = constantValue . A.fromInteger'
{-
This instance is enough for Module here.
The difference to Module instances on Haskell tuples is,
that LLVM vectors cannot be nested.
-}
instance (SoV.PseudoModule a v, SoV.IntegerConstant a) =>
Module.C (T a) (T v) where
(*>) = lift2 SoV.scale
instance (IsArithmetic a, SoV.IntegerConstant a) => Enum (T a) where
succ x = x + one
pred x = x - one
fromEnum _ = error "CodeGenFunction Value: fromEnum"
toEnum = fromIntegral
{-
instance (IsArithmetic a, Cmp a b, Num a, IsConst a) => Real (T a) where
toRational _ = error "CodeGenFunction Value: toRational"
instance (Cmp a b, Num a, IsConst a, IsInteger a) => Integral (T a) where
quot = lift2 idiv
rem = lift2 irem
quotRem x y = (quot x y, rem x y)
toInteger _ = error "CodeGenFunction Value: toInteger"
-}
instance (IsFloating a, SoV.RationalConstant a) => Field.C (T a) where
(/) = lift2 A.fdiv
fromRational' =
constantValue . A.fromRational' . fromRational'
{-
instance (Cmp a b, Fractional a, IsConst a, IsFloating a) => RealFrac (T a) where
properFraction _ = error "CodeGenFunction Value: properFraction"
-}
instance (IsFloating a, SoV.RationalConstant a) => Algebraic.C (T a) where
sqrt = lift1 A.sqrt
root n x = lift2 A.pow x (1 / fromInteger n)
x^/r = lift2 A.pow x (fromRational' r)
instance (IsFloating a, SoV.RationalConstant a, Trans.C a) => Trans.C (T a) where
pi = constant pi
sin = lift1 A.sin
cos = lift1 A.cos
(**) = lift2 A.pow
exp = lift1 A.exp
log = lift1 A.log
asin _ = error "LLVM missing intrinsic: asin"
acos _ = error "LLVM missing intrinsic: acos"
atan _ = error "LLVM missing intrinsic: atan"
{-
sinh x = (exp x - exp (-x)) / 2
cosh x = (exp x + exp (-x)) / 2
asinh x = log (x + sqrt (x*x + 1))
acosh x = log (x + sqrt (x*x - 1))
atanh x = (log (1 + x) - log (1 - x)) / 2
-}
twoPi ::
(IsFloating a, IsConst a, Trans.C a) =>
T a
twoPi = constant (2*pi)
{-
twoPi ::
(Cmp a b, P.Floating a, IsConst a, IsFloating a) =>
Compute r a
twoPi = P.fromInteger 2 P.* P.pi
-}
instance (SoV.Real a, SoV.IntegerConstant a, CmpRet a Bool) =>
Absolute.C (T a) where
abs = lift1 A.abs
signum = lift1 Frame.signum
{-
For useful instances with different scalar and vector type,
we would need a more flexible superclass.
-}
instance (SoV.Real a, SoV.IntegerConstant a, CmpRet a Bool,
SoV.PseudoModule a a) =>
NormedSum.C (T a) (T a) where
norm = lift1 A.abs
instance (SoV.Real a, SoV.IntegerConstant a, CmpRet a Bool,
SoV.PseudoModule a a) =>
NormedEuc.Sqr (T a) (T a) where
normSqr = lift1 A.square
instance
(Algebraic.C a, NormedEuc.Sqr (T a) (T v), SoV.RationalConstant a, IsFloating a) =>
NormedEuc.C (T a) (T v) where
norm = NormedEuc.defltNorm
{-
instance (Ring.C a, IsArithmetic a, IsConst a, CmpRet a Bool) =>
NormedMax.C (T a) (T a) where
norm = lift1 A.abs
-}
lift0 ::
(forall r. CodeGenFunction r (Value a)) ->
T a
lift0 f =
consUnique $ MT.lift $ f
lift1 ::
(forall r. Value a -> CodeGenFunction r (Value b)) ->
T a -> T b
lift1 f x =
consUnique $ MT.lift . f =<< code x
lift2 ::
(forall r. Value a -> Value b -> CodeGenFunction r (Value c)) ->
T a -> T b -> T c
lift2 f x y =
consUnique $ do
xv <- code x
yv <- code y
MT.lift $ f xv yv
constantValue :: Value a -> T a
constantValue x =
consUnique (return x)
constant :: (IsConst a) => a -> T a
constant = constantValue . valueOf
class Flatten value register | value -> register where
flatten :: value -> CodeGenFunction r register
unfold :: register -> value
flattenTraversable ::
(Flatten value register, Trav.Traversable f) =>
f value -> CodeGenFunction r (f register)
flattenTraversable =
Trav.mapM flatten
unfoldFunctor ::
(Flatten value register, Functor f) =>
f register -> f value
unfoldFunctor =
fmap unfold
instance (Flatten ah al, Flatten bh bl) =>
Flatten (ah,bh) (al,bl) where
flatten (a,b) =
liftM2 (,) (flatten a) (flatten b)
unfold (a,b) =
(unfold a, unfold b)
instance (Flatten ah al, Flatten bh bl, Flatten ch cl) =>
Flatten (ah,bh,ch) (al,bl,cl) where
flatten (a,b,c) =
liftM3 (,,) (flatten a) (flatten b) (flatten c)
unfold (a,b,c) =
(unfold a, unfold b, unfold c)
instance Flatten v r =>
Flatten (Stereo.T v) (Stereo.T r) where
flatten = flattenTraversable
unfold = unfoldFunctor
instance Flatten v r =>
Flatten (Complex.T v) (Complex.T r) where
flatten s =
liftM2 (Complex.+:)
(flatten $ Complex.real s)
(flatten $ Complex.imag s)
unfold = unfoldFunctor
instance
(RealRing.C v, Flatten v r) =>
Flatten (Phase.T v) r where
flatten s =
flatten $ Phase.toRepresentative s
unfold s =
-- could also be unsafeFromRepresentative
Phase.fromRepresentative $ unfold s
instance Flatten (T a) (Value a) where
flatten = decons
unfold = constantValue
instance Flatten () () where
flatten = return
unfold = id