{-# LANGUAGE ForeignFunctionInterface #-}
module Main where

import qualified LLVM.Core as LLVM
import qualified LLVM.ExecutionEngine as EE

import qualified Foreign.Marshal.Utils as MU
import Foreign.Marshal.Alloc (alloca, )
import Foreign.Storable (peek, )
import Foreign.Ptr (Ptr, FunPtr, )

import qualified Type.Data.Num.Decimal as TypeNum
import qualified Data.Word as W

import qualified Data.NonEmpty.Class as NonEmptyC


type Vector4 = LLVM.Vector TypeNum.D4 Float
type Vector8 = LLVM.Vector TypeNum.D8 Float
type Vector = Vector4

vector :: Vector
vector = LLVM.vector $ NonEmptyC.iterate (1.2+) (-1.7 :: Float)

roundpsExtern4 ::
   LLVM.CodeGenFunction r
      (LLVM.Function (Vector4 -> W.Word32 -> IO Vector4))
roundpsExtern4 =
   LLVM.externFunction "llvm.x86.sse41.round.ps"

roundpsExtern8 ::
   LLVM.CodeGenFunction r
      (LLVM.Function (Vector8 -> W.Word32 -> IO Vector8))
roundpsExtern8 =
   LLVM.externFunction "llvm.x86.avx.round.ps.256"

roundps ::
   LLVM.Value Vector -> LLVM.Value W.Word32 ->
   LLVM.CodeGenFunction s (LLVM.Value Vector)
roundps xs mode = do
   f <- roundpsExtern4
   LLVM.call f xs mode

modul ::
   LLVM.CodeGenModule (LLVM.Function (Ptr Vector -> Ptr Vector -> IO ()))
modul =
   LLVM.createFunction LLVM.ExternalLinkage $ \ptr0 ptr1 -> do
      flip LLVM.store ptr1 =<< flip roundps (LLVM.valueOf 1) =<< LLVM.load ptr0
      LLVM.ret ()

type Importer func = FunPtr func -> func

foreign import ccall safe "dynamic" derefFloorPtr ::
   Importer (Ptr Vector -> Ptr Vector -> IO ())

run :: IO ()
run = do
   m <- LLVM.newModule
   floorFunc <- do
      func <- LLVM.defineModule m $ LLVM.setTarget LLVM.hostTriple >> modul
      EE.runEngineAccessWithModule m $
         EE.getExecutionFunction derefFloorPtr func
   LLVM.writeBitcodeToFile "floor.bc" m

   print vector
   MU.with vector $ \ptr0 ->
      alloca $ \ptr1 -> do
         floorFunc ptr0 ptr1
         print =<< peek ptr1


main :: IO ()
main = do
   LLVM.initializeNativeTarget
   run
