1{-# LANGUAGE PatternGuards, TypeSynonymInstances, CPP #-}
3module IRTS.Compiler where
6import IRTS.Defunctionalise
8import IRTS.CodegenCommon
10import IRTS.CodegenJava
12import IRTS.CodegenJavaScript
14import IRTS.CodegenLLVM
21import Idris.AbsSyntaxTree
28import Idris.Core.Evaluate
29import Idris.Core.CaseTree
31import Control.Applicative
32import Control.Monad.State
35import Data.IntSet (IntSet)
36import qualified Data.IntSet as IS
37import qualified Data.Map as M
38import qualified Data.Set as S
41import System.Directory
42import System.Environment
43import System.FilePath ((</>), addTrailingPathSeparator)
47compile :: Codegen -> FilePath -> Term -> Idris ()
49 = do checkMVs -- check for undefined metavariables
50 reachableNames <- performUsageAnalysis
52 iLOG $ "MAIN: " ++ show maindef
53 objs <- getObjectFiles codegen
54 libs <- getLibs codegen
55 flags <- getFlags codegen
56 hdrs <- getHdrs codegen
57 impdirs <- allImportDirs
58 defsIn <- mkDecls tm reachableNames
59 let defs = defsIn ++ [(sMN 0 "runMain", maindef)]
60 -- iputStrLn $ showSep "\n" (map show defs)
61 let (nexttag, tagged) = addTags 65536 (liftAll defs)
62 let ctxtIn = addAlist tagged emptyContext
63 iLOG "Defunctionalising"
64 let defuns_in = defunctionalise nexttag ctxtIn
65 logLvl 5 $ show defuns_in
67 let defuns = inline defuns_in
68 logLvl 5 $ show defuns
69 iLOG "Resolving variables for CG"
70 -- iputStrLn $ showSep "\n" (map show (toAlist defuns))
71 let checked = checkDefs defuns (toAlist defuns)
73 dumpCases <- getDumpCases
74 dumpDefun <- getDumpDefun
77 Just f -> runIO $ writeFile f (showCaseTrees defs)
80 Just f -> runIO $ writeFile f (dumpDefuns defuns)
81 triple <- Idris.AbsSyntax.targetTriple
82 cpu <- Idris.AbsSyntax.targetCPU
84 iLOG "Building output"
87 OK c -> do let cginfo = CodegenInfo f outty triple cpu optimise
88 hdrs impdirs objs libs flags
89 NONE c (toAlist defuns)
91 runIO $ case codegen of
92 ViaC -> codegenC cginfo
93 ViaJava -> codegenJava cginfo
94 ViaJavaScript -> codegenJavaScript cginfo
95 ViaNode -> codegenNode cginfo
96 ViaLLVM -> codegenLLVM cginfo
97 Bytecode -> dumpBC c f
99 where checkMVs = do i <- getIState
100 case map fst (idris_metavars i) \\ primDefs of
102 ms -> ifail $ "There are undefined metavariables: " ++ show ms
103 inDir d h = do let f = d </> h
104 ex <- doesFileExist f
105 if ex then return f else return h
107irMain :: TT Name -> Idris LDecl
108irMain tm = do i <- ir tm
109 return $ LFun [] (sMN 0 "runMain") [] (LForce i)
111mkDecls :: Term -> [Name] -> Idris [(Name, LDecl)]
114 let ds = filter (\(n, d) -> n `elem` used || isCon d) $ ctxtAlist (tt_ctxt i)
115 decls <- mapM build ds
118showCaseTrees :: [(Name, LDecl)] -> String
119showCaseTrees ds = showSep "\n\n" (map showCT ds)
121 showCT (n, LFun _ f args lexp)
122 = show n ++ " " ++ showSep " " (map show args) ++ " =\n\t "
124 showCT (n, LConstructor c t a) = "data " ++ show n ++ " " ++ show a
126isCon (TyDecl _ _) = True
130 ir :: a -> Idris LExp
132build :: (Name, Def) -> Idris (Name, LDecl)
135 case lookup n (idris_scprims i) of
137 let args = map (\x -> sMN x "op") [0..] in
138 return (n, (LFun [] n (take ar args)
139 (LOp op (map (LV . Glob) (take ar args)))))
140 _ -> do def <- mkLDecl n d
141 logLvl 3 $ "Compiled " ++ show n ++ " =\n\t" ++ show def
144declArgs args inl n (LLam xs x) = declArgs (args ++ xs) inl n x
145declArgs args inl n x = LFun (if inl then [Inline] else []) n args x
147mkLDecl n (Function tm _) = do e <- ir tm
148 return (declArgs [] True n e)
149mkLDecl n (CaseOp ci _ _ _ pats cd)
150 = let (args, sc) = cases_runtime cd in
151 do e <- ir (args, sc)
152 return (declArgs [] (case_inlinable ci) n e)
154mkLDecl n (TyDecl (DCon tag arity) _) = do
157 let realArity = case lookupCtxt n (idris_callgraph ist) of
158 [CGInfo _ _ _ _ xs] -> length xs
161 return $ LConstructor n tag realArity
163mkLDecl n (TyDecl (TCon t a) _) = return $ LConstructor n (-1) a
164mkLDecl n _ = return $ (declArgs [] True n LNothing) -- postulate, never run
166instance ToIR (TT Name) where
167 ir tm = ir' [] tm where
169 | (P _ (UN m) _, args) <- unApply tm,
170 m == txt "mkForeignPrim"
172 | (P _ (UN u) _, [_, arg]) <- unApply tm,
173 u == txt "unsafePerformPrimIO"
175 -- TMP HACK - until we get inlining.
176 | (P _ (UN r) _, [_, _, _, _, _, arg]) <- unApply tm,
179 -- Laziness, the old way
180 | (P _ (UN l) _, [_, arg]) <- unApply tm,
182 = do arg' <- ir' env arg
183 error "lazy has crept in somehow"
184-- return $ LLazyExp arg'
185 | (P _ (UN l) _, [_, arg]) <- unApply tm,
187 = do arg' <- ir' env arg
189 -- Laziness, the new way
190 | (P _ (UN l) _, [_, arg]) <- unApply tm,
192 = do arg' <- ir' env arg
193 return $ LLazyExp arg'
194 | (P _ (UN l) _, [_, _, arg]) <- unApply tm,
196 = do arg' <- ir' env arg
198 | (P _ (UN a) _, [_, _, arg]) <- unApply tm,
199 a == txt "assert_smaller"
201 | (P _ (UN a) _, [_, arg]) <- unApply tm,
202 a == txt "assert_total"
204 | (P _ (UN p) _, [_, arg]) <- unApply tm,
206 = do arg' <- ir' env arg
207 return $ LOp LPar [LLazyExp arg']
208 | (P _ (UN pf) _, [arg]) <- unApply tm,
209 pf == txt "prim_fork"
210 = do arg' <- ir' env arg
211 return $ LOp LFork [LLazyExp arg']
212 | (P _ (UN m) _, [_,size,t]) <- unApply tm,
214 = do size' <- ir' env size
216 return t' -- TODO $ malloc_ size' t'
217 | (P _ (UN tm) _, [_,t]) <- unApply tm,
218 tm == txt "trace_malloc"
222 -- This case is here until we get more general inlining. It's just
223 -- a really common case, and the laziness hurts...
224 | (P _ (NS (UN be) [b,p]) _, [_,x,(App (P _ (UN d) _) t),
225 (App (P _ (UN d') _) e)]) <- unApply tm,
226 be == txt "boolElim" && d == txt "Delay" && d' == txt "Delay"
230 return (LCase x' [LConCase 0 (sNS (sUN "False") ["Bool","Prelude"]) [] e',
231 LConCase 1 (sNS (sUN "True") ["Bool","Prelude"]) [] t'])
233 | (P (DCon t a) n _, args) <- unApply tm = do
234 cg <- idris_callgraph <$> getIState
235 let used = maybe [] (map fst . usedpos) $ lookupCtxtExact n cg
236 args' = [if i `elem` used then a else Erased | (i,a) <- zip [0..] args]
237 irCon env t a n args'
239 | (P (TCon t a) n _, args) <- unApply tm
242 | (P _ n _, args) <- unApply tm = do
244 case lookup n (idris_scprims ist) of
245 -- if it's a primitive which is already saturated,
246 -- compile to the corresponding op here already to save work
247 Just (arity, op) | length args == arity
248 -> LOp op <$> mapM (ir' env) args
250 _ -> applyName n ist args
252{- collapsibility is turned off for now
254 -- if it's collapsible, compile to LNothing
255 | (collapsible <$> lookupCtxtExact n (idris_optimisation ist)) == Just True
258 -- otherwise, compile normally
259 | otherwise -> applyName n ist args
263 | (f, args) <- unApply tm
265 args' <- mapM (ir' env) args
266 return (LApp False f' args')
269 applyName :: Name -> IState -> [Term] -> Idris LExp
270 applyName n ist args =
271 LApp False (LV $ Glob n) <$> mapM (ir' env . erase) (zip [0..] args)
274 | i >= arity || i `elem` used = x
277 arity = case fst4 <$> lookupCtxt n (definitions . tt_ctxt $ ist) of
278 [CaseOp ci ty tys def tot cdefs] -> length tys
279 [TyDecl (DCon tag ar) _] -> ar
280 [Operator ty ar op] -> ar
283 used = maybe [] (map fst . usedpos) $ lookupCtxtExact n (idris_callgraph ist)
286-- ir' env (P _ (NS (UN "Z") ["Nat", "Prelude"]) _)
287-- = return $ LConst (BI 0)
288 ir' env (P _ n _) = return $ LV (Glob n)
289 ir' env (V i) | i >= 0 && i < length env = return $ LV (Glob (env!!i))
290 | otherwise = ifail $ "IR fail " ++ show i ++ " " ++ show tm
291 ir' env (Bind n (Lam _) sc)
292 = do let n' = uniqueName n env
293 sc' <- ir' (n' : env) sc
294 return $ LLam [n'] sc'
295 ir' env (Bind n (Let _ v) sc)
296 = do sc' <- ir' (n : env) sc
298 return $ LLet n v' sc'
299 ir' env (Bind _ _ _) = return $ LNothing
300 ir' env (Proj t i) | i == -1
302 return $ LOp (LMinus (ATInt ITBig))
304 ir' env (Proj t i) = do t' <- ir' env t
306 ir' env (Constant c) = return $ LConst c
307 ir' env (TType _) = return $ LNothing
308 ir' env Erased = return $ LNothing
309 ir' env Impossible = return $ LNothing
310-- ir' env _ = return $ LError "Impossible"
312 irCon env t arity n args
313 | length args == arity = buildApp env (LV (Glob n)) args
314 | otherwise = let extra = satArgs (arity - length args) in
315 do sc' <- irCon env t arity n
316 (args ++ map (\n -> P Bound n undefined) extra)
317 return $ LLam extra sc'
319 satArgs n = map (\i -> sMN i "sat") [1..n]
321 buildApp env e [] = return e
322 buildApp env e xs = do xs' <- mapM (ir' env) xs
323 return $ LApp False e xs'
325 doForeign :: [Name] -> [TT Name] -> Idris LExp
326 doForeign env (_ : fgn : args)
327 | (_, (Constant (Str fgnName) : fgnArgTys : ret : [])) <- unApply fgn
328 = let maybeTys = getFTypes fgnArgTys
331 Nothing -> ifail $ "Foreign type specification is not a constant list: " ++ show (fgn:args)
333 args' <- mapM (ir' env) (init args)
334 -- wrap it in a prim__IO
335 -- return $ con_ 0 @@ impossible @@
336 return $ -- LLazyExp $
337 LForeign LANG_C rty fgnName (zip tys args')
338 | otherwise = ifail "Badly formed foreign function call"
340getFTypes :: TT Name -> Maybe [FType]
341getFTypes tm = case unApply tm of
342 -- nil : {a : Type} -> List a
343 (nil, [_]) -> Just []
344 -- cons : {a : Type} -> a -> List a -> List a
345 (cons, [_, ty, xs]) -> (mkIty' ty :) <$> getFTypes xs
348mkIty' (P _ (UN ty) _) = mkIty (str ty)
349mkIty' (App (P _ (UN fi) _) (P _ (UN intTy) _))
350 | fi == txt "FIntT" = mkIntIty (str intTy)
351mkIty' (App (App (P _ (UN ff) _) _) (App (P _ (UN fa) _) (App (P _ (UN io) _) _)))
352 | ff == txt "FFunction" && fa == txt "FAny" &&
355mkIty' (App (App (P _ (UN ff) _) _) _)
356 | ff == txt "FFunction" = FFunction
359-- would be better if these FInt types were evaluated at compile time
360-- TODO: add %eval directive for such things
362mkIty "FFloat" = FArith ATFloat
363mkIty "FInt" = mkIntIty "ITNative"
364mkIty "FChar" = mkIntIty "ITChar"
365mkIty "FByte" = mkIntIty "IT8"
366mkIty "FShort" = mkIntIty "IT16"
367mkIty "FLong" = mkIntIty "IT64"
368mkIty "FBits8" = mkIntIty "IT8"
369mkIty "FBits16" = mkIntIty "IT16"
370mkIty "FBits32" = mkIntIty "IT32"
371mkIty "FBits64" = mkIntIty "IT64"
372mkIty "FString" = FString
374mkIty "FManagedPtr" = FManagedPtr
376mkIty "FFunction" = FFunction
377mkIty "FFunctionIO" = FFunctionIO
378mkIty "FBits8x16" = FArith (ATInt (ITVec IT8 16))
379mkIty "FBits16x8" = FArith (ATInt (ITVec IT16 8))
380mkIty "FBits32x4" = FArith (ATInt (ITVec IT32 4))
381mkIty "FBits64x2" = FArith (ATInt (ITVec IT64 2))
382mkIty x = error $ "Unknown type " ++ x
384mkIntIty "ITNative" = FArith (ATInt ITNative)
385mkIntIty "ITChar" = FArith (ATInt ITChar)
386mkIntIty "IT8" = FArith (ATInt (ITFixed IT8))
387mkIntIty "IT16" = FArith (ATInt (ITFixed IT16))
388mkIntIty "IT32" = FArith (ATInt (ITFixed IT32))
389mkIntIty "IT64" = FArith (ATInt (ITFixed IT64))
391zname = sNS (sUN "Z") ["Nat","Prelude"]
392sname = sNS (sUN "S") ["Nat","Prelude"]
394instance ToIR ([Name], SC) where
395 ir (args, tree) = do logLvl 3 $ "Compiling " ++ show args ++ "\n" ++ show tree
397 return $ LLam args tree'
399instance ToIR SC where
403 ir' (UnmatchedCase str) = return $ LError str
404 ir' (ProjCase tm alt) = do tm' <- ir tm
405 alt' <- mkIRAlt tm' alt
406 return $ LCase tm' [alt']
408 -- This is important for runtime because sometimes we case on irrelevant data:
411 -- SingleConstructor x y => ... x, y never used ...
413 -- In the example above, {foo} will most probably have been erased
414 -- so this vain projection would make the resulting program segfault.
416 -- Hence, we check whether the variables are used at all
417 -- and erase the casesplit if they are not.
418 ir' (Case n [alt]) = do
419 alt' <- mkIRAlt (LV (Glob n)) alt
420 return $ case namesBoundIn alt' `usedIn` subexpr alt' of
421 [] -> subexpr alt' -- strip the unused top-most case
422 _ -> LCase (LV (Glob n)) [alt']
424 namesBoundIn :: LAlt -> [Name]
425 namesBoundIn (LConCase cn i ns sc) = ns
426 namesBoundIn (LConstCase c sc) = []
427 namesBoundIn (LDefaultCase sc) = []
429 subexpr :: LAlt -> LExp
430 subexpr (LConCase _ _ _ e) = e
431 subexpr (LConstCase _ e) = e
432 subexpr (LDefaultCase e) = e
434 ir' (Case n alts) = do alts' <- mapM (mkIRAlt (LV (Glob n))) alts
435 return $ LCase (LV (Glob n)) alts'
436 ir' ImpossibleCase = return LNothing
438 -- special cases for Z and S
439 -- Needs rethink: projections make this fail
440-- mkIRAlt n (ConCase z _ [] rhs) | z == zname
441-- = mkIRAlt n (ConstCase (BI 0) rhs)
442-- mkIRAlt n (ConCase s _ [arg] rhs) | s == sname
445-- return $ LDefaultCase
446-- (LLet arg (LOp LBMinus [n', LConst (BI 1)])
448 mkIRAlt _ (ConCase n t args rhs)
450 return $ LConCase (-1) n args rhs'
451 mkIRAlt _ (ConstCase x rhs)
454 return $ LConstCase x rhs'
457 return $ LDefaultCase rhs'
458 mkIRAlt tm (SucCase n rhs)
460 return $ LDefaultCase (LLet n (LOp (LMinus (ATInt ITBig))
462 LConst (BI 1)]) rhs')
463-- return $ LSucCase n rhs'
464 mkIRAlt _ (ConstCase c rhs)
465 = ifail $ "Can't match on (" ++ show c ++ ")"
466 mkIRAlt _ (DefaultCase rhs)
468 return $ LDefaultCase rhs'
470 matchable (I _) = True
471 matchable (BI _) = True
472 matchable (Ch _) = True
473 matchable (Str _) = True
476 matchableTy (AType (ATInt ITNative)) = True
477 matchableTy (AType (ATInt ITBig)) = True
478 matchableTy (AType (ATInt ITChar)) = True
479 matchableTy StrType = True
481 matchableTy (AType (ATInt (ITFixed IT8))) = True
482 matchableTy (AType (ATInt (ITFixed IT16))) = True
483 matchableTy (AType (ATInt (ITFixed IT32))) = True
484 matchableTy (AType (ATInt (ITFixed IT64))) = True
486 matchableTy _ = False
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list