alpha-1.0.15: src/Alpha.hs
{-# LANGUAGE ViewPatterns, ImplicitParams, NoMonomorphismRestriction, ParallelListComp, TupleSections, CPP, ForeignFunctionInterface, MultiParamTypeClasses, RankNTypes #-}
import Bindings.Posix.Sys.Mman
import Bindings.Posix.Unistd
import Compile
import Context as C
import Control.Category ((>>>))
import Data.ByteString.Internal
import Data.ByteString.Unsafe
import Data.Functor.Identity
import Data.IORef
import Data.List
import Data.Maybe
import Data.Monoid
import Data.Ord
import Data.Version
import Foreign hiding (unsafePerformIO,unsafeForeignPtrToPtr,void)
import Foreign.C
import Foreign.ForeignPtr.Unsafe
import Format
import ID
import My.Control.Monad
import My.Control.Monad.State
import My.Prelude
import Options
import PCode
import Paths_alpha (version)
import Serialize
import Specialize
import Specialize.Architecture
import Syntax
import Syntax.Parse
import System.Directory
import System.Environment as SE
import System.FilePath
import System.IO
import System.IO.Unsafe (unsafePerformIO)
import System.Posix.Files
import qualified Data.Bimap as BM
import qualified Data.ByteString as B
import qualified Data.Map as M
import qualified Data.Serialize as Ser
-- The mprotect function in System.Posix.Mman doesn't have the right type signature, so we reimport it here
foreign import ccall "mprotect" mprotect :: Ptr () -> CSize -> CInt -> IO CInt
newtype Str = Str String
instance Show Str where show (Str s) = s
withSettings :: ((?settings :: Settings) => IO a) -> IO a
withSettings m = gets settings >>= \s -> let ?settings = s in m
main = do
args <- getArgs
case getSettings args of
Right s -> do
put (C s undefined undefined undefined undefined undefined)
withSettings $ case (action ?settings,programs ?settings) of
(PrintHelp,_) -> printHelp
(PrintVersion,_) -> printVersion
(Compile,[]) -> interactive
(Compile,progs) -> mapM_ compileProgram progs
Left err -> fail err
printHelp = putStrLn helpMsg
printVersion = putStrLn $ "Alpha version "++showVersion version
entry = formatEntry $ outputFmt ?settings
languageFile language = languageDir ?settings</>language<.>"l"
findSource language = findM doesFileExist [dir</>language<.>"a" | dir <- sourceDirs ?settings]
interactive = withInitialContext $ do
putStrLn $ "Alpha, version "++showVersion version++". Type alpha/help() for help on Alpha invocation. ^D to exit."
str <- getContents
putStr "> " >> hFlush stdout
mapM_ (\e -> compileExpr e >> putStr "> " >> hFlush stdout) (parseAlpha "/dev/stdin" str)
putStrLn "\rGoodbye !"
compileProgram (language,entryName) = withInitialContext $ do
langTime <- importLanguage (const $ return ()) language
skipLink <- fileExist entryName <&&> liftM (langTime <=) (modTime entryName)
unless skipLink $ tagIO ("Linking program "++entryName) $ do
entrySym <- viewState language_ $ internSym entryName
_ <- getAddressComp (outputArch ?settings) entrySym
(addrs,ptrs) <- unzip $< sortBy (comparing fst) $< M.elems $< gets compAddresses
top <- gets compTop
contents <- B.concat $< sequence [withForeignPtr ptr $ \p -> unsafePackCStringLen (castPtr p,size)
| ptr <- ptrs | size <- zipWith (-) (tail addrs++[top]) addrs]
writeFormat (outputFmt ?settings) entryName contents
modTime f = modificationTime $< getFileStatus f
compileLanguage force name = do
let langFile = languageFile name
newerThan f1 f2 = liftM2 (>=) (modTime f1) (modTime f2)
source <- findSource name
skip <- return (not force) <&&> fileExist langFile <&&> maybe (return True) (langFile `newerThan`) source
l <- if skip then either (\e -> error $ "Error reading language file "++langFile++": "++e)
id $< Ser.decode $< B.readFile langFile else do
tagIO ("Compiling language "++name) $ do
lang <- compileFile $ fromMaybe (error $ "Couldn't find source file for language "++name) source
createDirectoryIfMissing True (dropFileName langFile)
B.writeFile langFile (Ser.encode lang)
return lang
time <- modTime langFile
return (time,l)
where compileFile src = withInitialContext $ do
str <- readFile src
inits <- mapM compileExpr (parseAlpha src str)
getting (language_ >>> f_ (purgeLanguage >>> (,inits)))
importLanguage loadImport = import'
where import' lang = gets language >>= \l -> ifThenElse (lang`isImport`l) (modTime $ languageFile lang) $ do
node@(time,(l',_)) <- compileLanguage False lang
times <- mapM import' (getImports l')
node <- if any (> time) times then compileLanguage True lang else return node
let (time,(l'',init)) = node
l' = l''{ nameL = lang }
init' <- viewing language_ $ modify (<>l') >> gets (translateInit init l')
loadImport init'
return time
compileExpr expr = do
expr <- doTransform =<< viewing language_ (envCast expr)
code <- viewing language_ $ compile [] Nothing expr
internCode code >>= \s -> runSym s >> return s
internCode code = viewing language_ $ state createSym >>= \s -> modify (exportSymVal s (Verb code)) >> return s
#define DYNAMIC(name,type) foreign import ccall "dynamic" name :: FunPtr (type) -> type
DYNAMIC(mkProc,Ptr() -> IO ())
DYNAMIC(mkFunSize,Ptr() -> IO Int)
DYNAMIC(mkFunInit,Ptr() -> Ptr() -> IO ())
DYNAMIC(mkFunTransform,Ptr() -> Ptr() -> IO (Ptr()))
funPtrToInteger f = fromIntegral $ ptrToIntPtr $ castFunPtrToPtr f
exportAlpha stub ptr = unsafePerformIO $ do
unsafeUseAsCStringLen (stub $ funPtrToInteger ptr) $ \(src,size) -> do
ptr <- mallocForeignPtrBytes size
withForeignPtr ptr $ \dst -> copyBytes (castPtr dst) src size
return ptr
initialBindings = [(n,Left $ Builtin b) | (b,n) <- bNames] ++ [
("alter" ,Left $ Axiom XAlter),
("bind" ,Left $ Axiom XBind),
("choose" ,Left $ Axiom XChoose),
("<-" ,Left $ Axiom XRestart),
("->" ,Left $ Axiom XReturn),
("do" ,Left $ Axiom XDo),
("@" ,Left $ Axiom XAddr),
("#" ,Left $ Axiom XSize),
("id" ,Left $ Axiom XID),
("verb" ,Left $ Axiom XVerb),
("noun" ,Left $ Axiom XNoun),
("lang" ,Left $ Axiom XLang)] ++ [
("alpha/c@" , Right $ exportAlpha callStub1 alpha_compAddr),
("alpha/create-symbol" , Right $ exportAlpha callStub0 alpha_createSym),
("alpha/number-symbol" , Right $ exportAlpha callStub1 alpha_numSym),
("alpha/symbol-name" , Right $ exportAlpha callStub1 alpha_symName),
("alpha/name-symbol" , Right $ exportAlpha callStub1 alpha_nameSym),
("alpha/set-transform" , Right $ exportAlpha callStub1 alpha_setTransform),
("alpha/import" , Right $ exportAlpha callStub1 alpha_import),
("alpha/reload" , Right $ exportAlpha callStub0 alpha_reload),
("alpha/allocate" , Right $ exportAlpha callStub1 alpha_allocate),
("alpha/free" , Right $ exportAlpha callStub1 alpha_free),
("alpha/help" , Right $ exportAlpha callStub0 alpha_printHelp),
-- debugging functions
("alpha/list" , Right $ exportAlpha callStub0 alpha_printList),
("alpha/lang" , Right $ exportAlpha callStub0 alpha_printLang),
("alpha/print-OK" , Right $ exportAlpha callStub0 alpha_printOK),
("alpha/print-num" , Right $ exportAlpha callStub1 alpha_printNum),
("alpha/print-expr" , Right $ exportAlpha callStub1 alpha_printExpr),
("alpha/print-val" , Right $ exportAlpha callStub1 alpha_printVal)
]
#define str(x) #x
#define ALPHA_EXPORT(fun,t) foreign export ccall str(__alpha_##fun) __alpha_##fun :: t ; foreign import ccall str(&__alpha_##fun) alpha_##fun :: FunPtr (t) ; __alpha_##fun
ALPHA_EXPORT(setTransform,Ptr () -> IO ()) fun = modify $ \c -> c { transform = Just fun }
ALPHA_EXPORT(compAddr,ID -> IO Int) id = readIORef compAddrRef >>= ($id)
ALPHA_EXPORT(symName,ID -> IO (Ptr Word8)) sym = do
n <- gets (lookupSymName sym . language)
ret <- newArray0 0 (map c2w $ fromMaybe "" n)
return ret
ALPHA_EXPORT(nameSym,Ptr Word8 -> IO ID) p = do
l <- peekArray0 0 p
viewState language_ (internSym $ map w2c l)
ALPHA_EXPORT(createSym,IO ID) = viewState language_ createSym
ALPHA_EXPORT(numSym,Int -> IO ID) = viewState language_ . internSym . show
ALPHA_EXPORT(allocate,Int -> IO (Ptr())) = mallocBytes
ALPHA_EXPORT(free,Ptr() -> IO ()) = free
ALPHA_EXPORT(printVal,ID -> IO ()) sym = gets (language >>> lookupSymVal sym) >>= print
ALPHA_EXPORT(printHelp,IO()) = printHelp
ALPHA_EXPORT(printOK,IO()) = putStrLn "OK"
ALPHA_EXPORT(printNum,Int -> IO()) n = print (intPtrToPtr $ fromIntegral n)
ALPHA_EXPORT(printList,IO()) = do
syms <- getting (language_ >>> syms_)
putStrLn $ intercalate " " (map fst $ BM.toList syms)
ALPHA_EXPORT(printLang,IO()) = getting language_ >>= print
ALPHA_EXPORT(printExpr,Ptr() -> IO()) = readTree >=> transform >=> print
where transform t = do
smap <- getting (language_ >>> syms_)
let symName s = fromMaybe (show s) $ BM.lookupR s smap
return (fmap (Str . symName) t)
ALPHA_EXPORT(import,ID -> IO()) sym = withSettings $ do
name <- getting (language_ >>> f_ (lookupSymName sym))
void $ case name of
Just name -> importLanguage (mapM_ runSym) name
Nothing -> error $ "Symbol "++show sym++" has no name."
ALPHA_EXPORT(reload,IO()) = withSettings $ do
imports <- getting (language_ >>> f_ (languagesL >>> BM.toList >>> map fst))
put initialContext
mapM_ (importLanguage (mapM_ runSym)) imports
compAddrRef = unsafePerformIO $ newIORef (undefined :: ID -> IO Int)
contextRef = unsafePerformIO $ newIORef (error "Undefined context" :: Context)
instance MonadState Context IO where
get = readIORef contextRef
put = writeIORef contextRef
withInitialContext = swapping id_ initialContext
initialContext = C ?settings lang jitA M.empty (fromIntegral entry) Nothing
where (lang,jitA) = execState (mapM_ st initialBindings) (mempty,M.empty)
where st (s,v) = do
i <- viewState fst_ (internSym s)
case v of
Left v -> modifying fst_ (setSymVal i v)
Right p -> modifying snd_ (M.insert i p)
pageSize = fromIntegral $ unsafePerformIO $ c'sysconf c'_SC_PAGESIZE
enableExec p size = do
let p' = alignPtr (p`plusPtr`(1-pageSize)) pageSize
mprotect (castPtr p') (fromIntegral $ size+ p`minusPtr`p') (c'PROT_READ .|. c'PROT_WRITE .|. c'PROT_EXEC)
callSym :: (FunPtr (Ptr() -> a) -> Ptr() -> a) -> ByteString -> ID -> (a -> IO b) -> IO b
callSym wrap stub sym f = unsafeUseAsCString stub $ \stub -> do
p <- getAddressJIT sym
f $ wrap (castPtrToFunPtr stub) (intPtrToPtr $ fromIntegral p)
runSym sym = callSym mkProc execStub sym id
withRef ref val x = readIORef ref >>= \v -> writeIORef ref val >> x >>= \x -> writeIORef ref v >> return x
getAddress arch lookup register = withRef compAddrRef getAddr . getAddr
where
getAddr sym = lookup sym >>= \val -> case val of
Just a -> return a
Nothing -> gets language >>= \lang -> (>> getAddr sym) $ case lookupSymVal sym lang of
Verb c -> void $ do
let (size,codem) = specialize arch (sym,getAddr) c
ptr <- mallocForeignPtrBytes size
register sym ptr size
code <- codem
withForeignPtr ptr $ \p -> do
unsafeUseAsCStringLen code $ \(p',n) -> copyBytes p (castPtr p') n
enableExec p size
Noun sizeCode initCode -> do
[sizeSym,initSym] <- mapM internCode [sizeCode,initCode]
size <- callSym mkFunSize execStub sizeSym id
ptr <- mallocForeignPtrBytes size
register sym ptr size
withForeignPtr ptr $ \p -> callSym mkFunInit initStub initSym ($castPtr p)
_ -> fail $ "Couldn't find definition of symbol "++fromMaybe (show sym) (lookupSymName sym lang)
getAddressJIT = getAddress arch_host lookup register
where lookup id = do
val <- M.lookup id $< gets jitAddresses
return $ (fromIntegral . ptrToIntPtr . unsafeForeignPtrToPtr) $< val
register id ptr size = modifying jitAddresses_ (M.insert id ptr)
getAddressComp arch = getAddress arch lookup register
where lookup id = (fst$<) $< M.lookup id $< gets compAddresses
register id ptr size = do
n <- getting compTop_
modifying compAddresses_ (M.insert id (n,ptr))
modifying compTop_ (+size)
doTransform syn = gets transform >>= ($syn) . maybe return tr
where tr fun tree = do
root <- allocTree tree
new <- unsafeUseAsCString initStub $ \stub -> mkFunTransform (castPtrToFunPtr stub) fun root
readTree new
intSize = sizeOf (undefined::Int) ; ptrSize = sizeOf (undefined::Ptr())
pok e p = poke (castPtr p) e >> return (p`plusPtr`sizeOf e)
pik p = peek (castPtr p) >>= \e -> return (e,p`plusPtr`sizeOf e)
allocTree (Group g) = do
p <- mallocBytes (intSize+intSize+(length g*ptrSize))
p' <- pok (0::Int) p
p'' <- pok (length g) p'
mapM allocTree g >>= \l -> pokeArray (castPtr p'') l
return p
allocTree (Symbol (ID s)) = do
p <- mallocBytes (intSize+intSize)
p' <- pok (1::Int) p
pok s p'
return p
readTree p = do
(t,p') <- pik p
case t :: Int of
0 -> do
(s,p'') <- pik p'
l <- peekArray s p''
liftM Group $ mapM readTree l
1 -> do
liftM (Symbol . ID) $ peek p'