1{-# LANGUAGE PatternGuards, TypeSynonymInstances, CPP #-}
3module IRTS.Compiler where
6import IRTS.Defunctionalise
8import IRTS.CodegenCommon
10import IRTS.CodegenJava
12import IRTS.CodegenJavaScript
14import IRTS.CodegenLLVM
25import Idris.Core.Evaluate
26import Idris.Core.CaseTree
28import Control.Applicative
29import Control.Monad.State
31import Data.IntSet (IntSet)
32import qualified Data.IntMap as IM
33import qualified Data.IntSet as IS
34import qualified Data.Map as M
35import qualified Data.Set as S
38import System.Directory
39import System.Environment
40import System.FilePath ((</>), addTrailingPathSeparator)
44compile :: Codegen -> FilePath -> Term -> Idris ()
47 let tmnames = namesUsed (STerm tm)
48 usedIn <- mapM (allNames []) tmnames
49 let used = sUN "prim__subBigInt" : sUN "prim__addBigInt" : concat usedIn
51 -- TODO: DEBUG-ONLY, remove
52 ctx <- tt_ctxt <$> getIState
53 let depMap = buildDepMap ctx used
54 let printItem (cond, deps) = show (S.toList cond) ++ " -> " ++ show (S.toList deps)
55 iLOG $ "USAGE ANALYSIS:\n" ++ unlines (map printItem . M.toList $ depMap)
57 let (reachableNames', minUse) = minimalUsage depMap
58 reachableNames = sUN "prim__subBigInt" : sUN "prim__addBigInt" : S.toList reachableNames'
59 iLOG $ "REACHABLE NAMES: " ++ show reachableNames
61 let fmtDepMap = unlines . map (\(n,is) -> show n ++ " -> " ++ show (IS.toList is)) . M.toList
62 iLOG $ "MINIMAL USAGE:\n" ++ fmtDepMap minUse
64 mapM_ (uncurry storeUsage) (M.toList minUse)
68 iLOG $ "MAIN: " ++ show maindef
69 iLOG $ "USED: " ++ show used
70 objs <- getObjectFiles codegen
71 libs <- getLibs codegen
72 flags <- getFlags codegen
73 hdrs <- getHdrs codegen
74 impdirs <- allImportDirs
75 defsIn <- mkDecls tm reachableNames
76 let defs = defsIn ++ [(sMN 0 "runMain", maindef)]
77 -- iputStrLn $ showSep "\n" (map show defs)
78 let (nexttag, tagged) = addTags 65536 (liftAll defs)
79 let ctxtIn = addAlist tagged emptyContext
80 iLOG "Defunctionalising"
81 let defuns_in = defunctionalise nexttag ctxtIn
82 logLvl 5 $ show defuns_in
84 let defuns = inline defuns_in
85 logLvl 5 $ show defuns
86 iLOG "Resolving variables for CG"
87 -- iputStrLn $ showSep "\n" (map show (toAlist defuns))
88 let checked = checkDefs defuns (toAlist defuns)
90 dumpCases <- getDumpCases
91 dumpDefun <- getDumpDefun
94 Just f -> runIO $ writeFile f (showCaseTrees defs)
97 Just f -> runIO $ writeFile f (dumpDefuns defuns)
98 triple <- Idris.AbsSyntax.targetTriple
99 cpu <- Idris.AbsSyntax.targetCPU
101 iLOG "Building output"
104 OK c -> do let cginfo = CodegenInfo f outty triple cpu optimise
105 hdrs impdirs objs libs flags
106 NONE c (toAlist defuns)
108 runIO $ case codegen of
109 ViaC -> codegenC cginfo
110 ViaJava -> codegenJava cginfo
111 ViaJavaScript -> codegenJavaScript cginfo
112 ViaNode -> codegenNode cginfo
113 ViaLLVM -> codegenLLVM cginfo
114 Bytecode -> dumpBC c f
116 where checkMVs = do i <- getIState
117 case map fst (idris_metavars i) \\ primDefs of
119 ms -> ifail $ "There are undefined metavariables: " ++ show ms
120 inDir d h = do let f = d </> h
121 ex <- doesFileExist f
122 if ex then return f else return h
124storeUsage :: Name -> IntSet -> Idris ()
125storeUsage n args = do
126 cg <- idris_callgraph <$> getIState
127 case lookupCtxt n cg of
128 [x] -> addToCG n x{ usedpos = IS.toList args }
131irMain :: TT Name -> Idris LDecl
132irMain tm = do i <- ir tm
133 return $ LFun [] (sMN 0 "runMain") [] (LForce i)
135mkDecls :: Term -> [Name] -> Idris [(Name, LDecl)]
138 let ds = filter (\ (n, d) -> n `elem` used || isCon d) $ ctxtAlist (tt_ctxt i)
139 decls <- mapM build ds
142showCaseTrees :: [(Name, LDecl)] -> String
143showCaseTrees ds = showSep "\n\n" (map showCT ds)
145 showCT (n, LFun _ f args lexp)
146 = show n ++ " " ++ showSep " " (map show args) ++ " =\n\t "
148 showCT (n, LConstructor c t a) = "data " ++ show n ++ " " ++ show a
150isCon (TyDecl _ _) = True
154 ir :: a -> Idris LExp
156build :: (Name, Def) -> Idris (Name, LDecl)
159 case lookup n (idris_scprims i) of
161 let args = map (\x -> sMN x "op") [0..] in
162 return (n, (LFun [] n (take ar args)
163 (LOp op (map (LV . Glob) (take ar args)))))
164 _ -> do def <- mkLDecl n d
165 logLvl 3 $ "Compiled " ++ show n ++ " =\n\t" ++ show def
168getPrim :: IState -> Name -> [LExp] -> Maybe LExp
169getPrim i n args = case lookup n (idris_scprims i) of
171 if (ar == length args)
172 then return (LOp op args)
176declArgs args inl n (LLam xs x) = declArgs (args ++ xs) inl n x
177declArgs args inl n x = LFun (if inl then [Inline] else []) n args x
179mkLDecl n (Function tm _) = do e <- ir tm
180 return (declArgs [] True n e)
181mkLDecl n (CaseOp ci _ _ _ pats cd)
182 = let (args, sc) = cases_runtime cd in
183 do e <- ir (args, sc)
184 return (declArgs [] (case_inlinable ci) n e)
185mkLDecl n (TyDecl (DCon t a) _) = return $ LConstructor n t a
186mkLDecl n (TyDecl (TCon t a) _) = return $ LConstructor n (-1) a
187mkLDecl n _ = return $ (declArgs [] True n LNothing) -- postulate, never run
189instance ToIR (TT Name) where
190 ir tm = ir' [] tm where
192 | (P _ (UN m) _, args) <- unApply tm,
193 m == txt "mkForeignPrim"
195 | (P _ (UN u) _, [_, arg]) <- unApply tm,
196 u == txt "unsafePerformPrimIO"
198 -- TMP HACK - until we get inlining.
199 | (P _ (UN r) _, [_, _, _, _, _, arg]) <- unApply tm,
202 -- Laziness, the old way
203 | (P _ (UN l) _, [_, arg]) <- unApply tm,
205 = do arg' <- ir' env arg
206 error "lazy has crept in somehow"
207-- return $ LLazyExp arg'
208 | (P _ (UN l) _, [_, arg]) <- unApply tm,
210 = do arg' <- ir' env arg
212 -- Laziness, the new way
213 | (P _ (UN l) _, [arg]) <- unApply tm,
215 = do arg' <- ir' env arg
216 return $ LLazyExp arg'
217 | (P _ (UN l) _, [_, _, arg]) <- unApply tm,
219 = do arg' <- ir' env arg
221 | (P _ (UN a) _, [_, _, arg]) <- unApply tm,
222 a == txt "assert_smaller"
224 | (P _ (UN a) _, [_, arg]) <- unApply tm,
225 a == txt "assert_total"
227 | (P _ (UN p) _, [_, arg]) <- unApply tm,
229 = do arg' <- ir' env arg
230 return $ LOp LPar [LLazyExp arg']
231 | (P _ (UN pf) _, [arg]) <- unApply tm,
232 pf == txt "prim_fork"
233 = do arg' <- ir' env arg
234 return $ LOp LFork [LLazyExp arg']
235 | (P _ (UN m) _, [_,size,t]) <- unApply tm,
237 = do size' <- ir' env size
239 return t' -- TODO $ malloc_ size' t'
240 | (P _ (UN tm) _, [_,t]) <- unApply tm,
241 tm == txt "trace_malloc"
244 -- This case is here until we get more general inlining. It's just
245 -- a really common case, and the laziness hurts...
246 | (P _ (NS (UN be) [b,p]) _, [_,x,(App (P _ (UN d) _) t),
247 (App (P _ (UN d') _) e)]) <- unApply tm,
248 be == txt "boolElim" && d == txt "Delay" && d' == txt "Delay"
252 return (LCase x' [LConCase 0 (sNS (sUN "False") ["Bool","Prelude"]) [] e',
253 LConCase 1 (sNS (sUN "True") ["Bool","Prelude"]) [] t'])
254 | (P (DCon t a) n _, args) <- unApply tm
255 = irCon env t a n args
256 | (P (TCon t a) n _, args) <- unApply tm
258 | (P _ n _, args) <- unApply tm
260 args' <- mapM (ir' env) args
261 case getPrim i n args' of
265 = case lookupCtxtExact n
266 (idris_optimisation i) of
267 Just oi -> collapsible oi
270 = case lookupCtxtExact n
271 (idris_callgraph i) of
272 Just (CGInfo _ _ _ _ usedpos) ->
274 _ -> [] -- TODO: is this correct?
277 else return (LApp False (LV (Glob n))
278 (mkUsed used 0 args'))
279 | (f, args) <- unApply tm
281 args' <- mapM (ir' env) args
282 return (LApp False f' args')
283 where mkUsed u i [] = []
284 mkUsed u i (x : xs) | i `elem` u = x : mkUsed u (i + 1) xs
285 | otherwise = LNothing : mkUsed u (i + 1) xs
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
344 fmap (mkIty' ty :) (getFTypes xs)
347mkIty' (P _ (UN ty) _) = mkIty (str ty)
348mkIty' (App (P _ (UN fi) _) (P _ (UN intTy) _))
349 | fi == txt "FIntT" = mkIntIty (str intTy)
350mkIty' (App (App (P _ (UN ff) _) _) (App (P _ (UN fa) _) (App (P _ (UN io) _) _)))
351 | ff == txt "FFunction" && fa == txt "FAny" &&
354mkIty' (App (App (P _ (UN ff) _) _) _)
355 | ff == txt "FFunction" = FFunction
358-- would be better if these FInt types were evaluated at compile time
359-- TODO: add %eval directive for such things
361mkIty "FFloat" = FArith ATFloat
362mkIty "FInt" = mkIntIty "ITNative"
363mkIty "FChar" = mkIntIty "ITChar"
364mkIty "FByte" = mkIntIty "IT8"
365mkIty "FShort" = mkIntIty "IT16"
366mkIty "FLong" = mkIntIty "IT64"
367mkIty "FBits8" = mkIntIty "IT8"
368mkIty "FBits16" = mkIntIty "IT16"
369mkIty "FBits32" = mkIntIty "IT32"
370mkIty "FBits64" = mkIntIty "IT64"
371mkIty "FString" = FString
373mkIty "FManagedPtr" = FManagedPtr
375mkIty "FFunction" = FFunction
376mkIty "FFunctionIO" = FFunctionIO
377mkIty "FBits8x16" = FArith (ATInt (ITVec IT8 16))
378mkIty "FBits16x8" = FArith (ATInt (ITVec IT16 8))
379mkIty "FBits32x4" = FArith (ATInt (ITVec IT32 4))
380mkIty "FBits64x2" = FArith (ATInt (ITVec IT64 2))
381mkIty x = error $ "Unknown type " ++ x
383mkIntIty "ITNative" = FArith (ATInt ITNative)
384mkIntIty "ITChar" = FArith (ATInt ITChar)
385mkIntIty "IT8" = FArith (ATInt (ITFixed IT8))
386mkIntIty "IT16" = FArith (ATInt (ITFixed IT16))
387mkIntIty "IT32" = FArith (ATInt (ITFixed IT32))
388mkIntIty "IT64" = FArith (ATInt (ITFixed IT64))
390zname = sNS (sUN "Z") ["Nat","Prelude"]
391sname = sNS (sUN "S") ["Nat","Prelude"]
393instance ToIR ([Name], SC) where
394 ir (args, tree) = do logLvl 3 $ "Compiling " ++ show args ++ "\n" ++ show tree
396 return $ LLam args tree'
398instance ToIR SC where
402 ir' (UnmatchedCase str) = return $ LError str
403 ir' (ProjCase tm alt) = do tm' <- ir tm
404 alt' <- mkIRAlt tm' alt
405 return $ LCase tm' [alt']
406 ir' (Case n alts) = do alts' <- mapM (mkIRAlt (LV (Glob n))) alts
407 return $ LCase (LV (Glob n)) alts'
408 ir' ImpossibleCase = return LNothing
410 -- special cases for Z and S
411 -- Needs rethink: projections make this fail
412-- mkIRAlt n (ConCase z _ [] rhs) | z == zname
413-- = mkIRAlt n (ConstCase (BI 0) rhs)
414-- mkIRAlt n (ConCase s _ [arg] rhs) | s == sname
417-- return $ LDefaultCase
418-- (LLet arg (LOp LBMinus [n', LConst (BI 1)])
420 mkIRAlt _ (ConCase n t args rhs)
422 return $ LConCase (-1) n args rhs'
423 mkIRAlt _ (ConstCase x rhs)
426 return $ LConstCase x rhs'
429 return $ LDefaultCase rhs'
430 mkIRAlt tm (SucCase n rhs)
432 return $ LDefaultCase (LLet n (LOp (LMinus (ATInt ITBig))
434 LConst (BI 1)]) rhs')
435-- return $ LSucCase n rhs'
436 mkIRAlt _ (ConstCase c rhs)
437 = ifail $ "Can't match on (" ++ show c ++ ")"
438 mkIRAlt _ (DefaultCase rhs)
440 return $ LDefaultCase rhs'
442 matchable (I _) = True
443 matchable (BI _) = True
444 matchable (Ch _) = True
445 matchable (Str _) = True
448 matchableTy (AType (ATInt ITNative)) = True
449 matchableTy (AType (ATInt ITBig)) = True
450 matchableTy (AType (ATInt ITChar)) = True
451 matchableTy StrType = True
453 matchableTy (AType (ATInt (ITFixed IT8))) = True
454 matchableTy (AType (ATInt (ITFixed IT16))) = True
455 matchableTy (AType (ATInt (ITFixed IT32))) = True
456 matchableTy (AType (ATInt (ITFixed IT64))) = True
458 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