{-# LANGUAGE TypeFamilies #-}
 -- |An 'ExecutionEngine' is JIT compiler that is used to generate code for an LLVM module.
module LLVM.ExecutionEngine(
    -- * Execution engine
    EngineAccess,
    ExecutionEngine,
    getEngine,
    runEngineAccess,
    runEngineAccessWithModule,
    addModule,
    ExecutionFunction,
    getExecutionFunction,
    getPointerToFunction,
    addFunctionValue,
    addGlobalMappings,
    -- * Translation
    Translatable, Generic,
    generateFunction,
    -- * Unsafe type conversion
    Unsafe,
    unsafeRemoveIO,
    -- * Simplified interface.
    simpleFunction,
    unsafeGenerateFunction,
    -- * Target information
    module LLVM.ExecutionEngine.Target
    ) where

import LLVM.ExecutionEngine.Engine
import LLVM.ExecutionEngine.Target
import LLVM.Core.CodeGen (Value(..))
import LLVM.Core
         (CodeGenModule, Function, newModule, defineModule, getGlobalMappings,
          setTarget, hostTriple)

import LLVM.FFI.Core (ValueRef)

import System.IO.Unsafe (unsafePerformIO)

import Control.Monad (liftM2, )


-- |Class of LLVM function types that can be translated to the corresponding
-- Haskell type.
class Translatable f where
    translate :: (ValueRef -> [GenericValue] -> IO GenericValue) -> [GenericValue] -> ValueRef -> f

instance (Generic a, Translatable b) => Translatable (a -> b) where
    translate run args f = \ arg -> translate run (toGeneric arg : args) f

instance (Generic a) => Translatable (IO a) where
    translate run args f = fmap fromGeneric $ run f $ reverse args

-- |Generate a Haskell function from an LLVM function.
--
-- Note that the function is compiled for every call (Just-In-Time compilation).
-- If you want to compile the function once and call it a lot of times
-- then you should better use 'getPointerToFunction'.
generateFunction ::
    (Translatable f) =>
    Function f -> EngineAccess f
generateFunction (Value f) = do
    run <- getRunFunction
    return $ translate run [] f

class Unsafe a where
    type RemoveIO a :: *
    unsafeRemoveIO :: a -> RemoveIO a  -- ^Remove the IO from a function return type.  This is unsafe in general.

instance (Unsafe b) => Unsafe (a->b) where
    type RemoveIO (a -> b) = a -> RemoveIO b
    unsafeRemoveIO f = unsafeRemoveIO . f

instance Unsafe (IO a) where
    type RemoveIO (IO a) = a
    unsafeRemoveIO = unsafePerformIO

-- |Translate a function to Haskell code.  This is a simplified interface to
-- the execution engine and module mechanism.
-- It is based on 'generateFunction', so see there for limitations.
simpleFunction :: (Translatable f) => CodeGenModule (Function f) -> IO f
simpleFunction bld = do
    m <- newModule
    (func, mappings) <-
        defineModule m $
            setTarget hostTriple >> liftM2 (,) bld getGlobalMappings
    runEngineAccessInterpreterWithModule m $ do
        addGlobalMappings mappings
        generateFunction func

-- | Combine 'simpleFunction' and 'unsafeRemoveIO'.
unsafeGenerateFunction :: (Unsafe t, Translatable t) =>
                          CodeGenModule (Function t) -> RemoveIO t
unsafeGenerateFunction bld = unsafePerformIO $ do
    fun <- simpleFunction bld
    return $ unsafeRemoveIO fun
