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
50 defsIn <- mkDecls tm used
53 -- TODO: traverse all names correctly
55 -- TODO: DEBUG-ONLY, remove
56 ctx <- tt_ctxt <$> getIState
57 let depMap = buildDepMap 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 fmtDepMap = unlines . map (\(n,is) -> show n ++ " -> " ++ show (IS.toList is)) . M.toList
62 let minUse = minimalUsage depMap
63 iLOG $ "MINIMAL USAGE:\n" ++ fmtDepMap minUse
65 mapM_ (uncurry storeUsage) (M.toList minUse)
69 objs <- getObjectFiles codegen
70 libs <- getLibs codegen
71 flags <- getFlags codegen
72 hdrs <- getHdrs codegen
73 impdirs <- allImportDirs
74 let defs = defsIn ++ [(sMN 0 "runMain", maindef)]
75 -- iputStrLn $ showSep "\n" (map show defs)
76 let (nexttag, tagged) = addTags 65536 (liftAll defs)
77 let ctxtIn = addAlist tagged emptyContext
78 iLOG "Defunctionalising"
79 let defuns_in = defunctionalise nexttag ctxtIn
80 logLvl 5 $ show defuns_in
82 let defuns = inline defuns_in
83 logLvl 5 $ show defuns
84 iLOG "Resolving variables for CG"
85 -- iputStrLn $ showSep "\n" (map show (toAlist defuns))
86 let checked = checkDefs defuns (toAlist defuns)
88 dumpCases <- getDumpCases
89 dumpDefun <- getDumpDefun
92 Just f -> runIO $ writeFile f (showCaseTrees defs)
95 Just f -> runIO $ writeFile f (dumpDefuns defuns)
96 triple <- Idris.AbsSyntax.targetTriple
97 cpu <- Idris.AbsSyntax.targetCPU
99 iLOG "Building output"
102 OK c -> do let cginfo = CodegenInfo f outty triple cpu optimise
103 hdrs impdirs objs libs flags
104 NONE c (toAlist defuns)
106 runIO $ case codegen of
107 ViaC -> codegenC cginfo
108 ViaJava -> codegenJava cginfo
109 ViaJavaScript -> codegenJavaScript cginfo
110 ViaNode -> codegenNode cginfo
111 ViaLLVM -> codegenLLVM cginfo
112 Bytecode -> dumpBC c f
114 where checkMVs = do i <- getIState
115 case map fst (idris_metavars i) \\ primDefs of
117 ms -> ifail $ "There are undefined metavariables: " ++ show ms
118 inDir d h = do let f = d </> h
119 ex <- doesFileExist f
120 if ex then return f else return h
122storeUsage :: Name -> IntSet -> Idris ()
123storeUsage n args = do
124 cg <- idris_callgraph <$> getIState
125 case lookupCtxt n cg of
126 [x] -> addToCG n x{ usedpos = IS.toList args }
129irMain :: TT Name -> Idris LDecl
130irMain tm = do i <- ir tm
131 return $ LFun [] (sMN 0 "runMain") [] (LForce i)
133mkDecls :: Term -> [Name] -> Idris [(Name, LDecl)]
136 let ds = filter (\ (n, d) -> n `elem` used || isCon d) $ ctxtAlist (tt_ctxt i)
138 decls <- mapM build ds
141showCaseTrees :: [(Name, LDecl)] -> String
142showCaseTrees ds = showSep "\n\n" (map showCT ds)
144 showCT (n, LFun _ f args lexp)
145 = show n ++ " " ++ showSep " " (map show args) ++ " =\n\t "
147 showCT (n, LConstructor c t a) = "data " ++ show n ++ " " ++ show a
149isCon (TyDecl _ _) = True
153 ir :: a -> Idris LExp
155build :: (Name, Def) -> Idris (Name, LDecl)
158 case lookup n (idris_scprims i) of
160 let args = map (\x -> sMN x "op") [0..] in
161 return (n, (LFun [] n (take ar args)
162 (LOp op (map (LV . Glob) (take ar args)))))
163 _ -> do def <- mkLDecl n d
164 logLvl 3 $ "Compiled " ++ show n ++ " =\n\t" ++ show def
167getPrim :: IState -> Name -> [LExp] -> Maybe LExp
168getPrim i n args = case lookup n (idris_scprims i) of
170 if (ar == length args)
171 then return (LOp op args)
175declArgs args inl n (LLam xs x) = declArgs (args ++ xs) inl n x
176declArgs args inl n x = LFun (if inl then [Inline] else []) n args x
178mkLDecl n (Function tm _) = do e <- ir tm
179 return (declArgs [] True n e)
180mkLDecl n (CaseOp ci _ _ _ pats cd)
181 = let (args, sc) = cases_runtime cd in
182 do e <- ir (args, sc)
183 return (declArgs [] (case_inlinable ci) n e)
184mkLDecl n (TyDecl (DCon t a) _) = return $ LConstructor n t a
185mkLDecl n (TyDecl (TCon t a) _) = return $ LConstructor n (-1) a
186mkLDecl n _ = return $ (declArgs [] True n LNothing) -- postulate, never run
188instance ToIR (TT Name) where
189 ir tm = ir' [] tm where
191 | (P _ (UN m) _, args) <- unApply tm,
192 m == txt "mkForeignPrim"
194 | (P _ (UN u) _, [_, arg]) <- unApply tm,
195 u == txt "unsafePerformPrimIO"
197 -- TMP HACK - until we get inlining.
198 | (P _ (UN r) _, [_, _, _, _, _, arg]) <- unApply tm,
201 -- Laziness, the old way
202 | (P _ (UN l) _, [_, arg]) <- unApply tm,
204 = do arg' <- ir' env arg
205 error "lazy has crept in somehow"
206-- return $ LLazyExp arg'
207 | (P _ (UN l) _, [_, arg]) <- unApply tm,
209 = do arg' <- ir' env arg
211 -- Laziness, the new way
212 | (P _ (UN l) _, [arg]) <- unApply tm,
214 = do arg' <- ir' env arg
215 return $ LLazyExp arg'
216 | (P _ (UN l) _, [_, _, arg]) <- unApply tm,
218 = do arg' <- ir' env arg
220 | (P _ (UN a) _, [_, _, arg]) <- unApply tm,
221 a == txt "assert_smaller"
223 | (P _ (UN a) _, [_, arg]) <- unApply tm,
224 a == txt "assert_total"
226 | (P _ (UN p) _, [_, arg]) <- unApply tm,
228 = do arg' <- ir' env arg
229 return $ LOp LPar [LLazyExp arg']
230 | (P _ (UN pf) _, [arg]) <- unApply tm,
231 pf == txt "prim_fork"
232 = do arg' <- ir' env arg
233 return $ LOp LFork [LLazyExp arg']
234 | (P _ (UN m) _, [_,size,t]) <- unApply tm,
236 = do size' <- ir' env size
238 return t' -- TODO $ malloc_ size' t'
239 | (P _ (UN tm) _, [_,t]) <- unApply tm,
240 tm == txt "trace_malloc"
243 -- This case is here until we get more general inlining. It's just
244 -- a really common case, and the laziness hurts...
245 | (P _ (NS (UN be) [b,p]) _, [_,x,(App (P _ (UN d) _) t),
246 (App (P _ (UN d') _) e)]) <- unApply tm,
247 be == txt "boolElim" && d == txt "Delay" && d' == txt "Delay"
251 return (LCase x' [LConCase 0 (sNS (sUN "False") ["Bool","Prelude"]) [] e',
252 LConCase 1 (sNS (sUN "True") ["Bool","Prelude"]) [] t'])
253 | (P (DCon t a) n _, args) <- unApply tm
254 = irCon env t a n args
255 | (P (TCon t a) n _, args) <- unApply tm
257 | (P _ n _, args) <- unApply tm
259 args' <- mapM (ir' env) args
260 case getPrim i n args' of
264 = case lookupCtxtExact n
265 (idris_optimisation i) of
266 Just oi -> collapsible oi
269 = case lookupCtxtExact n
270 (idris_callgraph i) of
271 Just (CGInfo _ _ _ _ usedpos) ->
273 _ -> [] -- TODO: is this correct?
276 else return (LApp False (LV (Glob n))
277 (mkUsed used 0 args'))
278 | (f, args) <- unApply tm
280 args' <- mapM (ir' env) args
281 return (LApp False f' args')
282 where mkUsed u i [] = []
283 mkUsed u i (x : xs) | i `notElem` u = LNothing : mkUsed u (i + 1) xs
284 | otherwise = x : mkUsed u (i + 1) xs
285-- ir' env (P _ (NS (UN "Z") ["Nat", "Prelude"]) _)
286-- = return $ LConst (BI 0)
287 ir' env (P _ n _) = return $ LV (Glob n)
288 ir' env (V i) | i >= 0 && i < length env = return $ LV (Glob (env!!i))
289 | otherwise = ifail $ "IR fail " ++ show i ++ " " ++ show tm
290 ir' env (Bind n (Lam _) sc)
291 = do let n' = uniqueName n env
292 sc' <- ir' (n' : env) sc
293 return $ LLam [n'] sc'
294 ir' env (Bind n (Let _ v) sc)
295 = do sc' <- ir' (n : env) sc
297 return $ LLet n v' sc'
298 ir' env (Bind _ _ _) = return $ LNothing
299 ir' env (Proj t i) | i == -1
301 return $ LOp (LMinus (ATInt ITBig))
303 ir' env (Proj t i) = do t' <- ir' env t
305 ir' env (Constant c) = return $ LConst c
306 ir' env (TType _) = return $ LNothing
307 ir' env Erased = return $ LNothing
308 ir' env Impossible = return $ LNothing
309-- ir' env _ = return $ LError "Impossible"
311 irCon env t arity n args
312 | length args == arity = buildApp env (LV (Glob n)) args
313 | otherwise = let extra = satArgs (arity - length args) in
314 do sc' <- irCon env t arity n
315 (args ++ map (\n -> P Bound n undefined) extra)
316 return $ LLam extra sc'
318 satArgs n = map (\i -> sMN i "sat") [1..n]
320 buildApp env e [] = return e
321 buildApp env e xs = do xs' <- mapM (ir' env) xs
322 return $ LApp False e xs'
324 doForeign :: [Name] -> [TT Name] -> Idris LExp
325 doForeign env (_ : fgn : args)
326 | (_, (Constant (Str fgnName) : fgnArgTys : ret : [])) <- unApply fgn
327 = let maybeTys = getFTypes fgnArgTys
330 Nothing -> ifail $ "Foreign type specification is not a constant list: " ++ show (fgn:args)
332 args' <- mapM (ir' env) (init args)
333 -- wrap it in a prim__IO
334 -- return $ con_ 0 @@ impossible @@
335 return $ -- LLazyExp $
336 LForeign LANG_C rty fgnName (zip tys args')
337 | otherwise = ifail "Badly formed foreign function call"
339getFTypes :: TT Name -> Maybe [FType]
340getFTypes tm = case unApply tm of
343 fmap (mkIty' ty :) (getFTypes xs)
346mkIty' (P _ (UN ty) _) = mkIty (str ty)
347mkIty' (App (P _ (UN fi) _) (P _ (UN intTy) _))
348 | fi == txt "FIntT" = mkIntIty (str intTy)
349mkIty' (App (App (P _ (UN ff) _) _) (App (P _ (UN fa) _) (App (P _ (UN io) _) _)))
350 | ff == txt "FFunction" && fa == txt "FAny" &&
353mkIty' (App (App (P _ (UN ff) _) _) _)
354 | ff == txt "FFunction" = FFunction
357-- would be better if these FInt types were evaluated at compile time
358-- TODO: add %eval directive for such things
360mkIty "FFloat" = FArith ATFloat
361mkIty "FInt" = mkIntIty "ITNative"
362mkIty "FChar" = mkIntIty "ITChar"
363mkIty "FByte" = mkIntIty "IT8"
364mkIty "FShort" = mkIntIty "IT16"
365mkIty "FLong" = mkIntIty "IT64"
366mkIty "FBits8" = mkIntIty "IT8"
367mkIty "FBits16" = mkIntIty "IT16"
368mkIty "FBits32" = mkIntIty "IT32"
369mkIty "FBits64" = mkIntIty "IT64"
370mkIty "FString" = FString
372mkIty "FManagedPtr" = FManagedPtr
374mkIty "FFunction" = FFunction
375mkIty "FFunctionIO" = FFunctionIO
376mkIty "FBits8x16" = FArith (ATInt (ITVec IT8 16))
377mkIty "FBits16x8" = FArith (ATInt (ITVec IT16 8))
378mkIty "FBits32x4" = FArith (ATInt (ITVec IT32 4))
379mkIty "FBits64x2" = FArith (ATInt (ITVec IT64 2))
380mkIty x = error $ "Unknown type " ++ x
382mkIntIty "ITNative" = FArith (ATInt ITNative)
383mkIntIty "ITChar" = FArith (ATInt ITChar)
384mkIntIty "IT8" = FArith (ATInt (ITFixed IT8))
385mkIntIty "IT16" = FArith (ATInt (ITFixed IT16))
386mkIntIty "IT32" = FArith (ATInt (ITFixed IT32))
387mkIntIty "IT64" = FArith (ATInt (ITFixed IT64))
389zname = sNS (sUN "Z") ["Nat","Prelude"]
390sname = sNS (sUN "S") ["Nat","Prelude"]
392instance ToIR ([Name], SC) where
393 ir (args, tree) = do logLvl 3 $ "Compiling " ++ show args ++ "\n" ++ show tree
395 return $ LLam args tree'
397instance ToIR SC where
401 ir' (UnmatchedCase str) = return $ LError str
402 ir' (ProjCase tm alt) = do tm' <- ir tm
403 alt' <- mkIRAlt tm' alt
404 return $ LCase tm' [alt']
405 ir' (Case n alts) = do alts' <- mapM (mkIRAlt (LV (Glob n))) alts
406 return $ LCase (LV (Glob n)) alts'
407 ir' ImpossibleCase = return LNothing
409 -- special cases for Z and S
410 -- Needs rethink: projections make this fail
411-- mkIRAlt n (ConCase z _ [] rhs) | z == zname
412-- = mkIRAlt n (ConstCase (BI 0) rhs)
413-- mkIRAlt n (ConCase s _ [arg] rhs) | s == sname
416-- return $ LDefaultCase
417-- (LLet arg (LOp LBMinus [n', LConst (BI 1)])
419 mkIRAlt _ (ConCase n t args rhs)
421 return $ LConCase (-1) n args rhs'
422 mkIRAlt _ (ConstCase x rhs)
425 return $ LConstCase x rhs'
428 return $ LDefaultCase rhs'
429 mkIRAlt tm (SucCase n rhs)
431 return $ LDefaultCase (LLet n (LOp (LMinus (ATInt ITBig))
433 LConst (BI 1)]) rhs')
434-- return $ LSucCase n rhs'
435 mkIRAlt _ (ConstCase c rhs)
436 = ifail $ "Can't match on (" ++ show c ++ ")"
437 mkIRAlt _ (DefaultCase rhs)
439 return $ LDefaultCase rhs'
441 matchable (I _) = True
442 matchable (BI _) = True
443 matchable (Ch _) = True
444 matchable (Str _) = True
447 matchableTy (AType (ATInt ITNative)) = True
448 matchableTy (AType (ATInt ITBig)) = True
449 matchableTy (AType (ATInt ITChar)) = True
450 matchableTy StrType = True
452 matchableTy (AType (ATInt (ITFixed IT8))) = True
453 matchableTy (AType (ATInt (ITFixed IT16))) = True
454 matchableTy (AType (ATInt (ITFixed IT32))) = True
455 matchableTy (AType (ATInt (ITFixed IT64))) = True
457 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