3Description : Coordinates the compilation process.
6Maintainer : The Idris Community.
8{-# LANGUAGE CPP, PatternGuards, TypeSynonymInstances #-}
10module IRTS.Compiler(compile, generate) where
13import Idris.AbsSyntaxTree
15import Idris.Core.CaseTree
16import Idris.Core.Evaluate
22import IRTS.CodegenCommon
23import IRTS.CodegenJavaScript
24import IRTS.Defunctionalise
33import Prelude hiding (id, (.))
35import Control.Applicative
36import Control.Category
37import Control.Monad.State
38import Data.IntSet (IntSet)
39import qualified Data.IntSet as IS
41import qualified Data.Map as M
44import qualified Data.Set as S
46import System.Directory
47import System.Environment
49import System.FilePath (addTrailingPathSeparator, (</>))
53-- | Compile to simplified forms and return CodegenInfo
54compile :: Codegen -> FilePath -> Maybe Term -> Idris CodegenInfo
56 = do checkMVs -- check for undefined metavariables
57 checkTotality -- refuse to compile if there are totality problems
58 exports <- findExports
59 let rootNames = case mtm of
63 reachableNames <- performUsageAnalysis
64 (rootNames ++ getExpNames exports)
65 maindef <- case mtm of
67 Just tm -> do md <- irMain tm
68 logCodeGen 1 $ "MAIN: " ++ show md
69 return [(sMN 0 "runMain", md)]
70 objs <- getObjectFiles codegen
71 libs <- getLibs codegen
72 flags <- getFlags codegen
73 hdrs <- getHdrs codegen
74 impdirs <- allImportDirs
75 defsIn <- mkDecls reachableNames
76 -- if no 'main term' given, generate interface files
77 let iface = case mtm of
81 let defs = defsIn ++ maindef
83 -- Inlined top level LDecl made here
84 let defsInlined = inlineAll defs
85 let defsUniq = map (allocUnique (addAlist defsInlined emptyContext))
88 let (nexttag, tagged) = addTags 65536 (liftAll defsUniq)
89 let ctxtIn = addAlist tagged emptyContext
91 logCodeGen 1 "Defunctionalising"
92 let defuns_in = defunctionalise nexttag ctxtIn
93 logCodeGen 5 $ show defuns_in
94 logCodeGen 1 "Inlining"
95 let defuns = inline defuns_in
96 logCodeGen 5 $ show defuns
97 logCodeGen 1 "Resolving variables for CG"
99 let checked = simplifyDefs defuns (toAlist defuns)
101 dumpCases <- getDumpCases
102 dumpDefun <- getDumpDefun
105 Just f -> runIO $ writeFile f (showCaseTrees defs)
108 Just f -> runIO $ writeFile f (dumpDefuns defuns)
109 triple <- Idris.AbsSyntax.targetTriple
110 cpu <- Idris.AbsSyntax.targetCPU
111 logCodeGen 1 "Building output"
113 OK c -> do return $ CodegenInfo f outty triple cpu
114 hdrs impdirs objs libs flags
115 NONE c (toAlist defuns)
118 where checkMVs = do i <- getIState
119 case map fst (idris_metavars i) \\ primDefs of
121 ms -> do iputStrLn $ "WARNING: There are incomplete holes:\n " ++ show ms
122 iputStrLn "\nEvaluation of any of these will crash at run time."
124 checkTotality = do i <- getIState
125 case idris_totcheckfail i of
127 ((fc, msg):fs) -> ierror . At fc . Msg $ "Cannot compile:\n " ++ msg
129generate :: Codegen -> FilePath -> CodegenInfo -> IO ()
130generate codegen mainmod ir
132 -- Built-in code generators (FIXME: lift these out!)
133 Via _ "c" -> codegenC ir
134 -- Any external code generator
135 Via fm cg -> do input <- case fm of
136 IBCFormat -> return mainmod
138 tempdir <- getTemporaryDirectory
139 (fn, h) <- openTempFile tempdir "idris-cg.json"
143 let cmd = "idris-codegen-" ++ cg
144 args = [input, "-o", outputFile ir] ++ compilerFlags ir
145 exit <- rawSystem cmd args
146 when (exit /= ExitSuccess) $
147 putStrLn ("FAILURE: " ++ show cmd ++ " " ++ show args)
148 Bytecode -> dumpBC (simpleDecls ir) (outputFile ir)
150irMain :: TT Name -> Idris LDecl
152 i <- irTerm (sMN 0 "runMain") M.empty [] tm
153 return $ LFun [] (sMN 0 "runMain") [] (LForce i)
155mkDecls :: [Name] -> Idris [(Name, LDecl)]
158 let ds = filter (\(n, d) -> n `elem` used || isCon d) $ ctxtAlist (tt_ctxt i)
159 decls <- mapM build ds
162showCaseTrees :: [(Name, LDecl)] -> String
163showCaseTrees = showSep "\n\n" . map showCT . sortBy (comparing defnRank)
165 showCT (n, LFun _ f args lexp)
166 = show n ++ " " ++ showSep " " (map show args) ++ " =\n\t"
168 showCT (n, LConstructor c t a) = "data " ++ show n ++ " " ++ show a
170 defnRank :: (Name, LDecl) -> String
171 defnRank (n, LFun _ _ _ _) = "1" ++ nameRank n
172 defnRank (n, LConstructor _ _ _) = "2" ++ nameRank n
174 nameRank :: Name -> String
175 nameRank (UN s) = "1" ++ show s
176 nameRank (MN i s) = "2" ++ show s ++ show i
177 nameRank (NS n ns) = "3" ++ concatMap show (reverse ns) ++ nameRank n
178 nameRank (SN sn) = "4" ++ snRank sn
179 nameRank n = "5" ++ show n
181 snRank :: SpecialName -> String
182 snRank (WhereN i n n') = "1" ++ nameRank n' ++ nameRank n ++ show i
183 snRank (ImplementationN n args) = "2" ++ nameRank n ++ concatMap show args
184 snRank (ParentN n s) = "3" ++ nameRank n ++ show s
185 snRank (MethodN n) = "4" ++ nameRank n
186 snRank (CaseN _ n) = "5" ++ nameRank n
187 snRank (ElimN n) = "6" ++ nameRank n
188 snRank (ImplementationCtorN n) = "7" ++ nameRank n
189 snRank (WithN i n) = "8" ++ nameRank n ++ show i
191isCon (TyDecl _ _) = True
194build :: (Name, Def) -> Idris (Name, LDecl)
199 let args = map (\x -> sMN x "op") [0..] in
200 return (n, (LFun [] n (take ar args)
201 (LOp op (map (LV . Glob) (take ar args)))))
202 _ -> do def <- mkLDecl n d
203 logCodeGen 3 $ "Compiled " ++ show n ++ " =\n\t" ++ show def
206 | Just (ar, op) <- lookup n (idris_scprims i)
208 | Just ar <- lookup n (S.toList (idris_externs i))
209 = Just (ar, LExternal n)
210 getPrim n i = Nothing
212declArgs args inl n (LLam xs x) = declArgs (args ++ xs) inl n x
213declArgs args inl n x = LFun (if inl then [Inline] else []) n args x
215mkLDecl n (Function tm _)
216 = declArgs [] True n <$> irTerm n M.empty [] tm
218mkLDecl n (CaseOp ci _ _ _ pats cd)
219 = declArgs [] (case_inlinable ci || caseName n) n <$> irTree n args sc
221 (args, sc) = cases_runtime cd
223 -- Always attempt to inline functions arising from 'case' expressions
224 caseName (SN (CaseN _ _)) = True
225 caseName (SN (WithN _ _)) = True
226 caseName (NS n _) = caseName n
229mkLDecl n (TyDecl (DCon tag arity _) _) =
230 LConstructor n tag . length <$> fgetState (cg_usedpos . ist_callgraph n)
232mkLDecl n (TyDecl (TCon t a) _) = return $ LConstructor n (-1) a
233mkLDecl n _ = return $ (declArgs [] True n LNothing) -- postulate, never run
236 { viMethod :: Maybe Name
240type Vars = M.Map Name VarInfo
242irTerm :: Name -> Vars -> [Name] -> Term -> Idris LExp
243irTerm top vs env tm@(App _ f a) = do
247 | n `elem` map fst (idris_metavars ist) \\ primDefs
248 -> return $ LError $ "ABORT: Attempt to evaluate hole " ++ show n
250 | m == txt "mkForeignPrim"
251 -> doForeign vs env (reverse (drop 4 args)) -- drop implicits
253 (P _ (UN u) _, [_, arg])
254 | u == txt "unsafePerformPrimIO"
255 -> irTerm top vs env arg
258 | u == txt "assert_unreachable"
259 -> return $ LError $ "ABORT: Reached an unreachable case in " ++ show top
261 -- TMP HACK - until we get inlining.
262 (P _ (UN r) _, [_, _, _, _, _, arg])
264 -> irTerm top vs env arg
266 -- 'void' doesn't have any pattern clauses and only gets called on
267 -- erased things in higher order contexts (also a TMP HACK...)
272 -- Laziness, the old way
273 (P _ (UN l) _, [_, arg])
275 -> error "lazy has crept in somehow"
277 (P _ (UN l) _, [_, arg])
279 -> LForce <$> irTerm top vs env arg
281 -- Laziness, the new way
282 (P _ (UN l) _, [_, _, arg])
284 -> LLazyExp <$> irTerm top vs env arg
286 (P _ (UN l) _, [_, _, arg])
288 -> LForce <$> irTerm top vs env arg
290 (P _ (UN a) _, [_, _, _, arg])
291 | a == txt "assert_smaller"
292 -> irTerm top vs env arg
294 (P _ (UN a) _, [_, arg])
295 | a == txt "assert_total"
296 -> irTerm top vs env arg
298 (P _ (UN p) _, [_, arg])
300 -> do arg' <- irTerm top vs env arg
301 return $ LOp LPar [LLazyExp arg']
303 (P _ (UN pf) _, [arg])
304 | pf == txt "prim_fork"
305 -> do arg' <- irTerm top vs env arg
306 return $ LOp LFork [LLazyExp arg']
308 (P _ (UN m) _, [_,size,t])
310 -> irTerm top vs env t
312 (P _ (UN tm) _, [_,t])
313 | tm == txt "trace_malloc"
314 -> irTerm top vs env t -- TODO
316 -- This case is here until we get more general inlining. It's just
317 -- a really common case, and the laziness hurts...
318 (P _ (NS (UN be) [b,p]) _, [_,x,(App _ (App _ (App _ (P _ (UN d) _) _) _) t),
319 (App _ (App _ (App _ (P _ (UN d') _) _) _) e)])
320 | be == txt "ifThenElse"
326 x' <- irTerm top vs env x
327 t' <- irTerm top vs env t
328 e' <- irTerm top vs env e
329 return (LCase Shared x'
330 [LConCase 0 (sNS (sUN "False") ["Bool","Prelude"]) [] e'
331 ,LConCase 1 (sNS (sUN "True" ) ["Bool","Prelude"]) [] t'
335 (P (DCon t arity _) n _, args) -> do
336 detag <- fgetState (opt_detaggable . ist_optimisation n)
337 used <- map fst <$> fgetState (cg_usedpos . ist_callgraph n)
339 let isNewtype = length used == 1 && detag
340 let argsPruned = [a | (i,a) <- zip [0..] args, i `elem` used]
342 -- The following code removes fields from data constructors
343 -- and performs the newtype optimisation.
345 -- The general rule here is:
346 -- Everything we get as input is not touched by erasure,
347 -- so it conforms to the official arities and types
348 -- and we can reason about it like it's plain TT.
350 -- It's only the data that leaves this point that's erased
351 -- and possibly no longer typed as the original TT version.
353 -- Especially, underapplied constructors must yield functions
354 -- even if all the remaining arguments are erased
355 -- (the resulting function *will* be applied, to NULLs).
357 -- This will probably need rethinking when we get erasure from functions.
359 -- "padLams" will wrap our term in LLam-bdas and give us
360 -- the "list of future unerased args" coming from these lambdas.
362 -- We can do whatever we like with the list of unerased args,
363 -- hence it takes a lambda: \unerased_argname_list -> resulting_LExp.
364 let padLams = padLambdas used (length args) arity
366 case compare (length args) arity of
369 GT -> ifail ("overapplied data constructor: " ++ show tm ++
371 "Arity: " ++ show arity ++ "\n" ++
372 "Arguments: " ++ show args ++ "\n" ++
373 "Pruned arguments: " ++ show argsPruned)
377 -> irTerm top vs env (head argsPruned)
379 | otherwise -- not newtype, plain data ctor
380 -> buildApp (LV $ Glob n) argsPruned
382 -- not saturated, underapplied
383 LT | isNewtype -- newtype
384 , length argsPruned == 1 -- and we already have the value
385 -> padLams . (\tm [] -> tm) -- the [] asserts there are no unerased args
386 <$> irTerm top vs env (head argsPruned)
388 | isNewtype -- newtype but the value is not among args yet
389 -> return . padLams $ \[vn] -> LApp False (LV $ Glob n) [LV $ Glob vn]
391 -- not a newtype, just apply to a constructor
393 -> padLams . applyToNames <$> buildApp (LV $ Glob n) argsPruned
396 (P (TCon t a) n _, args) -> return LNothing
398 -- an external name applied to arguments
399 (P _ n _, args) | S.member (n, length args) (idris_externs ist) -> do
400 LOp (LExternal n) <$> mapM (irTerm top vs env) args
402 -- a name applied to arguments
403 (P _ n _, args) -> do
404 case lookup n (idris_scprims ist) of
405 -- if it's a primitive that is already saturated,
406 -- compile to the corresponding op here already to save work
407 Just (arity, op) | length args == arity
408 -> LOp op <$> mapM (irTerm top vs env) args
410 -- otherwise, just apply the name
411 _ -> applyName n ist args
413 -- turn de bruijn vars into regular named references and try again
414 (V i, args) -> irTerm top vs env $ mkApp (P Bound (env !! i) Erased) args
418 <$> irTerm top vs env f
419 <*> mapM (irTerm top vs env) args
422 buildApp :: LExp -> [Term] -> Idris LExp
423 buildApp e [] = return e
424 buildApp e xs = LApp False e <$> mapM (irTerm top vs env) xs
426 applyToNames :: LExp -> [Name] -> LExp
427 applyToNames tm [] = tm
428 applyToNames tm ns = LApp False tm $ map (LV . Glob) ns
430 padLambdas :: [Int] -> Int -> Int -> ([Name] -> LExp) -> LExp
431 padLambdas used startIdx endSIdx mkTerm
432 = LLam allNames $ mkTerm nonerasedNames
434 allNames = [sMN i "sat" | i <- [startIdx .. endSIdx-1]]
435 nonerasedNames = [sMN i "sat" | i <- [startIdx .. endSIdx-1], i `elem` used]
437 applyName :: Name -> IState -> [Term] -> Idris LExp
438 applyName n ist args =
439 LApp False (LV $ Glob n) <$> mapM (irTerm top vs env . erase) (zip [0..] args)
442 | i >= arity || i `elem` used = x
445 arity = case fst4 <$> lookupCtxtExact n (definitions . tt_ctxt $ ist) of
446 Just (CaseOp ci ty tys def tot cdefs) -> length tys
447 Just (TyDecl (DCon tag ar _) _) -> ar
448 Just (TyDecl Ref ty) -> length $ getArgTys ty
449 Just (Operator ty ar op) -> ar
450 Just def -> error $ "unknown arity: " ++ show (n, def)
451 Nothing -> 0 -- no definition, probably local name => can't erase anything
453 -- name for purposes of usage info lookup
455 | Just n' <- viMethod =<< M.lookup n vs = n'
458 used = maybe [] (map fst . usedpos) $ lookupCtxtExact uName (idris_callgraph ist)
461irTerm top vs env (P _ n _) = return $ LV (Glob n)
462irTerm top vs env (V i)
463 | i >= 0 && i < length env = return $ LV (Glob (env!!i))
464 | otherwise = ifail $ "bad de bruijn index: " ++ show i
466irTerm top vs env (Bind n (Lam _) sc) = LLam [n'] <$> irTerm top vs (n':env) sc
468 n' = uniqueName n env
470irTerm top vs env (Bind n (Let _ v) sc)
471 = LLet n <$> irTerm top vs env v <*> irTerm top vs (n : env) sc
473irTerm top vs env (Bind _ _ _) = return $ LNothing
475irTerm top vs env (Proj t (-1)) = do
476 t' <- irTerm top vs env t
477 return $ LOp (LMinus (ATInt ITBig))
480irTerm top vs env (Proj t i) = LProj <$> irTerm top vs env t <*> pure i
481irTerm top vs env (Constant TheWorld) = return LNothing
482irTerm top vs env (Constant c) = return (LConst c)
483irTerm top vs env (TType _) = return LNothing
484irTerm top vs env Erased = return LNothing
485irTerm top vs env Impossible = return LNothing
487doForeign :: Vars -> [Name] -> [Term] -> Idris LExp
488doForeign vs env (ret : fname : world : args)
489 = do args' <- mapM splitArg args
490 let fname' = toFDesc fname
491 let ret' = toFDesc ret
492 return $ LForeign ret' fname' args'
494 splitArg tm | (_, [_,_,l,r]) <- unApply tm -- pair, two implicits
495 = do let l' = toFDesc l
496 r' <- irTerm (sMN 0 "__foreignCall") vs env r
498 splitArg _ = ifail "Badly formed foreign function call"
500 toFDesc (Constant (Str str)) = FStr str
502 | (P _ n _, []) <- unApply tm = FCon (deNS n)
503 | (P _ n _, as) <- unApply tm = FApp (deNS n) (map toFDesc as)
508doForeign vs env xs = ifail "Badly formed foreign function call"
510irTree :: Name -> [Name] -> SC -> Idris LExp
511irTree top args tree = do
512 logCodeGen 3 $ "Compiling " ++ show args ++ "\n" ++ show tree
513 LLam args <$> irSC top M.empty tree
515irSC :: Name -> Vars -> SC -> Idris LExp
516irSC top vs (STerm t) = irTerm top vs [] t
517irSC top vs (UnmatchedCase str) = return $ LError str
519irSC top vs (ProjCase tm alts) = do
520 tm' <- irTerm top vs [] tm
521 alts' <- mapM (irAlt top vs tm') alts
522 return $ LCase Shared tm' alts'
524-- Transform matching on Delay to applications of Force.
525irSC top vs (Case up n [ConCase (UN delay) i [_, _, n'] sc])
526 | delay == txt "Delay"
527 = do sc' <- irSC top vs sc -- mkForce n' n sc
528 return $ lsubst n' (LForce (LV (Glob n))) sc'
530-- There are two transformations in this case:
532-- 1. Newtype-case elimination:
534-- wrap({e1}) -> P({e1}) ==> P({e0})
536-- This is important because newtyped constructors are compiled away entirely
537-- and we need to do that everywhere.
539-- 2. Unused-case elimination (only valid for singleton branches):
541-- C(x,y) -> P[... x,y not used ...]
543-- This is important for runtime because sometimes we case on irrelevant data:
545-- In the example above, {e0} will most probably have been erased
546-- so this vain projection would make the resulting program segfault
547-- because the code generator still emits a PROJECT(...) G-machine instruction.
549-- Hence, we check whether the variables are used at all
550-- and erase the casesplit if they are not.
552irSC top vs (Case up n [alt]) = do
553 replacement <- case alt of
554 ConCase cn a ns sc -> do
555 detag <- fgetState (opt_detaggable . ist_optimisation cn)
556 used <- map fst <$> fgetState (cg_usedpos . ist_callgraph cn)
557 if detag && length used == 1
558 then return . Just $ substSC (ns !! head used) n sc
563 Just sc -> irSC top vs sc
565 alt' <- irAlt top vs (LV (Glob n)) alt
566 return $ case namesBoundIn alt' `usedIn` subexpr alt' of
567 [] -> subexpr alt' -- strip the unused top-most case
568 _ -> LCase up (LV (Glob n)) [alt']
570 namesBoundIn :: LAlt -> [Name]
571 namesBoundIn (LConCase cn i ns sc) = ns
572 namesBoundIn (LConstCase c sc) = []
573 namesBoundIn (LDefaultCase sc) = []
575 subexpr :: LAlt -> LExp
576 subexpr (LConCase _ _ _ e) = e
577 subexpr (LConstCase _ e) = e
578 subexpr (LDefaultCase e) = e
580-- FIXME: When we have a non-singleton case-tree of the form
586-- and C is detaggable (the only constructor of the family), we can be sure
587-- that the first branch will be always taken -- so we add special handling
588-- to remove the dead default branch.
590-- If we don't do so and C is newtype-optimisable, we will miss this newtype
591-- transformation and the resulting code will probably segfault.
593-- This work-around is not entirely optimal; the best approach would be
594-- to ensure that such case trees don't arise in the first place.
596irSC top vs (Case up n alts@[ConCase cn a ns sc, DefaultCase sc']) = do
597 detag <- fgetState (opt_detaggable . ist_optimisation cn)
599 then irSC top vs (Case up n [ConCase cn a ns sc])
600 else LCase up (LV (Glob n)) <$> mapM (irAlt top vs (LV (Glob n))) alts
602irSC top vs sc@(Case up n alts) = do
603 -- check that neither alternative needs the newtype optimisation,
605 goneWrong <- or <$> mapM isDetaggable alts
607 $ ifail ("irSC: non-trivial case-match on detaggable data: " ++ show sc)
610 LCase up (LV (Glob n)) <$> mapM (irAlt top vs (LV (Glob n))) alts
612 isDetaggable (ConCase cn _ _ _) = fgetState $ opt_detaggable . ist_optimisation cn
613 isDetaggable _ = return False
615irSC top vs ImpossibleCase = return LNothing
617irAlt :: Name -> Vars -> LExp -> CaseAlt -> Idris LAlt
619-- this leaves out all unused arguments of the constructor
620irAlt top vs _ (ConCase n t args sc) = do
621 used <- map fst <$> fgetState (cg_usedpos . ist_callgraph n)
622 let usedArgs = [a | (i,a) <- zip [0..] args, i `elem` used]
623 LConCase (-1) n usedArgs <$> irSC top (methodVars `M.union` vs) sc
625 methodVars = case n of
626 SN (ImplementationCtorN interfaceName)
627 -> M.fromList [(v, VI
628 { viMethod = Just $ mkFieldName n i
629 }) | (v,i) <- zip args [0..]]
631 -> M.empty -- not an implementation constructor
633irAlt top vs _ (ConstCase x rhs)
634 | matchable x = LConstCase x <$> irSC top vs rhs
635 | matchableTy x = LDefaultCase <$> irSC top vs rhs
637 matchable (I _) = True
638 matchable (BI _) = True
639 matchable (Ch _) = True
640 matchable (Str _) = True
641 matchable (B8 _) = True
642 matchable (B16 _) = True
643 matchable (B32 _) = True
644 matchable (B64 _) = True
647 matchableTy (AType (ATInt ITNative)) = True
648 matchableTy (AType (ATInt ITBig)) = True
649 matchableTy (AType (ATInt ITChar)) = True
650 matchableTy StrType = True
652 matchableTy (AType (ATInt (ITFixed IT8))) = True
653 matchableTy (AType (ATInt (ITFixed IT16))) = True
654 matchableTy (AType (ATInt (ITFixed IT32))) = True
655 matchableTy (AType (ATInt (ITFixed IT64))) = True
657 matchableTy _ = False
659irAlt top vs tm (SucCase n rhs) = do
660 rhs' <- irSC top vs rhs
661 return $ LDefaultCase (LLet n (LOp (LMinus (ATInt ITBig))
663 LConst (BI 1)]) rhs')
665irAlt top vs _ (ConstCase c rhs)
666 = ifail $ "Can't match on (" ++ show c ++ ")"
668irAlt top vs _ (DefaultCase rhs)
669 = LDefaultCase <$> irSC top vs rhs
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