module Feldspar.Compiler.Plugin
( loadFun
, loadFunOpts
, loadFunWithConfig
, defaultConfig
, pack
, unpack
)
where
import GHC.Paths (ghc)
import System.Plugins (initLinker, loadRawObject, resolveObjs)
import System.Plugins.MultiStage
import Data.List (isPrefixOf)
import Distribution.Verbosity (silent,verbose)
import Distribution.Simple.Utils (defaultPackageDesc)
import Distribution.PackageDescription
import Distribution.PackageDescription.Parse (readPackageDescription)
import Distribution.PackageDescription.Configuration (flattenPackageDescription)
import Feldspar.Compiler.CallConv (rewriteType, buildCType, buildHaskellType)
import Data.Default
import Foreign.Ptr
import Foreign.Marshal (with)
import Foreign.Marshal.Unsafe (unsafeLocalState)
import Foreign.Storable (Storable(..))
import Foreign.C.String (CString, withCString)
import Control.Monad (join, (>=>), when, unless)
import Control.Applicative
import Language.Haskell.TH
import System.Directory (doesFileExist, removeFile, createDirectoryIfMissing)
import System.Process (readProcessWithExitCode)
import System.Exit (ExitCode(..))
import System.Info (os)
import System.IO.Unsafe (unsafePerformIO)
import Feldspar.Runtime
import Feldspar.Compiler (compile, defaultOptions)
import Feldspar.Compiler.Backend.C.Library (encodeFunctionName)
import Feldspar.Compiler.Marshal ()
feldsparPluginConfig :: Config
feldsparPluginConfig =
defaultConfig { builder = feldsparBuilder
, worker = feldsparWorker
, typeFromName = loadFunType >=> rewriteType
, mkHSig = buildHaskellType
, mkCSig = buildCType
}
loadFun :: [Name] -> Q [Dec]
loadFun = loadFunWithConfig feldsparPluginConfig
loadFunOpts :: [String] -> [Name] -> Q [Dec]
loadFunOpts o = loadFunWithConfig feldsparPluginConfig{opts = o}
feldsparWorker :: Name -> [Name] -> Q Body
feldsparWorker fun as = normalB
[|with def $ \outPtr -> do
join $(infixApp (apply ([|pure $(varE fun)|] : map toRef as)) [|(<*>)|] [|pure outPtr|])
peek outPtr >>= from
|]
where
toRef name = [| pack $(varE name) |]
apply :: [ExpQ] -> ExpQ
apply [] = error "apply []"
apply [x] = x
apply (x:y:zs) = apply (infixApp x [|(<*>)|] y : zs)
feldsparBuilder :: Config -> Name -> Q Body
feldsparBuilder Config{..} fun = do
let db = getDB
let opts' = opts ++ map ("-I"++) db
normalB [|unsafeLocalState $ do
createDirectoryIfMissing True wdir
$(varE 'compile) $(varE fun) basename base defaultOptions
compileAndLoad basename opts'
lookupSymbol symbol
|]
where
base = nameBase fun
basename = wdir ++ "/" ++ base
symbol = ldprefix ++ encodeFunctionName base
ldprefix = case os of
"darwin" -> "_"
_ -> ""
getDB :: [String]
getDB = unsafePerformIO $ do
dirs <- sequence [ sandbox, user, local ]
putStrLn $ unwords $ "Using feldspar runtime in" : concat dirs
return $ concat dirs
where
sandbox = do
(c,d,_) <- readProcessWithExitCode "cabal" ["sandbox", "hc-pkg","field","feldspar-compiler","include-dirs"] ""
case c of
ExitSuccess -> return $ drop 1 $ words d
_ -> return []
user = do
(c,d,_) <- readProcessWithExitCode "ghc-pkg" ["field","feldspar-compiler","include-dirs"] ""
case c of
ExitSuccess -> return $ drop 1 $ words d
_ -> return []
local = do
pd <- readPackageDescription verbose =<< defaultPackageDesc verbose
let f a = return $ includeDirs $ libBuildInfo a
maybe (return []) f (maybeHasLibs $ flattenPackageDescription pd)
maybeHasLibs :: PackageDescription -> Maybe Library
maybeHasLibs p =
library p >>= \lib -> if buildable (libBuildInfo lib)
then Just lib
else Nothing
compileAndLoad :: String -> [String] -> IO ()
compileAndLoad name opts = do
let cname = name ++ ".c"
let oname = name ++ ".o"
exists <- doesFileExist oname
when exists $ removeFile oname
compileC cname oname opts
initLinker
_ <- loadRawObject oname
resolveObjs $ error $ "Symbols in " ++ oname ++ " could not be resolved"
compileC :: String -> String -> [String] -> IO ()
compileC srcfile objfile opts = do
let args = [ "-optc -std=c99"
, "-optc -Wall"
, "-w"
, "-c"
]
(_,stdout,stderr) <- readProcessWithExitCode ghc (args ++ opts ++ ["-o",objfile,srcfile]) ""
let output = stdout ++ stderr
unless (null output) $ putStrLn output
lookupSymbol :: String -> IO (Ptr a)
lookupSymbol symbol = do
when (0 /= feldspar_compiler_hook) $ error "lookupSymbol: Runtime library missing"
mptr <- withCString symbol _lookupSymbol
when (mptr == nullPtr) $ error $ "Symbol " ++ symbol ++ " not found"
return mptr
foreign import ccall safe "lookupSymbol"
_lookupSymbol :: CString -> IO (Ptr a)