1{-# LANGUAGE PatternGuards, TypeSynonymInstances, CPP #-}
3module IRTS.Compiler where
6import IRTS.Defunctionalise
8import IRTS.CodegenCommon
10import IRTS.CodegenJava
12import IRTS.CodegenJavaScript
14import IRTS.CodegenLLVM
27import Idris.Core.Evaluate
28import Idris.Core.CaseTree
30import Control.Applicative
31import Control.Monad.State
34import Data.IntSet (IntSet)
35import qualified Data.IntMap as IM
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 ()
50 let tmnames = namesUsed (STerm tm)
51 usedIn <- mapM (allNames []) tmnames
52 let used = sUN "prim__subBigInt" : sUN "prim__addBigInt" : concat usedIn
54 -- TODO: DEBUG-ONLY, remove
55 ctx <- tt_ctxt <$> getIState
56 ci <- idris_classes <$> getIState
57 let depMap = buildDepMap ci ctx used
58 let printItem (cond, deps) = show (S.toList cond) ++ " -> " ++ show (S.toList deps)
59 -- iLOG $ "USAGE ANALYSIS:\n" ++ unlines (map printItem . M.toList $ depMap)
61 let (residDeps, (reachableNames', minUse)) = minimalUsage depMap
62 reachableNames = sUN "prim__subBigInt" : sUN "prim__addBigInt" : S.toList reachableNames'
63 iLOG $ "RESIDUAL DEPS:\n" ++ unlines (map printItem . M.toList $ residDeps)
64 iLOG $ "REACHABLE NAMES:\n" ++ unlines (map show reachableNames)
66 let fmtDepMap = unlines . map (\(n,is) -> show n ++ " -> " ++ show (IS.toList is)) . M.toList
67 iLOG $ "MINIMAL USAGE:\n" ++ fmtDepMap minUse
69 mapM_ (uncurry storeUsage) (M.toList minUse)
73 iLOG $ "MAIN: " ++ show maindef
74 iLOG $ "USED: " ++ show used
75 objs <- getObjectFiles codegen
76 libs <- getLibs codegen
77 flags <- getFlags codegen
78 hdrs <- getHdrs codegen
79 impdirs <- allImportDirs
80 defsIn <- mkDecls tm reachableNames
81 let defs = defsIn ++ [(sMN 0 "runMain", maindef)]
82 -- iputStrLn $ showSep "\n" (map show defs)
83 let (nexttag, tagged) = addTags 65536 (liftAll defs)
84 let ctxtIn = addAlist tagged emptyContext
85 iLOG "Defunctionalising"
86 let defuns_in = defunctionalise nexttag ctxtIn
87 logLvl 5 $ show defuns_in
89 let defuns = inline defuns_in
90 logLvl 5 $ show defuns
91 iLOG "Resolving variables for CG"
92 -- iputStrLn $ showSep "\n" (map show (toAlist defuns))
93 let checked = checkDefs defuns (toAlist defuns)
95 dumpCases <- getDumpCases
96 dumpDefun <- getDumpDefun
99 Just f -> runIO $ writeFile f (showCaseTrees defs)
102 Just f -> runIO $ writeFile f (dumpDefuns defuns)
103 triple <- Idris.AbsSyntax.targetTriple
104 cpu <- Idris.AbsSyntax.targetCPU
106 iLOG "Building output"
109 OK c -> do let cginfo = CodegenInfo f outty triple cpu optimise
110 hdrs impdirs objs libs flags
111 NONE c (toAlist defuns)
113 runIO $ case codegen of
114 ViaC -> codegenC cginfo
115 ViaJava -> codegenJava cginfo
116 ViaJavaScript -> codegenJavaScript cginfo
117 ViaNode -> codegenNode cginfo
118 ViaLLVM -> codegenLLVM cginfo
119 Bytecode -> dumpBC c f
121 where checkMVs = do i <- getIState
122 case map fst (idris_metavars i) \\ primDefs of
124 ms -> ifail $ "There are undefined metavariables: " ++ show ms
125 inDir d h = do let f = d </> h
126 ex <- doesFileExist f
127 if ex then return f else return h
129storeUsage :: Name -> IntSet -> Idris ()
130storeUsage n args = do
131 cg <- idris_callgraph <$> getIState
132 case lookupCtxt n cg of
133 [x] -> addToCG n x{ usedpos = IS.toList args }
134 _ -> addToCG n (CGInfo [] [] [] [] (IS.toList args)) -- data ctors
136irMain :: TT Name -> Idris LDecl
137irMain tm = do i <- ir tm
138 return $ LFun [] (sMN 0 "runMain") [] (LForce i)
140mkDecls :: Term -> [Name] -> Idris [(Name, LDecl)]
143 let ds = filter (\ (n, d) -> n `elem` used || isCon d) $ ctxtAlist (tt_ctxt i)
144 decls <- mapM build ds
147showCaseTrees :: [(Name, LDecl)] -> String
148showCaseTrees ds = showSep "\n\n" (map showCT ds)
150 showCT (n, LFun _ f args lexp)
151 = show n ++ " " ++ showSep " " (map show args) ++ " =\n\t "
153 showCT (n, LConstructor c t a) = "data " ++ show n ++ " " ++ show a
155isCon (TyDecl _ _) = True
159 ir :: a -> Idris LExp
161build :: (Name, Def) -> Idris (Name, LDecl)
164 case lookup n (idris_scprims i) of
166 let args = map (\x -> sMN x "op") [0..] in
167 return (n, (LFun [] n (take ar args)
168 (LOp op (map (LV . Glob) (take ar args)))))
169 _ -> do def <- mkLDecl n d
170 logLvl 3 $ "Compiled " ++ show n ++ " =\n\t" ++ show def
173getPrim :: IState -> Name -> [LExp] -> Maybe LExp
174getPrim i n args = case lookup n (idris_scprims i) of
176 if (ar == length args)
177 then return (LOp op args)
181declArgs args inl n (LLam xs x) = declArgs (args ++ xs) inl n x
182declArgs args inl n x = LFun (if inl then [Inline] else []) n args x
184mkLDecl n (Function tm _) = do e <- ir tm
185 return (declArgs [] True n e)
186mkLDecl n (CaseOp ci _ _ _ pats cd)
187 = let (args, sc) = cases_runtime cd in
188 do e <- ir (args, sc)
189 return (declArgs [] (case_inlinable ci) n e)
190mkLDecl n (TyDecl (DCon t a) _) = return $ LConstructor n t a
191mkLDecl n (TyDecl (TCon t a) _) = return $ LConstructor n (-1) a
192mkLDecl n _ = return $ (declArgs [] True n LNothing) -- postulate, never run
194instance ToIR (TT Name) where
195 ir tm = ir' [] tm where
197 | (P _ (UN m) _, args) <- unApply tm,
198 m == txt "mkForeignPrim"
200 | (P _ (UN u) _, [_, arg]) <- unApply tm,
201 u == txt "unsafePerformPrimIO"
203 -- TMP HACK - until we get inlining.
204 | (P _ (UN r) _, [_, _, _, _, _, arg]) <- unApply tm,
207 -- Laziness, the old way
208 | (P _ (UN l) _, [_, arg]) <- unApply tm,
210 = do arg' <- ir' env arg
211 error "lazy has crept in somehow"
212-- return $ LLazyExp arg'
213 | (P _ (UN l) _, [_, arg]) <- unApply tm,
215 = do arg' <- ir' env arg
217 -- Laziness, the new way
218 | (P _ (UN l) _, [arg]) <- unApply tm,
220 = do arg' <- ir' env arg
221 return $ LLazyExp arg'
222 | (P _ (UN l) _, [_, _, arg]) <- unApply tm,
224 = do arg' <- ir' env arg
226 | (P _ (UN a) _, [_, _, arg]) <- unApply tm,
227 a == txt "assert_smaller"
229 | (P _ (UN a) _, [_, arg]) <- unApply tm,
230 a == txt "assert_total"
232 | (P _ (UN p) _, [_, arg]) <- unApply tm,
234 = do arg' <- ir' env arg
235 return $ LOp LPar [LLazyExp arg']
236 | (P _ (UN pf) _, [arg]) <- unApply tm,
237 pf == txt "prim_fork"
238 = do arg' <- ir' env arg
239 return $ LOp LFork [LLazyExp arg']
240 | (P _ (UN m) _, [_,size,t]) <- unApply tm,
242 = do size' <- ir' env size
244 return t' -- TODO $ malloc_ size' t'
245 | (P _ (UN tm) _, [_,t]) <- unApply tm,
246 tm == txt "trace_malloc"
249 -- This case is here until we get more general inlining. It's just
250 -- a really common case, and the laziness hurts...
251 | (P _ (NS (UN be) [b,p]) _, [_,x,(App (P _ (UN d) _) t),
252 (App (P _ (UN d') _) e)]) <- unApply tm,
253 be == txt "boolElim" && d == txt "Delay" && d' == txt "Delay"
257 return (LCase x' [LConCase 0 (sNS (sUN "False") ["Bool","Prelude"]) [] e',
258 LConCase 1 (sNS (sUN "True") ["Bool","Prelude"]) [] t'])
259 | (P (DCon t a) n _, args) <- unApply tm
261 cg <- idris_callgraph <$> getIState
262 case lookupCtxtExact n cg of
263 Just (CGInfo _ _ _ _ usedpos)
264 -> irCon env t a n [if i `elem` usedpos then a else Erased | (i,a) <- zip [0..] args]
265 Nothing -> irCon env t a n args
267 | (P (DCon t a) n _, args) <- unApply tm
268 = irCon env t a n args
269 | (P (TCon t a) n _, args) <- unApply tm
272 | (P _ n _, args) <- unApply tm = do
274 case lookup n (idris_scprims ist) of
275 -- if it's a primitive, compile to the corresponding code
276 Just _ -> fromJust . getPrim ist n <$> mapM (ir' env) args
279 -- if it's collapsible, compile to LNothing
280 | (collapsible <$> lookupCtxtExact n (idris_optimisation ist)) == Just True
283 -- otherwise, compile normally
284 | otherwise -> applyName n ist args
286 | (f, args) <- unApply tm
288 args' <- mapM (ir' env) args
289 return (LApp False f' args')
292 applyName :: Name -> IState -> [Term] -> Idris LExp
293 applyName n ist args =
294 LApp False (LV $ Glob n) <$> mapM (ir' env . erase) (zip [0..] args)
297 | i >= arity || i `elem` used = x
300 arity = case fst4 <$> lookupCtxt n (definitions . tt_ctxt $ ist) of
301 [CaseOp ci ty tys def tot cdefs] -> length tys
304 used = fromMaybe [] (usedpos <$> lookupCtxtExact n (idris_callgraph ist))
307-- ir' env (P _ (NS (UN "Z") ["Nat", "Prelude"]) _)
308-- = return $ LConst (BI 0)
309 ir' env (P _ n _) = return $ LV (Glob n)
310 ir' env (V i) | i >= 0 && i < length env = return $ LV (Glob (env!!i))
311 | otherwise = ifail $ "IR fail " ++ show i ++ " " ++ show tm
312 ir' env (Bind n (Lam _) sc)
313 = do let n' = uniqueName n env
314 sc' <- ir' (n' : env) sc
315 return $ LLam [n'] sc'
316 ir' env (Bind n (Let _ v) sc)
317 = do sc' <- ir' (n : env) sc
319 return $ LLet n v' sc'
320 ir' env (Bind _ _ _) = return $ LNothing
321 ir' env (Proj t i) | i == -1
323 return $ LOp (LMinus (ATInt ITBig))
325 ir' env (Proj t i) = do t' <- ir' env t
327 ir' env (Constant c) = return $ LConst c
328 ir' env (TType _) = return $ LNothing
329 ir' env Erased = return $ LNothing
330 ir' env Impossible = return $ LNothing
331-- ir' env _ = return $ LError "Impossible"
333 irCon env t arity n args
334 | length args == arity = buildApp env (LV (Glob n)) args
335 | otherwise = let extra = satArgs (arity - length args) in
336 do sc' <- irCon env t arity n
337 (args ++ map (\n -> P Bound n undefined) extra)
338 return $ LLam extra sc'
340 satArgs n = map (\i -> sMN i "sat") [1..n]
342 buildApp env e [] = return e
343 buildApp env e xs = do xs' <- mapM (ir' env) xs
344 return $ LApp False e xs'
346 doForeign :: [Name] -> [TT Name] -> Idris LExp
347 doForeign env (_ : fgn : args)
348 | (_, (Constant (Str fgnName) : fgnArgTys : ret : [])) <- unApply fgn
349 = let maybeTys = getFTypes fgnArgTys
352 Nothing -> ifail $ "Foreign type specification is not a constant list: " ++ show (fgn:args)
354 args' <- mapM (ir' env) (init args)
355 -- wrap it in a prim__IO
356 -- return $ con_ 0 @@ impossible @@
357 return $ -- LLazyExp $
358 LForeign LANG_C rty fgnName (zip tys args')
359 | otherwise = ifail "Badly formed foreign function call"
361getFTypes :: TT Name -> Maybe [FType]
362getFTypes tm = case unApply tm of
365 fmap (mkIty' ty :) (getFTypes xs)
368mkIty' (P _ (UN ty) _) = mkIty (str ty)
369mkIty' (App (P _ (UN fi) _) (P _ (UN intTy) _))
370 | fi == txt "FIntT" = mkIntIty (str intTy)
371mkIty' (App (App (P _ (UN ff) _) _) (App (P _ (UN fa) _) (App (P _ (UN io) _) _)))
372 | ff == txt "FFunction" && fa == txt "FAny" &&
375mkIty' (App (App (P _ (UN ff) _) _) _)
376 | ff == txt "FFunction" = FFunction
379-- would be better if these FInt types were evaluated at compile time
380-- TODO: add %eval directive for such things
382mkIty "FFloat" = FArith ATFloat
383mkIty "FInt" = mkIntIty "ITNative"
384mkIty "FChar" = mkIntIty "ITChar"
385mkIty "FByte" = mkIntIty "IT8"
386mkIty "FShort" = mkIntIty "IT16"
387mkIty "FLong" = mkIntIty "IT64"
388mkIty "FBits8" = mkIntIty "IT8"
389mkIty "FBits16" = mkIntIty "IT16"
390mkIty "FBits32" = mkIntIty "IT32"
391mkIty "FBits64" = mkIntIty "IT64"
392mkIty "FString" = FString
394mkIty "FManagedPtr" = FManagedPtr
396mkIty "FFunction" = FFunction
397mkIty "FFunctionIO" = FFunctionIO
398mkIty "FBits8x16" = FArith (ATInt (ITVec IT8 16))
399mkIty "FBits16x8" = FArith (ATInt (ITVec IT16 8))
400mkIty "FBits32x4" = FArith (ATInt (ITVec IT32 4))
401mkIty "FBits64x2" = FArith (ATInt (ITVec IT64 2))
402mkIty x = error $ "Unknown type " ++ x
404mkIntIty "ITNative" = FArith (ATInt ITNative)
405mkIntIty "ITChar" = FArith (ATInt ITChar)
406mkIntIty "IT8" = FArith (ATInt (ITFixed IT8))
407mkIntIty "IT16" = FArith (ATInt (ITFixed IT16))
408mkIntIty "IT32" = FArith (ATInt (ITFixed IT32))
409mkIntIty "IT64" = FArith (ATInt (ITFixed IT64))
411zname = sNS (sUN "Z") ["Nat","Prelude"]
412sname = sNS (sUN "S") ["Nat","Prelude"]
414instance ToIR ([Name], SC) where
415 ir (args, tree) = do logLvl 3 $ "Compiling " ++ show args ++ "\n" ++ show tree
417 return $ LLam args tree'
419instance ToIR SC where
423 ir' (UnmatchedCase str) = return $ LError str
424 ir' (ProjCase tm alt) = do tm' <- ir tm
425 alt' <- mkIRAlt tm' alt
426 return $ LCase tm' [alt']
427 ir' (Case n alts) = do alts' <- mapM (mkIRAlt (LV (Glob n))) alts
428 return $ LCase (LV (Glob n)) alts'
429 ir' ImpossibleCase = return LNothing
431 -- special cases for Z and S
432 -- Needs rethink: projections make this fail
433-- mkIRAlt n (ConCase z _ [] rhs) | z == zname
434-- = mkIRAlt n (ConstCase (BI 0) rhs)
435-- mkIRAlt n (ConCase s _ [arg] rhs) | s == sname
438-- return $ LDefaultCase
439-- (LLet arg (LOp LBMinus [n', LConst (BI 1)])
441 mkIRAlt _ (ConCase n t args rhs)
443 return $ LConCase (-1) n args rhs'
444 mkIRAlt _ (ConstCase x rhs)
447 return $ LConstCase x rhs'
450 return $ LDefaultCase rhs'
451 mkIRAlt tm (SucCase n rhs)
453 return $ LDefaultCase (LLet n (LOp (LMinus (ATInt ITBig))
455 LConst (BI 1)]) rhs')
456-- return $ LSucCase n rhs'
457 mkIRAlt _ (ConstCase c rhs)
458 = ifail $ "Can't match on (" ++ show c ++ ")"
459 mkIRAlt _ (DefaultCase rhs)
461 return $ LDefaultCase rhs'
463 matchable (I _) = True
464 matchable (BI _) = True
465 matchable (Ch _) = True
466 matchable (Str _) = True
469 matchableTy (AType (ATInt ITNative)) = True
470 matchableTy (AType (ATInt ITBig)) = True
471 matchableTy (AType (ATInt ITChar)) = True
472 matchableTy StrType = True
474 matchableTy (AType (ATInt (ITFixed IT8))) = True
475 matchableTy (AType (ATInt (ITFixed IT16))) = True
476 matchableTy (AType (ATInt (ITFixed IT32))) = True
477 matchableTy (AType (ATInt (ITFixed IT64))) = True
479 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