@@ -5,78 +5,118 @@ module Idris.DataOpts where
5-- Forcing, detagging and collapsing
5-- Forcing, detagging and collapsing
7import Idris.AbsSyntax
7import Idris.AbsSyntax
8+import Idris.AbsSyntaxTree
8import Core.TT
9import Core.TT
11+import Control.Applicative
12+import qualified Data.IntMap.Strict as M
10import Data.List
13import Data.List
11import Data.Maybe
14import Data.Maybe
12import Debug.Trace
15import Debug.Trace
17+type ForceMap = M.IntMap Forceability
14−-- Calculate the forceable arguments to a constructor and update the set of
19+-- Calculate the forceable arguments to a constructor
15−-- optimisations
20+-- and update the set of optimisations.
17−forceArgs :: Name -> Type -> Idris ()
21+forceArgs :: Name -> Name -> Type -> Idris ()
18−forceArgs n t = do i <- getIState
22+forceArgs typeName n t = do
19− let fargs = force i 0 t
23+ ist <- getIState
24+ let fargs = getForcedArgs ist typeName t
20− copt <- case lookupCtxt n (idris_optimisation i) of
25+ copt = case lookupCtxt n (idris_optimisation ist) of
21− [] -> return $ Optimise False False [] []
26+ [] -> Optimise False False [] []
22− (op:_) -> return op
27+ op:_ -> op
23− let opts = addDef n (copt { forceable = fargs }) (idris_optimisation i)
28+ opts = addDef n (copt { forceable = M.toList fargs }) (idris_optimisation ist)
24− putIState (i { idris_optimisation = opts })
29+ putIState (ist { idris_optimisation = opts })
25− addIBC (IBCOpt n)
30+ addIBC (IBCOpt n)
26− iLOG $ "Forced: " ++ show n ++ " " ++ show fargs ++ "\n from " ++
31+ iLOG $ "Forced: " ++ show n ++ " " ++ show fargs ++ "\n from " ++ show t
33+getForcedArgs :: IState -> Name -> Type -> ForceMap
34+getForcedArgs ist typeName t = addCollapsibleArgs 0 t $ forcedInTarget 0 t
29− force :: IState -> Int -> Term -> [Int]
36+ maxUnion = M.unionWith max
30− force ist i (Bind _ (Pi ty) sc)
31− | collapsibleIn ist ty
32− = nub $ i : (force ist (i + 1) $ instantiate (P Bound (MN i "?") Erased) sc)
33− | otherwise = force ist (i + 1) $ instantiate (P Bound (MN i "?") Erased) sc
34− force _ _ sc@(App f a)
35− | (_, args) <- unApply sc
36− = nub $ concatMap guarded args
40− | (P _ tn _, _) <- unApply t
41− = case lookupCtxt tn (idris_optimisation i) of
42− [oi] -> collapsible oi
46− isF (P _ (MN force "?") _) = Just force
49− guarded :: Term -> [Int]
51−-- | (P (TCon _ _) _ _, args) <- unApply t
52−-- = mapMaybe isF args ++ concatMap guarded args
53− | (P (DCon _ _) _ _, args) <- unApply t
54− = mapMaybe isF args ++ concatMap guarded args
55− guarded t = mapMaybe isF [t]
57−-- Calculate whether a collection of constructors is collapsible
38+ -- Label all occurrences of the variable bound in Pi in the rest of
39+ -- the term with the number i so that we can recognize them anytime later.
40+ label i = instantiate $ P Bound (MN i "ctor_arg") Erased
42+ addCollapsibleArgs :: Int -> Type -> ForceMap -> ForceMap
43+ addCollapsibleArgs i (Bind vn (Pi ty) rest) alreadyForceable
44+ = addCollapsibleArgs (i+1) (label i rest) (forceable $ unApply ty)
46+ -- forceable takes an un-applied type of a ctor argument
47+ forceable (P _ tn _, args)
48+ -- if `ty' is collapsible, the argument is unconditionally forceable
50+ = M.insert i Unconditional alreadyForceable
52+ -- a recursive occurrence with known indices is conditionally forceable
54+ = M.insertWith max i Conditional alreadyForceable
56+ forceable _ = alreadyForceable
58+ isCollapsible :: Name -> Bool
59+ isCollapsible n = case lookupCtxt n (idris_optimisation ist) of
60+ [oi] -> collapsible oi
63+ addCollapsibleArgs _ _ fs = fs
65+ forcedInTarget :: Int -> Type -> ForceMap
66+ forcedInTarget i (Bind _ (Pi _) rest) = forcedInTarget (i+1) (label i rest)
67+ forcedInTarget i t@(App f a) | (_, as) <- unApply t = unionMap guardedArgs as
68+ forcedInTarget _ _ = M.empty
70+ guardedArgs :: Term -> ForceMap
71+ guardedArgs t@(App f a) | (P (DCon _ _) _ _, args) <- unApply t
72+ = unionMap bareArg args `maxUnion` unionMap guardedArgs args
73+ guardedArgs t = bareArg t
75+ bareArg :: Term -> ForceMap
76+ bareArg (P _ (MN i "ctor_arg") _) = M.singleton i Unconditional
79+ unionMap :: (a -> ForceMap) -> [a] -> ForceMap
80+ unionMap f = M.unionsWith max . map f
82+-- Calculate whether a collection of constructors is collapsible
83+-- and update the state accordingly.
59collapseCons :: Name -> [(Name, Type)] -> Idris ()
84collapseCons :: Name -> [(Name, Type)] -> Idris ()
60−collapseCons ty cons =
85+collapseCons tn ctors = do
61− do i <- getIState
86+ ist <- getIState
62− let cons' = map (\ (n, t) -> (n, map snd (getArgTys t))) cons
87+ case ctors of
63− allFR <- mapM (forceRec i) cons'
88+ _
64− if and allFR then detaggable (map getRetTy (map snd cons))
89+ | all (ctorCollapsible ist) ctors
90+ , disjointTerms ctorTargetArgs
91+ -> mapM_ setCollapsible (tn : map fst ctors)
65− else checkNewType cons'
94+ -> checkNewType ist cn ct
96+ _ -> return () -- nothing can be done
98+ ctorTargetArgs = map (snd . unApply . getRetTy . snd) ctors
100+ ctorArity :: Type -> Int
101+ ctorArity = length . getArgTys
103+ ctorCollapsible :: IState -> (Name, Type) -> Bool
104+ ctorCollapsible ist (n, t) = all (`M.member` forceMap) [0 .. ctorArity t - 1]
106+ forceMap = case lookupCtxt n (idris_optimisation ist) of
107+ oi:_ -> M.fromList $ forceable oi
67 -- one constructor; if one remaining argument, treat as newtype
110 -- one constructor; if one remaining argument, treat as newtype
68− checkNewType [(n, ts)] = do
111+ checkNewType :: IState -> Name -> Type -> Idris ()
69− i <- getIState
112+ checkNewType ist cn ct
70− case lookupCtxt n (idris_optimisation i) of
113+ | oi:_ <- lookupCtxt cn opt
71− (oi:_) -> do let remaining = length ts - length (forceable oi)
114+ , length (getArgTys ct) == 1 + forcedCnt (M.fromList $ forceable oi)
72− if remaining == 1 then
115+ = putIState ist{ idris_optimisation = opt' oi }
116+ | otherwise = return ()
118+ opt = idris_optimisation ist
73− do let oi' = oi { isnewtype = True }
119+ opt' oi = addDef cn oi{ isnewtype = True } opt
74− let opts = addDef n oi' (idris_optimisation i)
75− putIState (i { idris_optimisation = opts })
79− checkNewType _ = return ()
81 setCollapsible :: Name -> Idris ()
121 setCollapsible :: Name -> Idris ()
82 setCollapsible n
122 setCollapsible n
@@ -91,79 +131,49 @@ collapseCons ty cons =
91 putIState (i { idris_optimisation = opts })
131 putIState (i { idris_optimisation = opts })
92 addIBC (IBCOpt n)
132 addIBC (IBCOpt n)
94− forceRec :: IState -> (Name, [Type]) -> Idris Bool
134+ disjointTerms :: [[Term]] -> Bool
95− forceRec i (n, ts)
135+ disjointTerms [] = True
96− = case lookupCtxt n (idris_optimisation i) of
136+ disjointTerms [xs] = True
97− (oi:_) -> checkFR (forceable oi) 0 ts
137+ disjointTerms (xs : xss) =
99− checkFR fs i [] = return True
100− checkFR fs i (_ : xs) | i `elem` fs = checkFR fs (i + 1) xs
101− checkFR fs i (t : xs)
102− -- must be recursive or type is not collapsible
103− = do let (rtf, rta) = unApply $ getRetTy t
104− if (ty `elem` freeNames rtf)
105− then checkFR fs (i+1) xs
108− detaggable :: [Type] -> Idris ()
110− = do let rtyArgs = map (snd . unApply) rtys
111− -- if every rtyArgs is disjoint with every other, it's detaggable,
138+ -- xs is disjoint with every pattern from xss
112− -- therefore also collapsible given forceable/recursive check
139+ all (or . zipWith disjoint xs) xss
114− then mapM_ setCollapsible (ty : map fst cons)
117− disjoint :: [[Term]] -> Bool
119− disjoint [x] = True
140+ -- and xss is pairwise disjoint, too
120− disjoint (x : xs) = anyDisjoint x xs && disjoint xs
141+ && disjointTerms xss
122− anyDisjoint x [] = True
143+ -- Return True if the two patterns are provably disjoint.
123− anyDisjoint x (y : ys) = disjointCons x y
144+ -- Return False if they're not or if unsure.
124−
145+ disjoint :: Term -> Term -> Bool
125− disjointCons [] [] = False
146+ disjoint x y = case (cx, cy) of
126− disjointCons [] y = False
147+ -- data constructors -> compare their names
127− disjointCons x [] = False
128− disjointCons (x : xs) (y : ys)
129− = disjointCon x y || disjointCons xs ys
131− disjointCon x y = let (cx, _) = unApply x
132− (cy, _) = unApply y in
134− (P (DCon _ _) nx _, P (DCon _ _) ny _) -> nx /= ny
148+ (P (DCon _ _) nx _, P (DCon _ _) ny _)
150+ | otherwise -> or $ zipWith disjoint xargs yargs
135− _ -> False
151+ _ -> False
153+ (cx, xargs) = unApply x
154+ (cy, yargs) = unApply y
137class Optimisable term where
156class Optimisable term where
138 applyOpts :: term -> Idris term
157 applyOpts :: term -> Idris term
139 stripCollapsed :: term -> Idris term
158 stripCollapsed :: term -> Idris term
141instance (Optimisable a, Optimisable b) => Optimisable (a, b) where
160instance (Optimisable a, Optimisable b) => Optimisable (a, b) where
142− applyOpts (x, y) = do x' <- applyOpts x
161+ applyOpts (x, y) = (,) <$> applyOpts x <*> applyOpts y
145− stripCollapsed (x, y) = do x' <- stripCollapsed x
162+ stripCollapsed (x, y) = (,) <$> stripCollapsed x <*> stripCollapsed y
146− y' <- stripCollapsed y
150instance (Optimisable a, Optimisable b) => Optimisable (vs, a, b) where
164instance (Optimisable a, Optimisable b) => Optimisable (vs, a, b) where
151− applyOpts (v, x, y) = do x' <- applyOpts x
165+ applyOpts (v, x, y) = (,,) v <$> applyOpts x <*> applyOpts y
154− stripCollapsed (v, x, y) = do x' <- stripCollapsed x
155− y' <- stripCollapsed y
166+ stripCollapsed (v, x, y) = (,,) v <$> stripCollapsed x <*> stripCollapsed y
158instance Optimisable a => Optimisable [a] where
168instance Optimisable a => Optimisable [a] where
159 applyOpts = mapM applyOpts
169 applyOpts = mapM applyOpts
160 stripCollapsed = mapM stripCollapsed
170 stripCollapsed = mapM stripCollapsed
162instance Optimisable a => Optimisable (Either a (a, a)) where
172instance Optimisable a => Optimisable (Either a (a, a)) where
163− applyOpts (Left t) = do t' <- applyOpts t; return $ Left t'
173+ applyOpts (Left t) = Left <$> applyOpts t
164− applyOpts (Right t) = do t' <- applyOpts t; return $ Right t'
174+ applyOpts (Right t) = Right <$> applyOpts t
165− stripCollapsed (Left t) = do t' <- stripCollapsed t; return $ Left t'
175+ stripCollapsed (Left t) = Left <$> stripCollapsed t
166− stripCollapsed (Right t) = do t' <- stripCollapsed t; return $ Right t'
176+ stripCollapsed (Right t) = Right <$> stripCollapsed t
168-- Raw is for compile time optimisation (before type checking)
178-- Raw is for compile time optimisation (before type checking)
169-- Term is for run time optimisation (after type checking, collapsing allowed)
179-- Term is for run time optimisation (after type checking, collapsing allowed)
@@ -175,41 +185,39 @@ instance Optimisable Raw where
175 | (Var n, args) <- raw_unapply t -- MAGIC HERE
185 | (Var n, args) <- raw_unapply t -- MAGIC HERE
176 = do args' <- mapM applyOpts args
186 = do args' <- mapM applyOpts args
177 i <- getIState
187 i <- getIState
178− case lookupCtxt n (idris_optimisation i) of
188+ return $ case lookupCtxt n (idris_optimisation i) of
179− (oi:_) -> return $ applyDataOpt oi n args'
189+ oi:_ -> applyDataOpt oi n args'
180− _ -> return (raw_apply (Var n) args')
190+ _ -> raw_apply (Var n) args'
181− | otherwise = do f' <- applyOpts f
191+ | otherwise = RApp <$> applyOpts f <*> applyOpts a
182− a' <- applyOpts a
192+
184− applyOpts (RBind n b t) = do b' <- applyOpts b
193+ applyOpts (RBind n b t) = RBind n <$> applyOpts b <*> applyOpts t
186− return (RBind n b' t')
187− applyOpts (RForce t) = applyOpts t
194+ applyOpts (RForce t) = applyOpts t
188 applyOpts t = return t
195 applyOpts t = return t
190 stripCollapsed t = return t
197 stripCollapsed t = return t
192instance Optimisable t => Optimisable (Binder t) where
199instance Optimisable t => Optimisable (Binder t) where
193− applyOpts (Let t v) = do t' <- applyOpts t
200+ applyOpts (Let t v) = Let <$> applyOpts t <*> applyOpts v
196 applyOpts b = do t' <- applyOpts (binderTy b)
201 applyOpts b = do t' <- applyOpts (binderTy b)
197 return (b { binderTy = t' })
202 return (b { binderTy = t' })
198− stripCollapsed (Let t v) = do t' <- stripCollapsed t
203+ stripCollapsed (Let t v) = Let <$> stripCollapsed t <*> stripCollapsed v
199− v' <- stripCollapsed v
201 stripCollapsed b = do t' <- stripCollapsed (binderTy b)
204 stripCollapsed b = do t' <- stripCollapsed (binderTy b)
202 return (b { binderTy = t' })
205 return (b { binderTy = t' })
207+forcedArgSeq :: OptInfo -> [Maybe Forceability]
208+forcedArgSeq oi = map (\i -> M.lookup i forceMap) [0..]
210+ forceMap = M.fromList $ forceable oi
212+forcedCnt :: ForceMap -> Int
213+forcedCnt = length . filter (== Unconditional) . M.elems
205applyDataOpt :: OptInfo -> Name -> [Raw] -> Raw
215applyDataOpt :: OptInfo -> Name -> [Raw] -> Raw
206−applyDataOpt oi n args
216+applyDataOpt oi n args
207− = let args' = zipWith doForce (map (\x -> x `elem` (forceable oi)) [0..])
217+ = raw_apply (Var n) $ zipWith doForce (forcedArgSeq oi) args
209− raw_apply (Var n) args'
211− doForce True a = RForce a
219+ doForce (Just Unconditional) a = RForce a
212− doForce False a = a
220+ doForce _ a = a
214-- Run-time: do everything
222-- Run-time: do everything
@@ -268,10 +276,10 @@ instance Optimisable (TT Name) where
269applyDataOptRT :: OptInfo -> Name -> Int -> Int -> [Term] -> Term
277applyDataOptRT :: OptInfo -> Name -> Int -> Int -> [Term] -> Term
270applyDataOptRT oi n tag arity args
278applyDataOptRT oi n tag arity args
271− | length args == arity = doOpts n args (collapsible oi) (forceable oi)
279+ | length args == arity = doOpts n args (collapsible oi) (M.fromList $ forceable oi)
272 | otherwise = let extra = satArgs (arity - length args)
280 | otherwise = let extra = satArgs (arity - length args)
273 tm = doOpts n (args ++ map (\n -> P Bound n Erased) extra)
281 tm = doOpts n (args ++ map (\n -> P Bound n Erased) extra)
274− (collapsible oi) (forceable oi) in
282+ (collapsible oi) (M.fromList $ forceable oi) in
275 bind extra tm
283 bind extra tm
277 satArgs n = map (\i -> MN i "sat") [1..n]
285 satArgs n = map (\i -> MN i "sat") [1..n]
@@ -285,18 +293,12 @@ applyDataOptRT oi n tag arity args
285 doOpts (NS (UN "S") ["Nat", "Prelude"]) [k] _ _
293 doOpts (NS (UN "S") ["Nat", "Prelude"]) [k] _ _
286 = App (App (P Ref (UN "prim__addBigInt") Erased) k) (Constant (BI 1))
294 = App (App (P Ref (UN "prim__addBigInt") Erased) k) (Constant (BI 1))
288− doOpts n args True f = Erased
296+ doOpts n args True _ = Erased
289− doOpts n args _ forced
297+ doOpts n args _ forceMap
290− = let args' = filter keep (zip (map (\x -> x `elem` forced) [0..])
292− if isnewtype oi
298+ | isnewtype oi = case args' of
294− [(_, val)] -> val
299+ [val] -> val
295− _ -> error "Can't happen (not isnewtype)"
300+ _ -> error $ "Can't happen (newtype not a singleton): " ++ show args'
296− else
301+ | otherwise = mkApp ctor' args'
297− mkApp (P (DCon tag (arity - length forced)) n Erased)
303+ ctor' = (P (DCon tag (arity - forcedCnt forceMap)) n Erased)
298− (map snd args')
304+ args' = [t | (f, t) <- zip (forcedArgSeq oi) args, f /= Just Unconditional]
300− keep (forced, _) = not forced