{-# LANGUAGE
  FlexibleContexts,
  ForeignFunctionInterface,
  OverloadedStrings
  #-}
module LLVM.Test.ExecutionEngine where

import Test.Tasty
import Test.Tasty.HUnit

import LLVM.Test.Support

import Control.Monad
import Data.Functor
import Data.Maybe

import Foreign.Ptr
import Data.Word

import LLVM.Context
import LLVM.Module
import LLVM.ExecutionEngine
import LLVM.AST
import LLVM.AST.Type
import LLVM.AST.Name
import LLVM.AST.AddrSpace
import qualified LLVM.AST.IntegerPredicate as IPred

import qualified LLVM.AST.Linkage as L
import qualified LLVM.AST.Visibility as V
import qualified LLVM.AST.CallingConvention as CC
import qualified LLVM.AST.Attribute as A
import qualified LLVM.AST.Global as G
import qualified LLVM.AST.Constant as C

foreign import ccall "dynamic" mkIO32Stub :: FunPtr (Word32 -> IO Word32) -> (Word32 -> IO Word32)

testJIT :: ExecutionEngine e (FunPtr ()) => (Context -> (e -> IO ()) -> IO ()) -> Assertion
testJIT withEE = withContext $ \context -> withEE context $ \executionEngine -> do
  let mAST = Module "runSomethingModule" "runSomethingModule" Nothing Nothing [
              GlobalDefinition $ functionDefaults {
                G.returnType = i32,
                G.name = Name "_foo",
                G.parameters = ([Parameter i32 (Name "bar") []],False),
                G.basicBlocks = [
                  BasicBlock (UnName 0) [] (
                    Do $ Ret (Just (ConstantOperand (C.Int 32 42))) []
                  )
                ]
               }
              ]

  s <- withModuleFromAST context mAST $ \m -> do
        withModuleInEngine executionEngine m $ \em -> do
          Just p <- getFunction em (Name "_foo")
          (mkIO32Stub ((castFunPtr p) :: FunPtr (Word32 -> IO Word32))) 7
  s @?= 42

tests = testGroup "ExecutionEngine" [
  testCase "run something with MCJIT" $ testJIT (\c -> withMCJIT c Nothing Nothing Nothing Nothing)
 ]