3Description : Coordinates the compilation process.
6Maintainer : The Idris Community.
8{-# LANGUAGE PatternGuards, TypeSynonymInstances, CPP #-}
10module IRTS.Compiler(compile, generate) where
14import IRTS.Defunctionalise
16import IRTS.CodegenCommon
19import IRTS.CodegenJavaScript
25import Idris.AbsSyntaxTree
34import Idris.Core.Evaluate
35import Idris.Core.CaseTree
37import Control.Category
38import Prelude hiding (id, (.))
40import Control.Applicative
41import Control.Monad.State
46import Data.IntSet (IntSet)
47import qualified Data.IntSet as IS
48import qualified Data.Map as M
49import qualified Data.Set as S
54import System.Directory
55import System.Environment
56import System.FilePath ((</>), addTrailingPathSeparator)
58-- | Compile to simplified forms and return CodegenInfo
59compile :: Codegen -> FilePath -> Maybe Term -> Idris CodegenInfo
61 = do checkMVs -- check for undefined metavariables
62 checkTotality -- refuse to compile if there are totality problems
63 exports <- findExports
64 let rootNames = case mtm of
68 reachableNames <- performUsageAnalysis
69 (rootNames ++ getExpNames exports)
70 maindef <- case mtm of
72 Just tm -> do md <- irMain tm
73 logCodeGen 1 $ "MAIN: " ++ show md
74 return [(sMN 0 "runMain", md)]
75 objs <- getObjectFiles codegen
76 libs <- getLibs codegen
77 flags <- getFlags codegen
78 hdrs <- getHdrs codegen
79 impdirs <- allImportDirs
80 defsIn <- mkDecls reachableNames
81 -- if no 'main term' given, generate interface files
82 let iface = case mtm of
86 let defs = defsIn ++ maindef
88 -- Inlined top level LDecl made here
89 let defsInlined = inlineAll defs
90 let defsUniq = map (allocUnique (addAlist defsInlined emptyContext))
93 let (nexttag, tagged) = addTags 65536 (liftAll defsUniq)
94 let ctxtIn = addAlist tagged emptyContext
96 logCodeGen 1 "Defunctionalising"
97 let defuns_in = defunctionalise nexttag ctxtIn
98 logCodeGen 5 $ show defuns_in
99 logCodeGen 1 "Inlining"
100 let defuns = inline defuns_in
101 logCodeGen 5 $ show defuns
102 logCodeGen 1 "Resolving variables for CG"
104 let checked = simplifyDefs defuns (toAlist defuns)
106 dumpCases <- getDumpCases
107 dumpDefun <- getDumpDefun
110 Just f -> runIO $ writeFile f (showCaseTrees defs)
113 Just f -> runIO $ writeFile f (dumpDefuns defuns)
114 triple <- Idris.AbsSyntax.targetTriple
115 cpu <- Idris.AbsSyntax.targetCPU
116 logCodeGen 1 "Building output"
118 OK c -> do return $ CodegenInfo f outty triple cpu
119 hdrs impdirs objs libs flags
120 NONE c (toAlist defuns)
123 where checkMVs = do i <- getIState
124 case map fst (idris_metavars i) \\ primDefs of
126 ms -> do iputStrLn $ "WARNING: There are incomplete holes:\n " ++ show ms
127 iputStrLn "\nEvaluation of any of these will crash at run time."
129 checkTotality = do i <- getIState
130 case idris_totcheckfail i of
132 ((fc, msg):fs) -> ierror . At fc . Msg $ "Cannot compile:\n " ++ msg
134generate :: Codegen -> FilePath -> CodegenInfo -> IO ()
135generate codegen mainmod ir
137 -- Built-in code generators (FIXME: lift these out!)
138 Via _ "c" -> codegenC ir
139 -- Any external code generator
140 Via fm cg -> do input <- case fm of
141 IBCFormat -> return mainmod
143 tempdir <- getTemporaryDirectory
144 (fn, h) <- openTempFile tempdir "idris-cg.json"
148 let cmd = "idris-codegen-" ++ cg
149 args = [input, "-o", outputFile ir] ++ compilerFlags ir
150 exit <- rawSystem cmd args
151 when (exit /= ExitSuccess) $
152 putStrLn ("FAILURE: " ++ show cmd ++ " " ++ show args)
153 Bytecode -> dumpBC (simpleDecls ir) (outputFile ir)
155irMain :: TT Name -> Idris LDecl
157 i <- irTerm M.empty [] tm
158 return $ LFun [] (sMN 0 "runMain") [] (LForce i)
160mkDecls :: [Name] -> Idris [(Name, LDecl)]
163 let ds = filter (\(n, d) -> n `elem` used || isCon d) $ ctxtAlist (tt_ctxt i)
164 decls <- mapM build ds
167showCaseTrees :: [(Name, LDecl)] -> String
168showCaseTrees = showSep "\n\n" . map showCT . sortBy (comparing defnRank)
170 showCT (n, LFun _ f args lexp)
171 = show n ++ " " ++ showSep " " (map show args) ++ " =\n\t"
173 showCT (n, LConstructor c t a) = "data " ++ show n ++ " " ++ show a
175 defnRank :: (Name, LDecl) -> String
176 defnRank (n, LFun _ _ _ _) = "1" ++ nameRank n
177 defnRank (n, LConstructor _ _ _) = "2" ++ nameRank n
179 nameRank :: Name -> String
180 nameRank (UN s) = "1" ++ show s
181 nameRank (MN i s) = "2" ++ show s ++ show i
182 nameRank (NS n ns) = "3" ++ concatMap show (reverse ns) ++ nameRank n
183 nameRank (SN sn) = "4" ++ snRank sn
184 nameRank n = "5" ++ show n
186 snRank :: SpecialName -> String
187 snRank (WhereN i n n') = "1" ++ nameRank n' ++ nameRank n ++ show i
188 snRank (InstanceN n args) = "2" ++ nameRank n ++ concatMap show args
189 snRank (ParentN n s) = "3" ++ nameRank n ++ show s
190 snRank (MethodN n) = "4" ++ nameRank n
191 snRank (CaseN _ n) = "5" ++ nameRank n
192 snRank (ElimN n) = "6" ++ nameRank n
193 snRank (InstanceCtorN n) = "7" ++ nameRank n
194 snRank (WithN i n) = "8" ++ nameRank n ++ show i
196isCon (TyDecl _ _) = True
199build :: (Name, Def) -> Idris (Name, LDecl)
204 let args = map (\x -> sMN x "op") [0..] in
205 return (n, (LFun [] n (take ar args)
206 (LOp op (map (LV . Glob) (take ar args)))))
207 _ -> do def <- mkLDecl n d
208 logCodeGen 3 $ "Compiled " ++ show n ++ " =\n\t" ++ show def
211 | Just (ar, op) <- lookup n (idris_scprims i)
213 | Just ar <- lookup n (S.toList (idris_externs i))
214 = Just (ar, LExternal n)
215 getPrim n i = Nothing
217declArgs args inl n (LLam xs x) = declArgs (args ++ xs) inl n x
218declArgs args inl n x = LFun (if inl then [Inline] else []) n args x
220mkLDecl n (Function tm _)
221 = declArgs [] True n <$> irTerm M.empty [] tm
223mkLDecl n (CaseOp ci _ _ _ pats cd)
224 = declArgs [] (case_inlinable ci || caseName n) n <$> irTree args sc
226 (args, sc) = cases_runtime cd
228 -- Always attempt to inline functions arising from 'case' expressions
229 caseName (SN (CaseN _ _)) = True
230 caseName (SN (WithN _ _)) = True
231 caseName (NS n _) = caseName n
234mkLDecl n (TyDecl (DCon tag arity _) _) =
235 LConstructor n tag . length <$> fgetState (cg_usedpos . ist_callgraph n)
237mkLDecl n (TyDecl (TCon t a) _) = return $ LConstructor n (-1) a
238mkLDecl n _ = return $ (declArgs [] True n LNothing) -- postulate, never run
241 { viMethod :: Maybe Name
245type Vars = M.Map Name VarInfo
247irTerm :: Vars -> [Name] -> Term -> Idris LExp
248irTerm vs env tm@(App _ f a) = do
252 | n `elem` map fst (idris_metavars ist) \\ primDefs
253 -> return $ LError $ "ABORT: Attempt to evaluate hole " ++ show n
255 | m == txt "mkForeignPrim"
256 -> doForeign vs env (reverse (drop 4 args)) -- drop implicits
258 (P _ (UN u) _, [_, arg])
259 | u == txt "unsafePerformPrimIO"
262 -- TMP HACK - until we get inlining.
263 (P _ (UN r) _, [_, _, _, _, _, arg])
267 -- 'void' doesn't have any pattern clauses and only gets called on
268 -- erased things in higher order contexts (also a TMP HACK...)
273 -- Laziness, the old way
274 (P _ (UN l) _, [_, arg])
276 -> error "lazy has crept in somehow"
278 (P _ (UN l) _, [_, arg])
280 -> LForce <$> irTerm vs env arg
282 -- Laziness, the new way
283 (P _ (UN l) _, [_, _, arg])
285 -> LLazyExp <$> irTerm vs env arg
287 (P _ (UN l) _, [_, _, arg])
289 -> LForce <$> irTerm vs env arg
291 (P _ (UN a) _, [_, _, _, arg])
292 | a == txt "assert_smaller"
295 (P _ (UN a) _, [_, arg])
296 | a == txt "assert_total"
299 (P _ (UN p) _, [_, arg])
301 -> do arg' <- irTerm vs env arg
302 return $ LOp LPar [LLazyExp arg']
304 (P _ (UN pf) _, [arg])
305 | pf == txt "prim_fork"
306 -> do arg' <- irTerm vs env arg
307 return $ LOp LFork [LLazyExp arg']
309 (P _ (UN m) _, [_,size,t])
313 (P _ (UN tm) _, [_,t])
314 | tm == txt "trace_malloc"
315 -> irTerm vs env t -- TODO
317 -- This case is here until we get more general inlining. It's just
318 -- a really common case, and the laziness hurts...
319 (P _ (NS (UN be) [b,p]) _, [_,x,(App _ (App _ (App _ (P _ (UN d) _) _) _) t),
320 (App _ (App _ (App _ (P _ (UN d') _) _) _) e)])
321 | be == txt "ifThenElse"
327 x' <- irTerm vs env x
328 t' <- irTerm vs env t
329 e' <- irTerm vs env e
330 return (LCase Shared x'
331 [LConCase 0 (sNS (sUN "False") ["Bool","Prelude"]) [] e'
332 ,LConCase 1 (sNS (sUN "True" ) ["Bool","Prelude"]) [] t'
336 (P (DCon t arity _) n _, args) -> do
337 detag <- fgetState (opt_detaggable . ist_optimisation n)
338 used <- map fst <$> fgetState (cg_usedpos . ist_callgraph n)
340 let isNewtype = length used == 1 && detag
341 let argsPruned = [a | (i,a) <- zip [0..] args, i `elem` used]
343 -- The following code removes fields from data constructors
344 -- and performs the newtype optimisation.
346 -- The general rule here is:
347 -- Everything we get as input is not touched by erasure,
348 -- so it conforms to the official arities and types
349 -- and we can reason about it like it's plain TT.
351 -- It's only the data that leaves this point that's erased
352 -- and possibly no longer typed as the original TT version.
354 -- Especially, underapplied constructors must yield functions
355 -- even if all the remaining arguments are erased
356 -- (the resulting function *will* be applied, to NULLs).
358 -- This will probably need rethinking when we get erasure from functions.
360 -- "padLams" will wrap our term in LLam-bdas and give us
361 -- the "list of future unerased args" coming from these lambdas.
363 -- We can do whatever we like with the list of unerased args,
364 -- hence it takes a lambda: \unerased_argname_list -> resulting_LExp.
365 let padLams = padLambdas used (length args) arity
367 case compare (length args) arity of
370 GT -> ifail ("overapplied data constructor: " ++ show tm ++
372 "Arity: " ++ show arity ++ "\n" ++
373 "Arguments: " ++ show args ++ "\n" ++
374 "Pruned arguments: " ++ show argsPruned)
378 -> irTerm vs env (head argsPruned)
380 | otherwise -- not newtype, plain data ctor
381 -> buildApp (LV $ Glob n) argsPruned
383 -- not saturated, underapplied
384 LT | isNewtype -- newtype
385 , length argsPruned == 1 -- and we already have the value
386 -> padLams . (\tm [] -> tm) -- the [] asserts there are no unerased args
387 <$> irTerm vs env (head argsPruned)
389 | isNewtype -- newtype but the value is not among args yet
390 -> return . padLams $ \[vn] -> LApp False (LV $ Glob n) [LV $ Glob vn]
392 -- not a newtype, just apply to a constructor
394 -> padLams . applyToNames <$> buildApp (LV $ Glob n) argsPruned
397 (P (TCon t a) n _, args) -> return LNothing
399 -- an external name applied to arguments
400 (P _ n _, args) | S.member (n, length args) (idris_externs ist) -> do
401 LOp (LExternal n) <$> mapM (irTerm vs env) args
403 -- a name applied to arguments
404 (P _ n _, args) -> do
405 case lookup n (idris_scprims ist) of
406 -- if it's a primitive that is already saturated,
407 -- compile to the corresponding op here already to save work
408 Just (arity, op) | length args == arity
409 -> LOp op <$> mapM (irTerm vs env) args
411 -- otherwise, just apply the name
412 _ -> applyName n ist args
414 -- turn de bruijn vars into regular named references and try again
415 (V i, args) -> irTerm vs env $ mkApp (P Bound (env !! i) Erased) args
420 <*> mapM (irTerm vs env) args
423 buildApp :: LExp -> [Term] -> Idris LExp
424 buildApp e [] = return e
425 buildApp e xs = LApp False e <$> mapM (irTerm vs env) xs
427 applyToNames :: LExp -> [Name] -> LExp
428 applyToNames tm [] = tm
429 applyToNames tm ns = LApp False tm $ map (LV . Glob) ns
431 padLambdas :: [Int] -> Int -> Int -> ([Name] -> LExp) -> LExp
432 padLambdas used startIdx endSIdx mkTerm
433 = LLam allNames $ mkTerm nonerasedNames
435 allNames = [sMN i "sat" | i <- [startIdx .. endSIdx-1]]
436 nonerasedNames = [sMN i "sat" | i <- [startIdx .. endSIdx-1], i `elem` used]
438 applyName :: Name -> IState -> [Term] -> Idris LExp
439 applyName n ist args =
440 LApp False (LV $ Glob n) <$> mapM (irTerm vs env . erase) (zip [0..] args)
443 | i >= arity || i `elem` used = x
446 arity = case fst4 <$> lookupCtxtExact n (definitions . tt_ctxt $ ist) of
447 Just (CaseOp ci ty tys def tot cdefs) -> length tys
448 Just (TyDecl (DCon tag ar _) _) -> ar
449 Just (TyDecl Ref ty) -> length $ getArgTys ty
450 Just (Operator ty ar op) -> ar
451 Just def -> error $ "unknown arity: " ++ show (n, def)
452 Nothing -> 0 -- no definition, probably local name => can't erase anything
454 -- name for purposes of usage info lookup
456 | Just n' <- viMethod =<< M.lookup n vs = n'
459 used = maybe [] (map fst . usedpos) $ lookupCtxtExact uName (idris_callgraph ist)
462irTerm vs env (P _ n _) = return $ LV (Glob n)
464 | i >= 0 && i < length env = return $ LV (Glob (env!!i))
465 | otherwise = ifail $ "bad de bruijn index: " ++ show i
467irTerm vs env (Bind n (Lam _) sc) = LLam [n'] <$> irTerm vs (n':env) sc
469 n' = uniqueName n env
471irTerm vs env (Bind n (Let _ v) sc)
472 = LLet n <$> irTerm vs env v <*> irTerm vs (n : env) sc
474irTerm vs env (Bind _ _ _) = return $ LNothing
476irTerm vs env (Proj t (-1)) = do
477 t' <- irTerm vs env t
478 return $ LOp (LMinus (ATInt ITBig))
481irTerm vs env (Proj t i) = LProj <$> irTerm vs env t <*> pure i
482irTerm vs env (Constant TheWorld) = return $ LNothing
483irTerm vs env (Constant c) = return $ LConst c
484irTerm vs env (TType _) = return $ LNothing
485irTerm vs env Erased = return $ LNothing
486irTerm vs env Impossible = return $ LNothing
488doForeign :: Vars -> [Name] -> [Term] -> Idris LExp
489doForeign vs env (ret : fname : world : args)
490 = do args' <- mapM splitArg args
491 let fname' = toFDesc fname
492 let ret' = toFDesc ret
493 return $ LForeign ret' fname' args'
495 splitArg tm | (_, [_,_,l,r]) <- unApply tm -- pair, two implicits
496 = do let l' = toFDesc l
497 r' <- irTerm vs env r
499 splitArg _ = ifail $ "Badly formed foreign function call"
501 toFDesc (Constant (Str str)) = FStr str
503 | (P _ n _, []) <- unApply tm = FCon (deNS n)
504 | (P _ n _, as) <- unApply tm = FApp (deNS n) (map toFDesc as)
509doForeign vs env xs = ifail "Badly formed foreign function call"
511irTree :: [Name] -> SC -> Idris LExp
513 logCodeGen 3 $ "Compiling " ++ show args ++ "\n" ++ show tree
514 LLam args <$> irSC M.empty tree
516irSC :: Vars -> SC -> Idris LExp
517irSC vs (STerm t) = irTerm vs [] t
518irSC vs (UnmatchedCase str) = return $ LError str
520irSC vs (ProjCase tm alts) = do
521 tm' <- irTerm vs [] tm
522 alts' <- mapM (irAlt vs tm') alts
523 return $ LCase Shared tm' alts'
525-- Transform matching on Delay to applications of Force.
526irSC vs (Case up n [ConCase (UN delay) i [_, _, n'] sc])
527 | delay == txt "Delay"
528 = do sc' <- irSC vs $ mkForce n' n sc
529 return $ LLet n' (LForce (LV (Glob n))) sc'
531-- There are two transformations in this case:
533-- 1. Newtype-case elimination:
535-- wrap({e1}) -> P({e1}) ==> P({e0})
537-- This is important because newtyped constructors are compiled away entirely
538-- and we need to do that everywhere.
540-- 2. Unused-case elimination (only valid for singleton branches):
542-- C(x,y) -> P[... x,y not used ...]
544-- This is important for runtime because sometimes we case on irrelevant data:
546-- In the example above, {e0} will most probably have been erased
547-- so this vain projection would make the resulting program segfault
548-- because the code generator still emits a PROJECT(...) G-machine instruction.
550-- Hence, we check whether the variables are used at all
551-- and erase the casesplit if they are not.
553irSC vs (Case up n [alt]) = do
554 replacement <- case alt of
555 ConCase cn a ns sc -> do
556 detag <- fgetState (opt_detaggable . ist_optimisation cn)
557 used <- map fst <$> fgetState (cg_usedpos . ist_callgraph cn)
558 if detag && length used == 1
559 then return . Just $ substSC (ns !! head used) n sc
564 Just sc -> irSC vs sc
566 alt' <- irAlt vs (LV (Glob n)) alt
567 return $ case namesBoundIn alt' `usedIn` subexpr alt' of
568 [] -> subexpr alt' -- strip the unused top-most case
569 _ -> LCase up (LV (Glob n)) [alt']
571 namesBoundIn :: LAlt -> [Name]
572 namesBoundIn (LConCase cn i ns sc) = ns
573 namesBoundIn (LConstCase c sc) = []
574 namesBoundIn (LDefaultCase sc) = []
576 subexpr :: LAlt -> LExp
577 subexpr (LConCase _ _ _ e) = e
578 subexpr (LConstCase _ e) = e
579 subexpr (LDefaultCase e) = e
581-- FIXME: When we have a non-singleton case-tree of the form
587-- and C is detaggable (the only constructor of the family), we can be sure
588-- that the first branch will be always taken -- so we add special handling
589-- to remove the dead default branch.
591-- If we don't do so and C is newtype-optimisable, we will miss this newtype
592-- transformation and the resulting code will probably segfault.
594-- This work-around is not entirely optimal; the best approach would be
595-- to ensure that such case trees don't arise in the first place.
597irSC vs (Case up n alts@[ConCase cn a ns sc, DefaultCase sc']) = do
598 detag <- fgetState (opt_detaggable . ist_optimisation cn)
600 then irSC vs (Case up n [ConCase cn a ns sc])
601 else LCase up (LV (Glob n)) <$> mapM (irAlt vs (LV (Glob n))) alts
603irSC vs sc@(Case up n alts) = do
604 -- check that neither alternative needs the newtype optimisation,
606 goneWrong <- or <$> mapM isDetaggable alts
608 $ ifail ("irSC: non-trivial case-match on detaggable data: " ++ show sc)
611 LCase up (LV (Glob n)) <$> mapM (irAlt vs (LV (Glob n))) alts
613 isDetaggable (ConCase cn _ _ _) = fgetState $ opt_detaggable . ist_optimisation cn
614 isDetaggable _ = return False
616irSC vs ImpossibleCase = return LNothing
618irAlt :: Vars -> LExp -> CaseAlt -> Idris LAlt
620-- this leaves out all unused arguments of the constructor
621irAlt vs _ (ConCase n t args sc) = do
622 used <- map fst <$> fgetState (cg_usedpos . ist_callgraph n)
623 let usedArgs = [a | (i,a) <- zip [0..] args, i `elem` used]
624 LConCase (-1) n usedArgs <$> irSC (methodVars `M.union` vs) sc
626 methodVars = case n of
627 SN (InstanceCtorN className)
628 -> M.fromList [(v, VI
629 { viMethod = Just $ mkFieldName n i
630 }) | (v,i) <- zip args [0..]]
632 -> M.empty -- not an instance constructor
634irAlt vs _ (ConstCase x rhs)
635 | matchable x = LConstCase x <$> irSC vs rhs
636 | matchableTy x = LDefaultCase <$> irSC vs rhs
638 matchable (I _) = True
639 matchable (BI _) = True
640 matchable (Ch _) = True
641 matchable (Str _) = True
642 matchable (B8 _) = True
643 matchable (B16 _) = True
644 matchable (B32 _) = True
645 matchable (B64 _) = True
648 matchableTy (AType (ATInt ITNative)) = True
649 matchableTy (AType (ATInt ITBig)) = True
650 matchableTy (AType (ATInt ITChar)) = True
651 matchableTy StrType = True
653 matchableTy (AType (ATInt (ITFixed IT8))) = True
654 matchableTy (AType (ATInt (ITFixed IT16))) = True
655 matchableTy (AType (ATInt (ITFixed IT32))) = True
656 matchableTy (AType (ATInt (ITFixed IT64))) = True
658 matchableTy _ = False
660irAlt vs tm (SucCase n rhs) = do
662 return $ LDefaultCase (LLet n (LOp (LMinus (ATInt ITBig))
664 LConst (BI 1)]) rhs')
666irAlt vs _ (ConstCase c rhs)
667 = ifail $ "Can't match on (" ++ show c ++ ")"
669irAlt vs _ (DefaultCase rhs)
670 = LDefaultCase <$> irSC 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