packages feed

llvm-extra-0.1: src/Array.hs

{-# LANGUAGE FlexibleContexts #-}
module Main where

import LLVM.Extra.Control (arrayLoop, )
import qualified LLVM.Extra.ScalarOrVector as SV
import qualified LLVM.Extra.Vector as Vector
import qualified LLVM.Extra.Control as U

import qualified LLVM.Extra.Extension.X86 as X86
import qualified LLVM.Extra.Extension as Ext

import qualified LLVM.Extra.Arithmetic as A

import LLVM.Core
import LLVM.ExecutionEngine (simpleFunction, )
import qualified System.IO as IO

import Data.TypeLevel.Num(D4, )
import Data.Word (Word32, )
import Data.Int (Int32, )
import Foreign.Storable (Storable, sizeOf, )
import Foreign.Marshal.Array (allocaArray, )

import Control.Monad.Trans.State (StateT(StateT), runStateT, )
import Control.Monad (liftM2, )



type Vec = ConstValue (Vector D4 Float)

constVec ::
   Float -> CodeGenFunction r (Value (Vector D4 Float))
constVec x =
   return $ valueOf $ toVector (x,x,x,x)

constVecInsert ::
   Float -> CodeGenFunction r (Value (Vector D4 Float))
constVecInsert x' =
   let x = valueOf x'
   in  foldr
          (\n mv v -> insertelement v x (valueOf n) >>= mv)
          return
          [0..3]
          (value (undef :: Vec))

{-
This implementation cannot make use of vector operations,
because 'frem' is only available in the FPU.
-}
fractionVector0 ::
   (IsFloating c, ABinOp a (Value (Vector D4 Float)) (v c)) =>
   a -> CodeGenFunction r (v c)
fractionVector0 x =
   frem x =<< constVec 1

{-
Works only when Floating point number is in the range
that is representable by Int32.
-}
fraction :: Value Float -> CodeGenFunction r (Value Float)
fraction x =
   A.sub x =<<
   sitofp . flip asTypeOf (undefined :: Value Int32) =<<
   fptosi x

fractionVector ::
   Value (Vector D4 Float) ->
   CodeGenFunction r (Value (Vector D4 Float))
fractionVector x =
   A.sub x =<<
   sitofp . flip asTypeOf (undefined :: Value (Vector D4 Int32)) =<<
   fptosi x


{-
This call

    fill (fromIntegral len) ptr
       (toVector (0.01003, 0.01001, 0.00999, 0.00997)) >>

would not work, because Vector is not of type Generic.
-}
mChorusVectorArg :: CodeGenModule (Function (Word32 -> Ptr Float -> Vector D4 Float -> IO Float))
mChorusVectorArg =
  createFunction ExternalLinkage $ \ size ptr freq -> do
    const1 <- constVec 1
    const2 <- constVec 2
    s <- arrayLoop size ptr (value (zero :: Vec)) $ \ ptri phase -> do
      y <- sub const1 =<< mul const2 phase
      s0 <- extractelement y (valueOf 0)
      s1 <- extractelement y (valueOf 1)
      s2 <- extractelement y (valueOf 2)
      s3 <- extractelement y (valueOf 3)
      s01 <- add s0 s1
      s23 <- add s2 s3
      s0123 <- add s01 s23
      flip store ptri =<< mul (valueOf 0.25 :: Value Float) s0123
      fractionVector =<< add phase freq
    ss <- extractelement s (valueOf 0)
    ret (ss :: Value Float)


{- |
differing vector sizes are allowed according to documentation,
but not supported by C++ library of LLVM-2.5

mixReduceSize :: Value (Vector D4 Float) -> CodeGenFunction r (Value Float)
mixReduceSize y = do
    y01 <- shufflevector y (value undef) (constVector [constOf 0, constOf 1])
    y23 <- shufflevector y (value undef) (constVector [constOf 2, constOf 3])
    z <- add
       (y01 :: Value (Vector D2 Float))
       (y23 :: Value (Vector D2 Float))
    s0 <- extractelement z (valueOf 0)
    s1 <- extractelement z (valueOf 1)
    mul (0.25 :: Float) =<< add s0 s1
-}

{-
Here we do use consistently Vectors of size 4.
Since we declare the upper floats as undefined
the code is efficient.
-}
mixGeneric :: Value (Vector D4 Float) -> CodeGenFunction r (Value Float)
mixGeneric y = do
    -- that is translated to movhlps
    y23 <- shufflevector y (value undef) (constVector [constOf 2, constOf 3, undef, undef])
    z <- add y (y23 :: Value (Vector D4 Float))
    s0 <- extractelement z (valueOf 0)
    s1 <- extractelement z (valueOf 1)
    mul (0.25 :: Float) =<< add s0 s1


{-
Needs the horizontal add instruction from the SSSE3 extension in ix86 CPUs.
-}
mixHorizontal :: Value (Vector D4 Float) -> CodeGenFunction r (Value Float)
mixHorizontal y = do
    z <- Ext.runUnsafe X86.haddps (value undef) y
    s <- Ext.runUnsafe X86.haddps (value undef) z
    mul (0.25 :: Float) =<< extractelement s (valueOf 0)

{-
Needs the dot product instruction from the SSE4 extension in ix86 CPUs.
-}
mixDotProduct :: Value (Vector D4 Float) -> CodeGenFunction r (Value Float)
mixDotProduct y = do
    x <- SV.replicate (valueOf 0.25)
    z <- Ext.runUnsafe X86.dpps x y (valueOf 0xF1)
    extractelement z (valueOf 0)

mChorusVector :: CodeGenModule (Function (Word32 -> Ptr Float -> Float -> Float -> Float -> Float -> IO Float))
mChorusVector =
  createFunction ExternalLinkage $ \ size ptr f0 f1 f2 f3 -> do
    freq <- Vector.assemble [f0,f1,f2,f3]
    const1 <- constVec 1
    const2 <- constVec (-2)
    s <- arrayLoop size ptr (value (zero :: Vec)) $ \ ptri phase -> do
      flip store ptri =<< mixHorizontal =<< add const1 =<< mul const2 phase
      fractionVector =<< add phase (freq :: Value (Vector D4 Float))
    ss <- extractelement s (valueOf 0)
    ret (ss :: Value Float)

waveSaw :: Value Float -> CodeGenFunction r (Value Float)
waveSaw t =
  sub (valueOf 1 :: Value Float) =<<
  mul (valueOf 2 :: Value Float) t

incPhase :: Value Float -> Value Float -> CodeGenFunction r (Value Float)
incPhase d p =
  fraction =<< add d p

osciSaw :: Value Float -> Value Float -> CodeGenFunction r (Value Float, Value Float)
osciSaw freq phase =
  liftM2 (,) (waveSaw phase) (incPhase freq phase)

mChorus :: CodeGenModule (Function (Word32 -> Ptr Float -> Float -> Float -> Float -> Float -> IO Float))
mChorus =
  createFunction ExternalLinkage $ \ size ptr f0 f1 f2 f3 -> do
    s <- arrayLoop size ptr
            ((valueOf 0 :: Value Float, valueOf 0 :: Value Float),
             (valueOf 0 :: Value Float, valueOf 0 :: Value Float)) $
         \ ptri ((phase0, phase1), (phase2, phase3)) -> do
      (y0, phase0') <- osciSaw f0 phase0
      (y1, phase1') <- osciSaw f1 phase1
      (y2, phase2') <- osciSaw f2 phase2
      (y3, phase3') <- osciSaw f3 phase3
      y01 <- add y0 y1
      y23 <- add y2 y3
      y0123 <- add y01 y23
      flip store ptri =<< mul (valueOf 0.25 :: Value Float) y0123
      return ((phase0', phase1'), (phase2', phase3'))
    ret (fst (fst s) :: Value Float)


sawOsciAction ::
  Value Float ->
  StateT (Value Float) (CodeGenFunction r) (Value Float)
sawOsciAction freq =
  StateT $ osciSaw freq

{-
(***) :: StateT s m a -> StateT t m b -> StateT (s,t) m (a,b)
(***) sta stb =
  StateT $ \(s0,t0) ->
  do (a,s1) <- runStateT sta s0
     (b,t1) <- runStateT stb t0
     return ((a,b), (s1,t1))
-}

(=+=) ::
  StateT s (CodeGenFunction r) (Value Float) ->
  StateT t (CodeGenFunction r) (Value Float) ->
  StateT (s,t) (CodeGenFunction r) (Value Float)
(=+=) sta stb =
  StateT $ \(s0,t0) ->
  do (a,s1) <- runStateT sta s0
     (b,t1) <- runStateT stb t0
     c <- add a b
     return (c, (s1,t1))

mChorusMonadic :: CodeGenModule (Function (Word32 -> Ptr Float -> Float -> Float -> Float -> Float -> IO Float))
mChorusMonadic =
  createFunction ExternalLinkage $ \ size ptr f0 f1 f2 f3 -> do
    s <- arrayLoop size ptr
            ((valueOf 0 :: Value Float, valueOf 0 :: Value Float),
             (valueOf 0 :: Value Float, valueOf 0 :: Value Float)) $
         \ ptri phases -> do
      (y, phases') <-
         flip runStateT phases $
            (sawOsciAction f0 =+= sawOsciAction f1) =+=
            (sawOsciAction f2 =+= sawOsciAction f3)
      flip store ptri =<< mul (valueOf 0.25 :: Value Float) y
      return phases'
    ret (fst (fst s) :: Value Float)

renderChorus :: IO ()
renderChorus = do
  m <- newModule
  _f <- defineModule m mChorusVector
  writeBitcodeToFile "array.bc" m

  fill <- simpleFunction mChorusVector
  IO.withFile "speedtest.f32" IO.WriteMode $ \h ->
    let len = 10000000
    in  allocaArray len $ \ ptr ->
          fill (fromIntegral len) ptr 0.01003 0.01001 0.00999 0.00997 >>
          IO.hPutBuf h ptr (len*sizeOf(undefined::Float))


mSaw :: CodeGenModule (Function (Word32 -> Ptr Float -> Float -> IO Float))
mSaw =
  createFunction ExternalLinkage $ \ size ptr freq -> do
    s <- arrayLoop size ptr (valueOf 0) $ \ ptri phase -> do
      (y, phase') <- osciSaw freq phase
      store y ptri
      return phase'
    ret (s :: Value Float)

renderSaw :: IO ()
renderSaw = do
  fill <- simpleFunction mSaw
  IO.withFile "speedtest.f32" IO.WriteMode $ \h ->
    let len = 10000000
    in  allocaArray len $ \ ptr ->
          fill (fromIntegral len) ptr 0.01 >>
          IO.hPutBuf h ptr (len*sizeOf(undefined::Float))


mRamp :: CodeGenModule (Function (Word32 -> Ptr Float -> Float -> IO Float))
mRamp =
  createFunction ExternalLinkage $ \ size ptr slope -> do
    s <- arrayLoop size ptr (valueOf 0) $ \ ptri y -> do
      store y ptri
      add slope y
    ret (s :: Value Float)

renderRamp :: IO ()
renderRamp = do
  fill <- simpleFunction mRamp
  IO.withFile "speedtest.f32" IO.WriteMode $ \h ->
    let len = 10000000
    in  allocaArray len $ \ ptr ->
          fill (fromIntegral len) ptr (recip $ fromIntegral len) >>
          IO.hPutBuf h ptr (len*sizeOf(undefined::Float))

main :: IO ()
main = do
   initializeNativeTarget
   renderChorus