on master help sign in
Much improved inlining

The inliner in LangOpts on the top level type erased IR now works more like an evaluator, rather than just rewriting, which means it can do much more sophisticated inlinings of simple functions, and this will open up lots more possibilities. As a data point, Blodwen both compiles and runs in half the time with this new inliner!

commitde1d8651e574a21baae9d92d04b6d6913276764b authorEdwin Brady <ecb10@st-andrews.ac.uk> dateTue 12 Dec 2017, 14:49 (9 years ago) parentae6ab2d
5 files changed+146−82
libs/prelude/IO.idr+3−2 src/IRTS/CodegenC.hs+19−22 src/IRTS/Compiler.hs+14−7 src/IRTS/Lang.hs+2−2 src/IRTS/LangOpts.hs+108−49
libs/prelude/IO.idr+3−2
@@ -101,9 +101,10 @@ unsafePerformPrimIO : PrimIO a -> a
101101prim_io_pure : a -> PrimIO a
102102prim_io_pure x = Prim__IO x
103103
104+-- %inline
104105io_bind : IO' l a -> (a -> IO' l b) -> IO' l b
105−io_bind (MkIO fn) k
106− = MkIO (\w => prim_io_bind (fn w)
106+io_bind (MkIO fn)
107+ = \k => MkIO (\w => prim_io_bind (fn w)
107108 (\ b => case k b of
108109 MkIO fkb => fkb w))
109110
src/IRTS/CodegenC.hs+19−22
@@ -697,29 +697,26 @@ ifaceC (ExportFun n cn ret args)
697697
698698 argNames = zipWith (++) (repeat "arg") (map show [0..])
699699
700−mkBody n as t = indent 1 ++ "INITFRAME;\n" ++
701− indent 1 ++ "RESERVE(" ++ show (max (length as) 3) ++ ");\n" ++
702− push 0 as ++ call n ++ retval t
703− where push i [] = ""
704− push i ((n, t) : ts) = indent 1 ++ c_irts (toFType t)
705− ("TOP(" ++ show i ++ ") = ") n
706− ++ ";\n" ++ push (i + 1) ts
707−
708− call _ = indent 1 ++ "STOREOLD;\n" ++
709− indent 1 ++ "BASETOP(0);\n" ++
710− indent 1 ++ "ADDTOP(" ++ show (length as) ++ ");\n" ++
711− indent 1 ++ "CALL(" ++ cname n ++ ");\n"
712−
713− retval (FIO t)
714− = indent 1 ++ "TOP(0) = NULL;\n" ++
715− indent 1 ++ "TOP(1) = NULL;\n" ++
716− indent 1 ++ "TOP(2) = RVAL;\n" ++
717− indent 1 ++ "STOREOLD;\n" ++
700+mkBody n as_in t
701+ = indent 1 ++ "INITFRAME;\n" ++
702+ indent 1 ++ "RESERVE(" ++ show (max (length as) 3) ++ ");\n" ++
703+ push 0 as ++ call n ++ retval t
704+ where
705+ as = case t of
706+ FIO t -> as_in ++ [("NULL", FUnknown)] -- add world token
707+ _ -> as_in
708+ push i [] = ""
709+ push i ((n, t) : ts) = indent 1 ++ c_irts (toFType t)
710+ ("TOP(" ++ show i ++ ") = ") n
711+ ++ ";\n" ++ push (i + 1) ts
712+
713+ call _ = indent 1 ++ "STOREOLD;\n" ++
718714 indent 1 ++ "BASETOP(0);\n" ++
719− indent 1 ++ "ADDTOP(3);\n" ++
720− indent 1 ++ "CALL(" ++ cname (sUN "call__IO") ++ ");\n" ++
721− retval t
722− retval t = indent 1 ++ "return " ++ irts_c (toFType t) "RVAL" ++ ";\n"
715+ indent 1 ++ "ADDTOP(" ++ show (length as) ++ ");\n" ++
716+ indent 1 ++ "CALL(" ++ cname n ++ ");\n"
717+
718+ retval (FIO t) = retval t
719+ retval t = indent 1 ++ "return " ++ irts_c (toFType t) "RVAL" ++ ";\n"
723720
724721ctype (FCon c)
725722 | c == sUN "C_Str" = "char*"
src/IRTS/Compiler.hs+14−7
@@ -81,7 +81,14 @@ compile codegen f mtm = do
8181 let defsUniq = map (allocUnique (addAlist defsInlined emptyContext))
8282 defsInlined
8383
84− let (nexttag, tagged) = addTags 65536 (liftAll defsUniq)
84+ logCodeGen 1 "Inlining"
85+
86+ dumpCases <- getDumpCases
87+ case dumpCases of
88+ Nothing -> return ()
89+ Just f -> runIO $ writeFile f (showCaseTrees defsUniq)
90+
91+ let (nexttag, tagged) = addTags 65536 (liftAll defsInlined)
8592 let ctxtIn = addAlist tagged emptyContext
8693
8794 logCodeGen 1 "Defunctionalising"
@@ -96,11 +103,7 @@ compile codegen f mtm = do
96103
97104 let checked = simplifyDefs defuns (toAlist defuns)
98105 outty <- outputTy
99− dumpCases <- getDumpCases
100106 dumpDefun <- getDumpDefun
101− case dumpCases of
102− Nothing -> return ()
103− Just f -> runIO $ writeFile f (showCaseTrees defs)
104107 case dumpDefun of
105108 Nothing -> return ()
106109 Just f -> runIO $ writeFile f (dumpDefuns defuns)
@@ -168,9 +171,13 @@ getDeclarations used
168171showCaseTrees :: [(Name, LDecl)] -> String
169172showCaseTrees = showSep "\n\n" . map showCT . sortBy (comparing defnRank)
170173 where
171− showCT (n, LFun _ f args lexp)
172− = show n ++ " " ++ showSep " " (map show args) ++ " =\n\t"
174+ showCT (n, LFun opts f args lexp)
175+ = showOpts ++
176+ show n ++ " " ++ showSep " " (map show args) ++ " =\n\t"
173177 ++ show lexp
178+ where
179+ showOpts | Inline `elem` opts = "%inline "
180+ | otherwise = ""
174181 showCT (n, LConstructor c t a) = "data " ++ show n ++ " " ++ show a
175182
176183 defnRank :: (Name, LDecl) -> String
src/IRTS/Lang.hs+2−2
@@ -346,8 +346,8 @@ instance Show LExp where
346346 ++ " in " ++ show' (env ++ [show n]) ind e
347347
348348 show' env ind (LLam args e)
349− = "\\ " ++ showSep "," (map show args)
350− ++ " => " ++ show' (env ++ (map show args)) ind e
349+ = "(\\ " ++ showSep "," (map show args)
350+ ++ " => " ++ show' (env ++ (map show args)) ind e ++ ") "
351351
352352 show' env ind (LProj t i) = show t ++ "!" ++ show i
353353
src/IRTS/LangOpts.hs+108−49
@@ -7,12 +7,16 @@ Maintainer : The Idris Community.
77-}
88{-# LANGUAGE DeriveFunctor, PatternGuards #-}
99
10−module IRTS.LangOpts where
10+module IRTS.LangOpts(inlineAll) where
1111
12+import Idris.Core.CaseTree
1213import Idris.Core.TT
1314import IRTS.Lang
1415
1516import Control.Monad.State hiding (lift)
17+import Data.List
18+
19+import Debug.Trace
1620
1721inlineAll :: [(Name, LDecl)] -> [(Name, LDecl)]
1822inlineAll lds = let defs = addAlist lds emptyContext in
@@ -31,53 +35,108 @@ nextN = do i <- get
3135doInline :: LDefs -> LDecl -> LDecl
3236doInline defs d@(LConstructor _ _ _) = d
3337doInline defs (LFun opts topn args exp)
34− = let res = evalState (inlineWith [topn] (map (\n -> (n, LV n)) args) exp) 0 in
35− LFun opts topn args res
38+ = let res = evalState (eval [] initEnv [topn] defs exp)
39+ (length args) in
40+-- = let res = evalState (inlineWith [topn] (map (\n -> (n, LV n)) args) exp) 0 in
41+ case res of
42+ LLam args' body -> LFun opts topn (map snd initNames ++ args') body
43+ _ -> LFun opts topn (map snd initNames) res
3644 where
37− inlineWith :: [Name] -> [(Name, LExp)] -> LExp -> State Int LExp
38− inlineWith done env var@(LV n)
39− = case lookup n env of
40− Just t -> return t
41− Nothing -> return var
42− inlineWith done env (LLazyApp n es) = LLazyApp n <$> (mapM (inlineWith done env) es)
43− inlineWith done env (LForce e) = LForce <$> inlineWith done env e
44− inlineWith done env (LLazyExp e) = LLazyExp <$> inlineWith done env e
45− -- Extend the environment for Let and Lam so that bound names aren't
46− -- expanded with any top level argument definitions they shadow
47− inlineWith done env (LLet n val sc)
48− = do n' <- nextN
49− LLet n' <$> inlineWith done env val <*>
50− inlineWith done ((n, LV n') : env) sc
51− inlineWith done env (LLam args sc)
52− = do ns' <- mapM (\n -> do n' <- nextN
53− return (n, n')) args
54− LLam (map snd ns') <$>
55− inlineWith done (map (\ (n,n') -> (n, LV n')) ns' ++ env) sc
56− inlineWith done env (LProj exp i) = LProj <$> inlineWith done env exp <*> return i
57− inlineWith done env (LCon loc i n es)
58− = LCon loc i n <$> mapM (inlineWith done env) es
59− inlineWith done env (LCase ty e alts)
60− = LCase ty <$> inlineWith done env e <*> mapM (inlineWithAlt done env) alts
61− inlineWith done env (LOp f es) = LOp f <$> mapM (inlineWith done env) es
62− -- the interesting case!
63− inlineWith done env (LApp t (LV n) es)
64− | n `notElem` done,
65− [LFun opts _ args body] <- lookupCtxt n defs,
66− Inline `elem` opts,
67− length es == length args
68− = do es' <- mapM (inlineWith done env) es
69− inlineWith (n : done) (zip args es' ++ env) body
70− inlineWith done env (LApp t f es)
71− = LApp t <$> inlineWith done env f <*> mapM (inlineWith done env) es
72− inlineWith done env (LForeign t s args)
73− = LForeign t s <$> mapM (\(t, e) -> do e' <- inlineWith done env e
74− return (t, e')) args
75− inlineWith done env t = return t
45+ initNames = zipWith (\n i -> (n, newn n i)) args [0..]
46+ initEnv = map (\(n, n') -> (n, LV n')) initNames
47+ newn (UN n) i = MN i n
48+ newn _ i = sMN i "arg"
49+
50+unload :: [LExp] -> LExp -> LExp
51+unload [] e = e
52+unload stk (LApp tc e args) = LApp tc e (args ++ stk)
53+unload stk e = LApp False e stk
54+
55+takeStk :: [(Name, LExp)] -> [Name] -> [LExp] ->
56+ ([(Name, LExp)], [Name], [LExp])
57+takeStk env (a : args) (v : stk) = takeStk ((a, v) : env) args stk
58+takeStk env args stk = (env, args, stk)
59+
60+eval :: [LExp] -> [(Name, LExp)] -> [Name] -> LDefs -> LExp -> State Int LExp
61+eval stk env rec defs (LLazyApp n es)
62+ = unload stk <$> LLazyApp n <$> (mapM (eval [] env rec defs) es)
63+eval stk env rec defs (LForce e)
64+ = unload stk <$> LForce <$> eval [] env rec defs e
65+eval stk env rec defs (LLazyExp e)
66+ = unload stk <$> LLazyExp <$> eval [] env rec defs e
67+-- Special case for io_bind, because it needs to keep executing the first
68+-- action, and is worth inlining to avoid the thunk
69+eval [] env rec defs (LApp t (LV n) [_, _, _, act, (LLam [arg] k)])
70+ | n == sUN "io_bind"
71+ = do w <- nextN
72+ let env' = (w, LV w) : env
73+ act' <- eval [] env' rec defs (LApp False act [LV w])
74+ argn <- nextN
75+ k' <- eval [] ((arg, LV argn) : env') rec defs (LApp False k [LV w])
76+ return $ LLam [w] (LLet argn act' k')
77+eval (world : stk) env rec defs (LApp t (LV n) [_, _, _, act, (LLam [arg] k)])
78+ | n == sUN "io_bind"
79+ = do act' <- eval [] env rec defs (LApp False act [world])
80+ argn <- nextN
81+ k' <- eval stk ((arg, LV argn) : env) rec defs (LApp False k [world])
82+ return $ LLet argn act' k'
83+
84+eval stk env rec defs (LApp t f es)
85+ = do es' <- mapM (eval [] env rec defs) es
86+ eval (es' ++ stk) env rec defs f
87+eval stk env rec defs (LLet n val sc)
88+ = do n' <- nextN
89+ LLet n' <$> eval [] env rec defs val
90+ <*> eval stk ((n, LV n') : env) rec defs sc
91+eval stk env rec defs (LProj exp i)
92+ = unload stk <$> (LProj <$> eval [] env rec defs exp <*> return i)
93+eval stk env rec defs (LCon loc i n es)
94+ = unload stk <$> (LCon loc i n <$> mapM (eval [] env rec defs) es)
95+eval stk env rec defs (LCase ty e alts)
96+ = LCase ty <$> eval [] env rec defs e
97+ <*> mapM (evalAlt stk env rec defs) alts
98+eval stk env rec defs (LOp f es)
99+ = unload stk <$> LOp f <$> mapM (eval [] env rec defs) es
100+eval stk env rec defs (LForeign t s args)
101+ = unload stk <$>
102+ LForeign t s <$> mapM (\(t, e) -> do e' <- eval [] env rec defs e
103+ return (t, e')) args
104+-- save the interesting cases for the end:
105+-- lambdas, and names to reduce
106+eval stk env rec defs (LLam args sc)
107+ | (env', args', stk') <- takeStk env args stk
108+ = case args' of
109+ [] -> eval stk' env' rec defs sc
110+ as -> do ns' <- mapM (\n -> do n' <- nextN
111+ return (n, n')) args'
112+ LLam (map snd ns') <$>
113+ eval [] (map (\ (n, n') -> (n, LV n')) ns' ++ env')
114+ rec defs sc
115+eval stk env rec defs var@(LV n)
116+ = case lookup n env of
117+ Just t
118+ | t /= LV n && n `notElem` rec ->
119+ eval stk env (n : rec) defs t
120+ | otherwise -> return (unload stk t)
121+ Nothing
122+ | n `notElem` rec,
123+ Just (LFun opts _ args body) <- lookupCtxtExact n defs,
124+ Inline `elem` opts ->
125+ apply stk env (n : rec) defs var args body
126+ | otherwise -> return (unload stk var)
127+eval stk env rec defs t = return (unload stk t)
128+
129+evalAlt stk env rec defs (LConCase i n es rhs)
130+ = do ns' <- mapM (\n -> do n' <- nextN
131+ return (n, n')) es
132+ LConCase i n (map snd ns') <$>
133+ eval stk (map (\ (n, n') -> (n, LV n')) ns' ++ env) rec defs rhs
134+evalAlt stk env rec defs (LConstCase c e)
135+ = LConstCase c <$> eval stk env rec defs e
136+evalAlt stk env rec defs (LDefaultCase e)
137+ = LDefaultCase <$> eval stk env rec defs e
76138
77− inlineWithAlt done env (LConCase i n es rhs)
78− = do ns' <- mapM (\n -> do n' <- nextN
79− return (n, n')) es
80− LConCase i n (map snd ns') <$>
81− inlineWith done (map (\ (n,n') -> (n, LV n')) ns' ++ env) rhs
82− inlineWithAlt done env (LConstCase c e) = LConstCase c <$> inlineWith done env e
83− inlineWithAlt done env (LDefaultCase e) = LDefaultCase <$> inlineWith done env e
139+apply :: [LExp] -> [(Name, LExp)] -> [Name] -> LDefs -> LExp ->
140+ [Name] -> LExp -> State Int LExp
141+apply stk env rec defs var args body
142+ = eval stk env rec defs (LLam args body)