packages feed

llvm-general-3.2.4.2: src/LLVM/General/Internal/FFI/Cleanup.hs

{-# LANGUAGE
  TemplateHaskell
  #-}
module LLVM.General.Internal.FFI.Cleanup where

import Language.Haskell.TH
import Control.Monad
import Data.Sequence as Seq
import Data.Foldable (toList)

import Data.Word
import Data.Int
import Foreign.C
import Foreign.Ptr

import LLVM.General.Internal.FFI.LLVMCTypes
import qualified LLVM.General.Internal.FFI.PtrHierarchy as FFI

import qualified LLVM.General.AST.IntegerPredicate as A (IntegerPredicate) 
import qualified LLVM.General.AST.FloatingPointPredicate as A (FloatingPointPredicate) 
import qualified LLVM.General.AST.Constant as A.C (Constant)
import qualified LLVM.General.AST.Operand as A (Operand)
import qualified LLVM.General.AST.Type as A (Type)

foreignDecl :: String -> String -> [TypeQ] -> TypeQ -> DecsQ
foreignDecl cName hName argTypeQs returnTypeQ = do
  let foreignDecl' hName argTypeQs = 
        forImpD cCall unsafe cName (mkName hName) 
                  (foldr (\a b -> appT (appT arrowT a) b) (appT (conT ''IO) returnTypeQ) argTypeQs)
      splitTuples :: [Type] -> Q ([Type], [Pat], [Exp])
      splitTuples ts = do
        let f :: Type -> Q (Seq Type, Pat, Seq Exp)
            f x@(AppT _ _) = maybe (d x) (\q -> q >>= \(ts, ps, es) -> return (ts, TupP (toList ps), es)) (g 0 x)
            f x = d x
            g :: Int -> Type -> Maybe (Q (Seq Type, Seq Pat, Seq Exp))
            g n (TupleT m) | m == n = return (return (Seq.empty, Seq.empty, Seq.empty))
            g n (AppT a b) = do
              k <- g (n+1) a
              return $ do
                (ts, ps, es) <- k
                (ts', p', es') <- f b
                return (ts >< ts', ps |> p', es >< es')
            g _ _ = Nothing
            d :: Type -> Q (Seq Type, Pat, Seq Exp)
            d x = do
              n <- newName "v"
              return (Seq.singleton x, VarP n, Seq.singleton (VarE n))
            seqsToList :: [Seq a] -> [a]
            seqsToList = toList . foldr (><) Seq.empty
                
        (tss, ps, ess) <- liftM unzip3 . mapM f $ ts
        return (seqsToList tss, ps, seqsToList ess)

                                
  argTypes <- sequence argTypeQs
  (ts, ps, es) <- splitTuples argTypes
  let phName = hName ++ "'"
  sequence [
    foreignDecl' phName (map return ts),
    funD (mkName hName) [
     clause (map return ps) (normalB (foldl appE (varE (mkName phName)) (map return es))) []
    ]
   ]

-- | The LLVM C-API for instructions with boolean flags (e.g. nsw) is weak, so they get
-- separated out for different handling. This check is an accurate but crude test for whether
-- an instruction needs such handling. As such it may need revision in the future (if has-a-boolean-member
-- is no longer the same as needs-special-handling).
hasFlags :: [Type] -> Bool
hasFlags = any (== ConT ''Bool)

typeMapping :: Type -> TypeQ
typeMapping t = case t of
  ConT h | h == ''Bool -> [t| LLVMBool |]
         | h == ''Int32 -> [t| CInt |]
         | h == ''Word32 -> [t| CUInt |]
         | h == ''String -> [t| CString |]
         | h == ''A.Operand -> [t| Ptr FFI.Value |]
         | h == ''A.Type -> [t| Ptr FFI.Type |]
         | h == ''A.C.Constant -> [t| Ptr FFI.Constant |]
         | h == ''A.FloatingPointPredicate -> [t| FCmpPredicate |]
         | h == ''A.IntegerPredicate -> [t| ICmpPredicate |]
  AppT ListT x -> foldl1 appT [tupleT 2, [t| CUInt |], appT [t| Ptr |] (typeMapping x)]